Files
XcodeGen/Sources/XcodeGenCLI/Commands/GenerateCommand.swift
T
Wendy Liga 43e40a9efc Added support for scheme management (#1142)
* wip

* Added scheme management metadata properties to TargetScheme

* wip

* Added Scheme.Management for metadata

* use sharedDefault

* move generateSchemeManagement to SchemeGenerator

* revert

* update changelog

* update docs

* update test case

* remove

* remove unused file

* revert

* changed on fixtures project

* Updated ProjectSpec docs index

* User Scheme.Management also for TargetSchemes, use XCUserData

* Changelog

* Switch XcodeProj back tuist/XcodeProj - 8.9.0

* Reindent TOC Docs/ProjectSpec.md

* Revert swift tools version

---------

Co-authored-by: Tieme van Veen <tiemevanveen@hotmail.com>
2023-02-24 09:49:57 +11:00

160 lines
5.0 KiB
Swift

import Foundation
import PathKit
import ProjectSpec
import SwiftCLI
import XcodeGenKit
import XcodeProj
import Version
class GenerateCommand: ProjectCommand {
@Flag("-q", "--quiet", description: "Suppress all informational and success output")
var quiet: Bool
@Flag("-c", "--use-cache", description: "Use a cache for the xcodegen spec. This will prevent unnecessarily generating the project if nothing has changed")
var useCache: Bool
@Key("--cache-path", description: "Where the cache file will be loaded from and save to. Defaults to ~/.xcodegen/cache/{SPEC_PATH_HASH}")
var cacheFilePath: Path?
@Key("-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")
var projectDirectory: Path?
@Flag("--only-plists", description: "Generate only plist files")
var onlyPlists: Bool
init(version: Version) {
super.init(version: version,
name: "generate",
shortDescription: "Generate an Xcode project from a spec")
}
override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {
let projectDirectory = self.projectDirectory?.absolute() ?? projectSpecPath.parent()
// validate project dictionary
do {
try specLoader.validateProjectDictionaryWarnings()
} catch {
warning("\(error)")
}
let projectPath = projectDirectory + "\(project.name).xcodeproj"
let cacheFilePath = self.cacheFilePath ??
Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// read cache
if useCache || self.cacheFilePath != nil {
do {
cacheFile = try specLoader.generateCacheFile()
} catch {
throw GenerationError.projectSpecParsingError(error)
}
}
let projectExists = XcodeProj.pbxprojPath(projectPath).exists
// check cache
if let cacheFile = cacheFile,
projectExists,
cacheFilePath.exists {
do {
let existingCacheFile: String = try cacheFilePath.read()
if cacheFile.string == existingCacheFile {
info("Project \(project.name) 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)
}
// run pre gen command
if let command = project.options.preGenCommand {
try Task.run(bash: command, directory: projectDirectory.absolute().string)
}
// generate plists
info("⚙️ Generating plists...")
let fileWriter = FileWriter(project: project)
do {
try fileWriter.writePlists()
if onlyPlists {
return
}
} catch {
throw GenerationError.writingError(error)
}
// generate project
info("⚙️ Generating project...")
let xcodeProject: XcodeProj
do {
let projectGenerator = ProjectGenerator(project: project)
guard let userName = ProcessInfo.processInfo.environment["LOGNAME"] else {
throw GenerationError.missingUsername
}
xcodeProject = try projectGenerator.generateXcodeProject(in: projectDirectory, userName: userName)
} catch {
throw GenerationError.generationError(error)
}
// write project
info("⚙️ Writing project...")
do {
try fileWriter.writeXcodeProject(xcodeProject, to: projectPath)
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)")
}
}
// run post gen command
if let command = project.options.postGenCommand {
try Task.run(bash: command, directory: projectDirectory.absolute().string)
}
}
func info(_ string: String) {
if !quiet {
stdout.print(string)
}
}
func warning(_ string: String) {
if !quiet {
stdout.print(string.yellow)
}
}
func success(_ string: String) {
if !quiet {
stdout.print(string.green)
}
}
}