From e9bc779da612c5404af507005e9c48516fbc696d Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Wed, 31 Aug 2022 16:42:22 +0200 Subject: [PATCH] Add --project-directory option to generate command to easily use relative path --- README.md | 14 +++++ Sources/ResgenSwift/Colors/Colors.swift | 2 +- .../Generator/ColorExtensionGenerator.swift | 2 +- .../Colors/Generator/ColorXcassetHelper.swift | 5 +- .../ResgenSwift/Fonts/FontsToolHelper.swift | 2 +- .../Generator/FontToolContentGenerator.swift | 2 +- .../Extensions/StringExtensions.swift | 18 +++++++ Sources/ResgenSwift/Generate/Generate.swift | 8 ++- .../Generate/GenerateOptions.swift | 9 ++++ ...ColorsConfiguration+ShellCommandable.swift | 15 +++--- .../FontsConfiguration+ShellCommandable.swift | 6 +-- ...ImagesConfiguration+ShellCommandable.swift | 10 ++-- .../Generate/Runnable/Runnable.swift | 2 +- ...tringsConfiguration+ShellCommandable.swift | 10 ++-- .../TagsConfiguration+ShellCommandable.swift | 8 +-- .../Generator/ImageExtensionGenerator.swift | 4 +- .../Images/Generator/XcassetsGenerator.swift | 20 ++++--- Sources/ResgenSwift/Images/Images.swift | 2 +- .../Generator/StringsFileGenerator.swift | 2 +- .../Strings/Generator/TagsGenerator.swift | 2 +- Sources/ResgenSwift/Strings/Twine/Twine.swift | 8 +-- Sources/ToolCore/Shell.swift | 52 ++++++++++++------- 22 files changed, 136 insertions(+), 67 deletions(-) create mode 100644 Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift diff --git a/README.md b/README.md index 81fb67f..6808906 100644 --- a/README.md +++ b/README.md @@ -246,4 +246,18 @@ tags: [] ... ``` +### Usage +If you **don't** install ResgenSwift: + +```sh +swift run -c release ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} +``` + +If you install ResgenSwift: + +```sh +resgen-swift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} +``` + +> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`) \ No newline at end of file diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 9d0da92..8c19054 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -92,7 +92,7 @@ struct Colors: ParsableCommand { // MARK: - Helpers private func deleteCurrentColors() { - Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*") + Shell.shell(["rm", "-rf", "\(options.xcassetsPath)/Colors/*"]) } } diff --git a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift index 9e0d17b..28a0426 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift @@ -17,7 +17,7 @@ struct ColorExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift index 1ec0dad..34cd3b1 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift @@ -20,11 +20,12 @@ struct ColorXcassetHelper { private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) { // Create ColorSet let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset" - Shell.shell("mkdir", "-p", "\(colorSetPath)") + Shell.shell(["mkdir", + "-p", "\(colorSetPath)"]) // Create Contents.json in ColorSet let contentsJsonPath = "\(colorSetPath)/Contents.json" - Shell.shell("touch", "\(contentsJsonPath)") + Shell.shell(["touch", "\(contentsJsonPath)"]) // Write content in Contents.json let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath) diff --git a/Sources/ResgenSwift/Fonts/FontsToolHelper.swift b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift index 3cfc99b..686db9f 100644 --- a/Sources/ResgenSwift/Fonts/FontsToolHelper.swift +++ b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift @@ -60,7 +60,7 @@ class FontsToolHelper { private static func getFontName(atPath path: String) -> String { //print("fc-scan --format %{postscriptname} \(path)") // Get real font name - let task = Shell.shell("fc-scan", "--format", "%{postscriptname}", path) + let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path]) guard let fontName = task.output, task.terminationStatus == 0 else { let error = FontsToolError.fcScan(path, task.terminationStatus, task.output) diff --git a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift index 6089cf0..490408e 100644 --- a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift @@ -14,7 +14,7 @@ class FontExtensionGenerator { // Check file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift b/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift new file mode 100644 index 0000000..7d0468f --- /dev/null +++ b/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift @@ -0,0 +1,18 @@ +// +// StringExtensions.swift +// +// +// Created by Thibaut Schmitt on 31/08/2022. +// + +import Foundation + +extension String { + + func prependIfRelativePath(_ prependPath: String) -> String { + if self.hasPrefix("/") { + return self + } + return prependPath + self + } +} diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift index 1d91f88..fd9c1b5 100644 --- a/Sources/ResgenSwift/Generate/Generate.swift +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -40,13 +40,17 @@ struct Generate: ParsableCommand { print(" - \(configuration.strings.count) strings configuration") print(" - \(configuration.tags.count) tags configuration") print() + + print("Input file: \(configuration.colors.first?.inputFile ?? "no input file")") + // Execute commands configuration.runnableConfigurations .forEach { - $0.run(force: options.forceGeneration) + $0.run(projectDirectory: options.projectDirectory, + force: options.forceGeneration) print("\n") } - + print("[\(Self.toolName)] Resgen ended") } } diff --git a/Sources/ResgenSwift/Generate/GenerateOptions.swift b/Sources/ResgenSwift/Generate/GenerateOptions.swift index a457828..bedf445 100644 --- a/Sources/ResgenSwift/Generate/GenerateOptions.swift +++ b/Sources/ResgenSwift/Generate/GenerateOptions.swift @@ -16,4 +16,13 @@ struct GenerateOptions: ParsableArguments { @Argument(help: "Configuration file.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var configurationFile: String + + @Option(help: "Project directory. It will be added to every relative path (path that does not start with `/`", + transform: { + if $0.last == "/" { + return $0 + } + return $0 + "/" + }) + var projectDirectory: String } diff --git a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift index 390e177..74850c3 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// ColorsConfiguration+ShellCommandable.swift +// ColorsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension ColorsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,13 +16,13 @@ extension ColorsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--style", style, "--xcassets-path", - xcassetsPath, + xcassetsPath.prependIfRelativePath(projectDirectory), "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] @@ -39,7 +39,8 @@ extension ColorsConfiguration: Runnable { extensionSuffix ] } - - Colors.main(args) + print("Colors args:") + dump(args) + //Colors.main(args) } } diff --git a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift index ece931a..6b62239 100644 --- a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// FontsConfiguration+ShellCommandable.swift +// FontsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension FontsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -18,7 +18,7 @@ extension FontsConfiguration: Runnable { args += [ inputFile, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift index 837bbf7..a99fd16 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// ImagesConfiguration+ShellCommandable.swift +// ImagesConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension ImagesConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,11 +16,11 @@ extension ImagesConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--xcassets-path", - xcassetsPath, + xcassetsPath.prependIfRelativePath(projectDirectory), "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/Runnable.swift index 5b8c46f..8e2d937 100644 --- a/Sources/ResgenSwift/Generate/Runnable/Runnable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/Runnable.swift @@ -8,6 +8,6 @@ import Foundation protocol Runnable { - func run(force: Bool) + func run(projectDirectory: String, force: Bool) } diff --git a/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift index ae60f95..d573569 100644 --- a/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// StringsConfiguration+ShellCommandable.swift +// StringsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension StringsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,15 +16,15 @@ extension StringsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--output-path", - outputPath, + outputPath.prependIfRelativePath(projectDirectory), "--langs", langs, "--default-lang", defaultLang, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift index efab0d2..04c6648 100644 --- a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// TagsConfiguration+ShellCommandable.swift +// TagsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension TagsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,11 +16,11 @@ extension TagsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--lang", lang, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift index 1d34c96..7778d32 100644 --- a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift @@ -34,7 +34,7 @@ class ImageExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Generate extension @@ -47,7 +47,7 @@ class ImageExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift index 8c5fe8f..d456bef 100644 --- a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift @@ -55,7 +55,7 @@ class XcassetsGenerator { // Create imageset folder let imagesetName = "\(parsedImage.name).imageset" let imagesetPath = "\(xcassetsPath)/\(imagesetName)" - Shell.shell("mkdir", "-p", imagesetPath) + Shell.shell(["mkdir", "-p", imagesetPath]) // Store managed images path generatedAssetsPaths.append(imagesetName) @@ -96,16 +96,22 @@ class XcassetsGenerator { // convert path/to/image.png -resize 200x300 path/to/output.png // convert path/to/image.png -resize 200x path/to/output.png // convert path/to/image.png -resize x300 path/to/output.png - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x) - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x) - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", + output1x]) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", + output2x]) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", + output3x]) } // Write Content.json let imagesetContentJson = parsedImage.contentJson let contentJsonFilePath = "\(imagesetPath)/Contents.json" if fileManager.fileExists(atPath: contentJsonFilePath) == false { - Shell.shell("touch", "\(contentJsonFilePath)") + Shell.shell(["touch", "\(contentJsonFilePath)"]) } let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath) @@ -160,8 +166,8 @@ class XcassetsGenerator { } // Info unavailable -> do not bypass - let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath) - let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath) + let taskWidth = Shell.shell(["identify", "-format", "%w", xcassetImagePath]) + let taskHeight = Shell.shell(["identify", "-format", "%h", xcassetImagePath]) guard taskWidth.terminationStatus == 0, taskHeight.terminationStatus == 0 else { return false diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index d3ff386..9f8d8d3 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -95,7 +95,7 @@ struct Images: ParsableCommand { @discardableResult static func getSvgConverterPath() -> String { - let taskSvgConverter = Shell.shell("which", "rsvg-convert") + let taskSvgConverter = Shell.shell(["which", "rsvg-convert"]) if taskSvgConverter.terminationStatus == 0 { return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines) } diff --git a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift index 0beb811..6e0f697 100644 --- a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift @@ -118,7 +118,7 @@ class StringsFileGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift index 7b3bbd7..38a1a9f 100644 --- a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -38,7 +38,7 @@ class TagsGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Strings/Twine/Twine.swift b/Sources/ResgenSwift/Strings/Twine/Twine.swift index e495a03..2a48bac 100644 --- a/Sources/ResgenSwift/Strings/Twine/Twine.swift +++ b/Sources/ResgenSwift/Strings/Twine/Twine.swift @@ -40,20 +40,20 @@ struct Twine: ParsableCommand { // Generate strings files (lproj files) for lang in options.langs { - Shell.shell(Self.twineExecutable, + Shell.shell([Self.twineExecutable, "generate-localization-file", options.inputFile, "--lang", "\(lang)", "\(options.outputPath)/\(lang).lproj/\(options.inputFilenameWithoutExt).strings", - "--tags=ios,iosonly,iosOnly") + "--tags=ios,iosonly,iosOnly"]) } // Generate extension - Shell.shell(Self.twineExecutable, + Shell.shell([Self.twineExecutable, "generate-localization-file", options.inputFile, "--format", "apple-swift", "--lang", "\(options.defaultLang)", options.extensionFilePath, - "--tags=ios,iosonly,iosOnly") + "--tags=ios,iosonly,iosOnly"]) print("[\(Self.toolName)] Strings generated") } diff --git a/Sources/ToolCore/Shell.swift b/Sources/ToolCore/Shell.swift index aa7c3bc..0bcefdc 100644 --- a/Sources/ToolCore/Shell.swift +++ b/Sources/ToolCore/Shell.swift @@ -9,32 +9,48 @@ import Foundation public class Shell { - @discardableResult - public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) { - let task = Process() - task.launchPath = launchPath - task.arguments = args - - let pipe = Pipe() - task.standardOutput = pipe - try? task.run() - task.waitUntilExit() - - let data = pipe.fileHandleForReading.readDataToEndOfFile() - - guard let output: String = String(data: data, encoding: .utf8) else { - return (terminationStatus: task.terminationStatus, output: nil) - } - - return (terminationStatus: task.terminationStatus, output: output) + public static var environment: [String: String] { + ProcessInfo.processInfo.environment } +// @discardableResult +// public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) { +// let task = Process() +// task.launchPath = launchPath +// task.arguments = args +// +// var currentEnv = ProcessInfo.processInfo.environment +// for (key, value) in environment { +// currentEnv[key] = value +// } +// task.environment = currentEnv +// +// let pipe = Pipe() +// task.standardOutput = pipe +// try? task.run() +// task.waitUntilExit() +// +// let data = pipe.fileHandleForReading.readDataToEndOfFile() +// +// guard let output: String = String(data: data, encoding: .utf8) else { +// return (terminationStatus: task.terminationStatus, output: nil) +// } +// +// return (terminationStatus: task.terminationStatus, output: output) +// } + @discardableResult public static func shell(launchPath: String = "/usr/bin/env", _ args: [String]) -> (terminationStatus: Int32, output: String?) { let task = Process() task.launchPath = launchPath task.arguments = args + var currentEnv = ProcessInfo.processInfo.environment + for (key, value) in environment { + currentEnv[key] = value + } + task.environment = currentEnv + let pipe = Pipe() task.standardOutput = pipe task.launch()