diff --git a/Sources/RswiftCore/CallInformation.swift b/Sources/RswiftCore/CallInformation.swift index a4f909bf..6e88db4b 100644 --- a/Sources/RswiftCore/CallInformation.swift +++ b/Sources/RswiftCore/CallInformation.swift @@ -26,11 +26,11 @@ public struct CallInformation { let scriptOutputFiles: [String] let lastRunURL: URL - private let buildProductsDirURL: URL - private let developerDirURL: URL - private let sourceRootURL: URL - private let sdkRootURL: URL - private let platformURL: URL + let buildProductsDirURL: URL + let developerDirURL: URL + let sourceRootURL: URL + let sdkRootURL: URL + let platformURL: URL public init( outputURL: URL, diff --git a/Sources/RswiftCore/EnvironmentValidation.swift b/Sources/RswiftCore/EnvironmentValidation.swift new file mode 100644 index 00000000..1a68a60b --- /dev/null +++ b/Sources/RswiftCore/EnvironmentValidation.swift @@ -0,0 +1,61 @@ +// +// EnvironmentValidation.swift +// Commander +// +// Created by Tom Lokhorst on 2018-12-12. +// + +import Foundation + +public func validateRswiftEnvironment( + outputURL: URL, + sourceRootPath: String, + scriptInputFiles: [String], + scriptOutputFiles: [String], + lastRunURL: URL, + podsRoot: String?, + commandLineArguments: [String]) -> [String] +{ + var errors: [String] = [] + var outputIsDirectory = false + + if outputURL.pathExtension != "swift" { + outputIsDirectory = true + + var error = "Output path must specify a file, it should not be a directory." + if FileManager.default.directoryExists(atPath: outputURL.path) { + let rswiftGeneratedFile = outputURL.appendingPathComponent("R.generated.swift").path + let commandParts = commandLineArguments + .map { $0.replacingOccurrences(of: podsRoot ?? "", with: "$PODS_ROOT") } + .map { $0.replacingOccurrences(of: outputURL.path, with: rswiftGeneratedFile) } + .map { $0.replacingOccurrences(of: sourceRootPath, with: "$SRCROOT") } + .map { $0.contains(" ") ? "\"\($0)\"" : $0 } + error += "\nExample: " + commandParts.joined(separator: " ") + } + + errors.append(error) + } + + if !scriptInputFiles.contains(lastRunURL.path) { + errors.append("Build phase Intput Files does not contain '$TEMP_DIR/\(lastRunURL.lastPathComponent)'.") + } + + if !scriptOutputFiles.contains(outputURL.path) { + var prettyPath = outputURL.path.replacingOccurrences(of: sourceRootPath, with: "$SRCROOT") + if outputIsDirectory { + prettyPath += "/R.generated.swift" + } + errors.append("Build phase Output Files do not contain '\(prettyPath)'.") + } + + return errors +} + +extension FileManager { + func directoryExists(atPath path: String) -> Bool { + var isDir: ObjCBool = false + let exists = fileExists(atPath: path, isDirectory: &isDir) + + return exists && isDir.boolValue + } +} diff --git a/Sources/RswiftCore/RswiftCore.swift b/Sources/RswiftCore/RswiftCore.swift index 619b4c3c..14a2d438 100644 --- a/Sources/RswiftCore/RswiftCore.swift +++ b/Sources/RswiftCore/RswiftCore.swift @@ -13,21 +13,6 @@ import XcodeEdit public struct RswiftCore { static public func run(_ callInformation: CallInformation) throws { - guard callInformation.scriptInputFiles.contains(callInformation.lastRunURL.path) else { - fail("Missing '$(TEMP_DIR)/\(callInformation.lastRunURL.lastPathComponent)' from Input Files. See upgrading to R.swift 5.0: http://bit.ly/2PsO9IF") - exit(EXIT_FAILURE) - } - - guard callInformation.outputURL.pathExtension == "swift" else { - fail("Output path must be a specific Swift file, folders are not allowed anymore. See upgrading to R.swift 5.0: http://bit.ly/2PsO9IF") - exit(EXIT_FAILURE) - } - - guard callInformation.scriptOutputFiles.contains(callInformation.outputURL.path) else { - fail("Build phase output files do not contain '\(callInformation.outputURL.path)'. See upgrading to R.swift 5.0: http://bit.ly/2PsO9IF") - exit(EXIT_FAILURE) - } - do { let xcodeproj = try Xcodeproj(url: callInformation.xcodeprojURL) let ignoreFile = (try? IgnoreFile(ignoreFileURL: callInformation.rswiftIgnoreURL)) ?? IgnoreFile() diff --git a/Sources/RswiftCore/Util/ErrorOutput.swift b/Sources/RswiftCore/Util/ErrorOutput.swift index 77368175..95061bb7 100644 --- a/Sources/RswiftCore/Util/ErrorOutput.swift +++ b/Sources/RswiftCore/Util/ErrorOutput.swift @@ -13,6 +13,6 @@ public func warn(_ warning: String) { print("warning: [R.swift] \(warning)") } -func fail(_ error: String) { +public func fail(_ error: String) { print("error: [R.swift] \(error)") } diff --git a/Sources/rswift/main.swift b/Sources/rswift/main.swift index 6ea8e8b2..b59aeef8 100644 --- a/Sources/rswift/main.swift +++ b/Sources/rswift/main.swift @@ -85,6 +85,15 @@ let generate = command( let processInfo = ProcessInfo() + // Touch last run file + do { + let tempDirPath = try ProcessInfo().environmentVariable(name: EnvironmentKeys.tempDir) + let lastRunFile = URL(fileURLWithPath: tempDirPath).appendingPathComponent(Rswift.lastRunFile) + try Date().description.write(to: lastRunFile, atomically: true, encoding: .utf8) + } catch { + warn("Failed to write out to '\(Rswift.lastRunFile)', this might cause Xcode to not run the R.swift build phase: \(error)") + } + let xcodeprojPath = try processInfo.environmentVariable(name: EnvironmentKeys.xcodeproj) let targetName = try processInfo.environmentVariable(name: EnvironmentKeys.target) let bundleIdentifier = try processInfo.environmentVariable(name: EnvironmentKeys.bundleIdentifier) @@ -105,6 +114,8 @@ let generate = command( .filter { !$0.isEmpty } .map { Module.custom(name: $0) } + let lastRunURL = URL(fileURLWithPath: tempDir).appendingPathComponent(Rswift.lastRunFile) + let scriptInputFileCountString = try processInfo.environmentVariable(name: EnvironmentKeys.scriptInputFileCount) guard let scriptInputFileCount = Int(scriptInputFileCountString) else { throw ArgumentError.invalidType(value: scriptInputFileCountString, type: "Int", argument: EnvironmentKeys.scriptInputFileCount) @@ -121,6 +132,22 @@ let generate = command( .map(EnvironmentKeys.scriptOutputFile) .map(processInfo.environmentVariable) + let errors = validateRswiftEnvironment( + outputURL: outputURL, + sourceRootPath: sourceRootPath, + scriptInputFiles: scriptInputFiles, + scriptOutputFiles: scriptOutputFiles, + lastRunURL: lastRunURL, + podsRoot: processInfo.environment["PODS_ROOT"], + commandLineArguments: CommandLine.arguments) + guard errors.isEmpty else { + for error in errors { + fail(error) + } + warn("For updating to R.swift 5.0, read our migration guide: https://github.com/mac-cain13/R.swift/blob/master/Documentation/Migration.md") + exit(EXIT_FAILURE) + } + let callInformation = CallInformation( outputURL: outputURL, rswiftIgnoreURL: rswiftIgnoreURL, @@ -135,7 +162,7 @@ let generate = command( scriptInputFiles: scriptInputFiles, scriptOutputFiles: scriptOutputFiles, - lastRunURL: URL(fileURLWithPath: tempDir).appendingPathComponent(Rswift.lastRunFile), + lastRunURL: lastRunURL, buildProductsDirURL: URL(fileURLWithPath: buildProductsDirPath), developerDirURL: URL(fileURLWithPath: developerDirPath), @@ -147,15 +174,6 @@ let generate = command( try RswiftCore.run(callInformation) } -// Touch last run file -do { - let tempDirPath = try ProcessInfo().environmentVariable(name: EnvironmentKeys.tempDir) - let lastRunFile = URL(fileURLWithPath: tempDirPath).appendingPathComponent(Rswift.lastRunFile) - try Date().description.write(to: lastRunFile, atomically: true, encoding: .utf8) -} catch { - warn("Failed to write out to '\(Rswift.lastRunFile)', this might cause Xcode to not run the R.swift build phase: \(error)") -} - // Start parsing the launch arguments let group = Group() group.addCommand("generate", "Generates R.generated.swift file", generate)