Merge master into lockfile

This commit is contained in:
Yonas Kolb
2018-11-12 23:07:29 +11:00
12 changed files with 256 additions and 190 deletions
+10
View File
@@ -0,0 +1,10 @@
import Foundation
import PathKit
import SwiftCLI
extension Path: ConvertibleFromString {
public static func convert(from: String) -> Path? {
return Path(from)
}
}
+20
View File
@@ -0,0 +1,20 @@
import Foundation
import SwiftCLI
class CommandRouter: Router {
let defaultCommand: Command
init(defaultCommand: Command) {
self.defaultCommand = defaultCommand
}
func parse(commandGroup: CommandGroup, arguments: ArgumentList) throws -> (CommandPath, OptionRegistry) {
if !arguments.hasNext() {
arguments.manipulate { _ in
[defaultCommand.name]
}
}
return try DefaultRouter().parse(commandGroup: commandGroup, arguments: arguments)
}
}
+134
View File
@@ -0,0 +1,134 @@
import Foundation
import SwiftCLI
import PathKit
import ProjectSpec
import XcodeGenKit
import xcodeproj
class GenerateCommand: Command {
let name: String = "generate"
let shortDescription: String = "Generate an Xcode project from a spec"
let quiet = Flag("-q", "--quiet",
description: "Suppress all informational and success output",
defaultValue: false)
let useCache = Flag("-c", "--use-cache",
description: "Use a cache for the xcodegen spe",
defaultValue: false)
let spec = Key<Path>("-s", "--spec",
description: "The path to the project spec file. Defaults to project.yml")
let projectDirectory = Key<Path>("-p", "--project", description: "The path to the directory where the project should be generated. Defaults to the directory the spec is in. The filename is defined in the project spec")
let version: Version
init(version: Version) {
self.version = version
}
func execute() throws {
let projectSpecPath = (spec.value ?? "project.yml").absolute()
let projectDirectory = self.projectDirectory.value?.absolute() ?? projectSpecPath.parent()
if !projectSpecPath.exists {
throw GenerationError.missingProjectSpec(projectSpecPath)
}
let specLoader = SpecLoader(version: version)
let project: Project
// load project spec
do {
project = try specLoader.loadProject(path: projectSpecPath)
info("Loaded project:\n \(project.debugDescription.replacingOccurrences(of: "\n", with: "\n "))")
} catch {
throw GenerationError.projectSpecParsingError(error)
}
let projectPath = projectDirectory + "\(project.name).xcodeproj"
let cacheFilePath = Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// read cache
if useCache.value {
do {
cacheFile = try specLoader.generateCacheFile()
} catch {
throw GenerationError.projectSpecParsingError(error)
}
}
// check cache
if let cacheFile = cacheFile,
projectPath.exists,
cacheFilePath.exists {
do {
let existingCacheFile: String = try cacheFilePath.read()
if cacheFile.string == existingCacheFile {
info("Project has not changed since cache was written")
return
}
} catch {
info("Couldn't load cache at \(cacheFile)")
}
}
// validate project
do {
try project.validateMinimumXcodeGenVersion(version)
try project.validate()
} catch let error as SpecValidationError {
throw GenerationError.validationError(error)
}
// generate project
info("⚙️ Generating project...")
let xcodeProject: XcodeProj
do {
let projectGenerator = ProjectGenerator(project: project)
xcodeProject = try projectGenerator.generateXcodeProject()
} catch {
throw GenerationError.generationError(error)
}
// write project
info("⚙️ Writing project...")
do {
let fileWriter = FileWriter(project: project)
try fileWriter.writeXcodeProject(xcodeProject, to: projectPath)
try fileWriter.writePlists()
success("Created project at \(projectPath)")
} catch {
throw GenerationError.writingError(error)
}
// write cache
if let cacheFile = cacheFile {
do {
try cacheFilePath.parent().mkpath()
try cacheFilePath.write(cacheFile.string)
} catch {
info("Failed to write cache: \(error.localizedDescription)")
}
}
}
func info(_ string: String) {
if !quiet.value {
stdout.print(string)
}
}
func success(_ string: String) {
if !quiet.value {
stdout.print(string.green)
}
}
}
+39
View File
@@ -0,0 +1,39 @@
import PathKit
import Foundation
import ProjectSpec
import SwiftCLI
import Rainbow
enum GenerationError: Error, CustomStringConvertible, ProcessError {
case missingProjectSpec(Path)
case projectSpecParsingError(Error)
case cacheGenerationError(Error)
case validationError(SpecValidationError)
case generationError(Error)
case writingError(Error)
var description: String {
switch self {
case .missingProjectSpec(let path):
return "No project spec found at \(path.absolute())"
case .projectSpecParsingError(let error):
return "Parsing project spec failed: \(error)"
case .cacheGenerationError(let error):
return "Couldn't generate cache file: \(error)"
case .validationError(let error):
return error.description
case .generationError(let error):
return String(describing: error)
case .writingError(let error):
return String(describing: error)
}
}
var message: String? {
return description.red
}
var exitStatus: Int32 {
return 1
}
}
+28
View File
@@ -0,0 +1,28 @@
import Foundation
import SwiftCLI
import ProjectSpec
public class XcodeGenCLI {
let cli: CLI
public init(version: Version) {
let generateCommand = GenerateCommand(version: version)
cli = CLI(name: "xcodegen",
version: version.string,
description: "Generates Xcode projects",
commands: [generateCommand])
cli.parser = Parser(router: CommandRouter(defaultCommand: generateCommand))
}
public func execute(arguments: [String]? = nil) {
let status: Int32
if let arguments = arguments {
status = cli.go(with: arguments)
} else {
status = cli.go()
}
exit(status)
}
}