Skip to content

Commit

Permalink
Improved errors for R.swift 5 (mac-cain13#469)
Browse files Browse the repository at this point in the history
* Collect all commandline argument errors, and show them at once

* Only touch file when running generate command. To prevent warnings when using --help or --version

* Move Rswift environment validation to separate file
  • Loading branch information
tomlokhorst authored and mac-cain13 committed Dec 12, 2018
1 parent a4bf31e commit 66ef687
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 31 deletions.
10 changes: 5 additions & 5 deletions Sources/RswiftCore/CallInformation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
61 changes: 61 additions & 0 deletions Sources/RswiftCore/EnvironmentValidation.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
15 changes: 0 additions & 15 deletions Sources/RswiftCore/RswiftCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion Sources/RswiftCore/Util/ErrorOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
}
38 changes: 28 additions & 10 deletions Sources/rswift/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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),
Expand All @@ -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)
Expand Down

0 comments on commit 66ef687

Please sign in to comment.