mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2024-12-11 07:16:40 +03:00
Merge master into lockfile
This commit is contained in:
commit
20eaafd246
@ -11,6 +11,7 @@
|
||||
|
||||
#### Changed
|
||||
- Changed spelling of build phases to **preBuildPhase** and **postBuildPhase**. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones
|
||||
- **BREAKING** Moved generation to a specific subcommand `xcodegen generate`. If not specifying any arguments `xcodegen` will still work [#437](https://github.com/yonaskolb/XcodeGen/pull/437) @yonaskolb
|
||||
|
||||
## 2.0.0
|
||||
|
||||
|
@ -10,15 +10,6 @@
|
||||
"version": "4.3.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Commander",
|
||||
"repositoryURL": "https://github.com/kylef/Commander.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e5b50ad7b2e91eeb828393e89b03577b16be7db9",
|
||||
"version": "0.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "JSONUtilities",
|
||||
"repositoryURL": "https://github.com/yonaskolb/JSONUtilities.git",
|
||||
@ -55,6 +46,15 @@
|
||||
"version": "0.9.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftCLI",
|
||||
"repositoryURL": "https://github.com/jakeheis/SwiftCLI.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "37f4a7f863f6fe76ce44fc0023f331eea0089beb",
|
||||
"version": "5.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftShell",
|
||||
"repositoryURL": "https://github.com/kareman/SwiftShell",
|
||||
|
@ -11,18 +11,23 @@ let package = Package(
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/kylef/PathKit.git", from: "0.9.0"),
|
||||
.package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"),
|
||||
.package(url: "https://github.com/jpsim/Yams.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.1.0"),
|
||||
.package(url: "https://github.com/kylef/Spectre.git", from: "0.9.0"),
|
||||
.package(url: "https://github.com/onevcat/Rainbow.git", from: "3.0.0"),
|
||||
.package(url: "https://github.com/tuist/xcodeproj.git", from: "6.3.0"),
|
||||
.package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "5.2.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "XcodeGen", dependencies: [
|
||||
"XcodeGenCLI",
|
||||
]),
|
||||
.target(name: "XcodeGenCLI", dependencies: [
|
||||
"XcodeGenKit",
|
||||
"Commander",
|
||||
"ProjectSpec",
|
||||
"SwiftCLI",
|
||||
"Rainbow",
|
||||
"PathKit",
|
||||
]),
|
||||
.target(name: "XcodeGenKit", dependencies: [
|
||||
"ProjectSpec",
|
||||
|
@ -118,15 +118,17 @@ Simply run:
|
||||
xcodegen
|
||||
```
|
||||
|
||||
This will look for a project spec in the current directory called `project.yml`
|
||||
This will look for a project spec in the current directory called `project.yml` and generate an Xcode project with the name defined in the spec.
|
||||
|
||||
Use `xcodegen --help` to see the list of options:
|
||||
To specify any options use the full `xcodegen generate` command and add the following:
|
||||
|
||||
- **--spec**: An optional path to a `.yml` or `.json` project spec.
|
||||
- **--spec**: An optional path to a `.yml` or `.json` project spec. Defaults to `project.yml`
|
||||
- **--project**: An optional path to a directory where the project will be generated. By default this is the directory the spec lives in.
|
||||
- **--quiet**: Suppress informational and success messages.
|
||||
- **--use-cache**: Used to prevent unnecessarily generating the project. If this is set, then a cache file will be written to when a project is generated. If `xcodegen` is later run but the spec and all the files it contains are the same, the project won't be generated.
|
||||
|
||||
Use `xcodegen help` to see more detailed usage information.
|
||||
|
||||
## Editing
|
||||
```shell
|
||||
git clone https://github.com/yonaskolb/XcodeGen.git
|
||||
|
@ -95,7 +95,6 @@ extension Project: CustomDebugStringConvertible {
|
||||
if !settingGroups.isEmpty {
|
||||
string += "\nSetting Groups:\n\(indent)" + settingGroups.keys
|
||||
.sorted()
|
||||
.map { "⚙️ \($0)" }
|
||||
.joined(separator: "\n\(indent)")
|
||||
}
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
import Foundation
|
||||
import Rainbow
|
||||
|
||||
struct Logger {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let isQuiet: Bool
|
||||
let isColored: Bool
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init(isQuiet: Bool = false, isColored: Bool = true) {
|
||||
self.isQuiet = isQuiet
|
||||
self.isColored = isColored
|
||||
}
|
||||
|
||||
// MARK: - Logging
|
||||
|
||||
func error(_ message: String) {
|
||||
print(isColored ? message.red : message)
|
||||
}
|
||||
|
||||
func info(_ message: String) {
|
||||
if isQuiet {
|
||||
return
|
||||
}
|
||||
|
||||
print(message)
|
||||
}
|
||||
|
||||
func success(_ message: String) {
|
||||
if isQuiet {
|
||||
return
|
||||
}
|
||||
|
||||
print(isColored ? message.green : message)
|
||||
}
|
||||
}
|
@ -1,140 +1,7 @@
|
||||
import Commander
|
||||
import Foundation
|
||||
import JSONUtilities
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
import XcodeGenKit
|
||||
import xcodeproj
|
||||
import Yams
|
||||
import XcodeGenCLI
|
||||
|
||||
let version = try Version("2.0.0")
|
||||
|
||||
func generate(spec: String, project: String, useCache: Bool, isQuiet: Bool, justVersion: Bool) {
|
||||
if justVersion {
|
||||
print(version)
|
||||
exit(EXIT_SUCCESS)
|
||||
}
|
||||
|
||||
let logger = Logger(isQuiet: isQuiet)
|
||||
|
||||
func fatalError(_ message: String) -> Never {
|
||||
logger.error(message)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let projectSpecPath = Path(spec).absolute()
|
||||
var projectPath = project == "" ? projectSpecPath.parent() : Path(project).absolute()
|
||||
|
||||
if !projectSpecPath.exists {
|
||||
fatalError("No project spec found at \(projectSpecPath.absolute())")
|
||||
}
|
||||
|
||||
let specLoader = SpecLoader(version: version)
|
||||
let project: Project
|
||||
|
||||
// load project spec
|
||||
do {
|
||||
project = try specLoader.loadProject(path: projectSpecPath)
|
||||
projectPath = projectPath + "\(project.name).xcodeproj"
|
||||
} catch let error as CustomStringConvertible {
|
||||
fatalError("Parsing project spec failed: \(error)")
|
||||
} catch {
|
||||
fatalError("Parsing project spec failed: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
let cacheFilePath = Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
|
||||
var cacheFile: CacheFile?
|
||||
|
||||
// read cache
|
||||
if useCache {
|
||||
do {
|
||||
cacheFile = try specLoader.generateCacheFile()
|
||||
} catch {
|
||||
logger.error("Couldn't generate cache file: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
// check cache
|
||||
if let cacheFile = cacheFile,
|
||||
projectPath.exists,
|
||||
cacheFilePath.exists {
|
||||
do {
|
||||
let existingCacheFile: String = try cacheFilePath.read()
|
||||
if cacheFile.string == existingCacheFile {
|
||||
logger.success("Project has not changed since cache was written")
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
logger.error("Couldn't load cache at \(cacheFile)")
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Loaded project:\n \(project.debugDescription.replacingOccurrences(of: "\n", with: "\n "))")
|
||||
|
||||
do {
|
||||
// validation
|
||||
try project.validateMinimumXcodeGenVersion(version)
|
||||
try project.validate()
|
||||
|
||||
// generation
|
||||
logger.info("⚙️ Generating project...")
|
||||
let projectGenerator = ProjectGenerator(project: project)
|
||||
let xcodeProject = try projectGenerator.generateXcodeProject()
|
||||
|
||||
// file writing
|
||||
logger.info("⚙️ Writing project...")
|
||||
let fileWriter = FileWriter(project: project)
|
||||
try fileWriter.writeXcodeProject(xcodeProject, to: projectPath)
|
||||
try fileWriter.writePlists()
|
||||
|
||||
logger.success("💾 Saved project to \(projectPath)")
|
||||
} catch let error as SpecValidationError {
|
||||
fatalError(error.description)
|
||||
} catch {
|
||||
fatalError("Generation failed: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
// write cache
|
||||
if let cacheFile = cacheFile {
|
||||
do {
|
||||
try cacheFilePath.parent().mkpath()
|
||||
try cacheFilePath.write(cacheFile.string)
|
||||
} catch {
|
||||
logger.error("Failed to write cache: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command(
|
||||
Option<String>(
|
||||
"spec",
|
||||
default: "project.yml",
|
||||
flag: "s",
|
||||
description: "The path to the project spec file"
|
||||
),
|
||||
Option<String>(
|
||||
"project",
|
||||
default: "",
|
||||
flag: "p",
|
||||
description: "The path to the folder where the project should be generated"
|
||||
),
|
||||
Flag(
|
||||
"use-cache",
|
||||
default: false,
|
||||
flag: "c",
|
||||
description: "Use a cache for the xcodegen spec"
|
||||
),
|
||||
Flag(
|
||||
"quiet",
|
||||
default: false,
|
||||
flag: "q",
|
||||
description: "Suppress printing of informational and success messages"
|
||||
),
|
||||
Flag(
|
||||
"version",
|
||||
default: false,
|
||||
flag: "v",
|
||||
description: "Show XcodeGen version"
|
||||
),
|
||||
generate
|
||||
).run(version.description)
|
||||
let cli = XcodeGenCLI(version: version)
|
||||
cli.execute()
|
||||
|
10
Sources/XcodeGenCLI/Arguments.swift
Normal file
10
Sources/XcodeGenCLI/Arguments.swift
Normal 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
Sources/XcodeGenCLI/CommandRouter.swift
Normal file
20
Sources/XcodeGenCLI/CommandRouter.swift
Normal 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
Sources/XcodeGenCLI/GenerateCommand.swift
Normal file
134
Sources/XcodeGenCLI/GenerateCommand.swift
Normal 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
Sources/XcodeGenCLI/GenerationError.swift
Normal file
39
Sources/XcodeGenCLI/GenerationError.swift
Normal 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
Sources/XcodeGenCLI/XcodeGenCLI.swift
Normal file
28
Sources/XcodeGenCLI/XcodeGenCLI.swift
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user