Merge branch 'master' into min/no_codegen

This commit is contained in:
Min Kim 2019-06-13 21:38:52 -07:00 committed by GitHub
commit e034dc982f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 822 additions and 53 deletions

View File

@ -2,11 +2,17 @@
## Next Version
## 2.6.0
#### Added
- Added ability to encode ProjectSpec to JSON [#545](https://github.com/yonaskolb/XcodeGen/pull/545) @ryohey
- Added ability to skip tests [#582](https://github.com/yonaskolb/XcodeGen/pull/582) @kadarandras
- Added ability to set `attributes` on build files [#583](https://github.com/yonaskolb/XcodeGen/pull/583) @min
#### Fixed
- Fixed `.pch` files being bundled as resources [#597](https://github.com/yonaskolb/XcodeGen/pull/597) @thii
- Fixed an issue that prevents watchOS Intents Extension from running correctly. [#571](https://github.com/yonaskolb/XcodeGen/pull/571) @KhaosT
#### Changed
- Updated the default `compatibilityVersion` project setting from `Xcode 9.3` to `Xcode 10.0` [#581](https://github.com/yonaskolb/XcodeGen/pull/581) @acecilia
## 2.5.0
#### Added

View File

@ -692,6 +692,7 @@ A multiline script can be written using the various YAML multiline methods, for
- [x] **name**: **String** - The name of the target
- [ ] **parallelizable**: **Bool** - Whether to run tests in parallel. Defaults to false
- [ ] **randomExecutionOrder**: **Bool** - Whether to run tests in a random order. Defaults to false
- [ ] **skippedTests**: **[String]** - List of tests in the test target to skip. Defaults to empty.
### Archive Action
@ -720,7 +721,12 @@ schemes:
config: prod-debug
commandLineArguments: "--option testValue"
gatherCoverageData: true
targets: [Tester1, Tester2]
targets:
- Tester1
- name: Tester2
parallelizable: true
randomExecutionOrder: true
skippedTests: [Test/testExample()]
environmentVariables:
- variable: TEST_ENV_VAR
value: VALUE

View File

@ -24,7 +24,7 @@ XcodeGen is a command line tool written in Swift that generates your Xcode proje
The project spec is a YAML or JSON file that defines your targets, configurations, schemes, custom build settings and many other options. All your source directories are automatically parsed and referenced appropriately while preserving your folder structure. Sensible defaults are used in many places, so you only need to customize what is needed. Very complex projects can also be defined using more advanced features.
- ✅ Generate projects on demand and remove your `.xcodeproj` file from git, which means **no more merge conflicts**!
- ✅ Generate projects on demand and remove your `.xcodeproj` from git, which means **no more merge conflicts**!
- ✅ Groups and files in Xcode are always **synced** to your directories on disk
- ✅ Easy **configuration** of projects which is human readable and git friendly
- ✅ Easily copy and paste **files and directories** without having to edit anything in Xcode

View File

@ -0,0 +1 @@
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks @executable_path/../../../../Frameworks"

View File

@ -62,6 +62,19 @@ extension AggregateTarget: NamedJSONDictionaryConvertible {
}
}
extension AggregateTarget: JSONEncodable {
public func toJSONValue() -> Any {
return [
"settings": settings.toJSONValue(),
"targets": targets,
"configFiles": configFiles,
"attributes": attributes,
"buildScripts": buildScripts.map { $0.toJSONValue() },
"scheme": scheme?.toJSONValue()
] as [String: Any?]
}
}
extension AggregateTarget: PathContainer {
static var pathProperties: [PathProperty] {

View File

@ -80,3 +80,29 @@ extension BuildRule: JSONObjectConvertible {
name = jsonDictionary.json(atKeyPath: "name")
}
}
extension BuildRule: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"outputFiles": outputFiles,
"outputFilesCompilerFlags": outputFilesCompilerFlags,
"name": name
]
switch fileType {
case .pattern(let string):
dict["filePattern"] = string
case .type(let string):
dict["fileType"] = string
}
switch action {
case .compilerSpec(let string):
dict["compilerSpec"] = string
case .script(let string):
dict["script"] = string
}
return dict
}
}

View File

@ -2,6 +2,8 @@ import Foundation
import JSONUtilities
public struct BuildScript: Equatable {
public static let runOnlyWhenInstallingDefault = false
public static let showEnvVarsDefault = true
public var script: ScriptType
public var name: String?
@ -26,8 +28,8 @@ public struct BuildScript: Equatable {
inputFileLists: [String] = [],
outputFileLists: [String] = [],
shell: String? = nil,
runOnlyWhenInstalling: Bool = false,
showEnvVars: Bool = true
runOnlyWhenInstalling: Bool = runOnlyWhenInstallingDefault,
showEnvVars: Bool = showEnvVarsDefault
) {
self.script = script
self.name = name
@ -57,8 +59,34 @@ extension BuildScript: JSONObjectConvertible {
script = .path(path)
}
shell = jsonDictionary.json(atKeyPath: "shell")
runOnlyWhenInstalling = jsonDictionary.json(atKeyPath: "runOnlyWhenInstalling") ?? false
showEnvVars = jsonDictionary.json(atKeyPath: "showEnvVars") ?? true
runOnlyWhenInstalling = jsonDictionary.json(atKeyPath: "runOnlyWhenInstalling") ?? BuildScript.runOnlyWhenInstallingDefault
showEnvVars = jsonDictionary.json(atKeyPath: "showEnvVars") ?? BuildScript.showEnvVarsDefault
}
}
extension BuildScript: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"inputFiles": inputFiles,
"inputFileLists": inputFileLists,
"outputFiles": outputFiles,
"outputFileLists": outputFileLists,
"runOnlyWhenInstalling": runOnlyWhenInstalling,
"name": name,
"shell": shell
]
if showEnvVars != BuildScript.showEnvVarsDefault {
dict["showEnvVars"] = showEnvVars
}
switch script {
case .path(let string):
dict["path"] = string
case .script(let string):
dict["script"] = string
}
return dict
}
}

View File

@ -2,15 +2,18 @@ import Foundation
import JSONUtilities
public struct Dependency: Equatable {
public static let removeHeadersDefault = true
public static let implicitDefault = false
public static let weakLinkDefault = false
public var type: DependencyType
public var reference: String
public var embed: Bool?
public var codeSign: Bool?
public var removeHeaders: Bool = true
public var removeHeaders: Bool = removeHeadersDefault
public var link: Bool?
public var implicit: Bool = false
public var weakLink: Bool = false
public var implicit: Bool = implicitDefault
public var weakLink: Bool = weakLinkDefault
public init(
type: DependencyType,
@ -18,8 +21,8 @@ public struct Dependency: Equatable {
embed: Bool? = nil,
codeSign: Bool? = nil,
link: Bool? = nil,
implicit: Bool = false,
weakLink: Bool = false
implicit: Bool = implicitDefault,
weakLink: Bool = weakLinkDefault
) {
self.type = type
self.reference = reference
@ -81,6 +84,42 @@ extension Dependency: JSONObjectConvertible {
}
}
extension Dependency: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"embed": embed,
"codeSign": codeSign,
"link": link
]
if removeHeaders != Dependency.removeHeadersDefault {
dict["removeHeaders"] = removeHeaders
}
if implicit != Dependency.implicitDefault {
dict["implicit"] = implicit
}
if weakLink != Dependency.weakLinkDefault {
dict["weak"] = weakLink
}
switch type {
case .target:
dict["target"] = reference
case .framework:
dict["framework"] = reference
case .carthage(let findFrameworks):
dict["carthage"] = reference
if let findFrameworks = findFrameworks {
dict["findFrameworks"] = findFrameworks
}
case .sdk:
dict["sdk"] = reference
}
return dict
}
}
extension Dependency: PathContainer {
static var pathProperties: [PathProperty] {

View File

@ -78,3 +78,14 @@ extension DeploymentTarget: JSONObjectConvertible {
macOS = try parseVersion("macOS")
}
}
extension DeploymentTarget: JSONEncodable {
public func toJSONValue() -> Any {
return [
"iOS": iOS?.string,
"tvOS": tvOS?.string,
"watchOS": watchOS?.string,
"macOS": macOS?.string,
]
}
}

View File

@ -0,0 +1,7 @@
import Foundation
import JSONUtilities
public protocol JSONEncodable {
// returns JSONDictionary or JSONArray or JSONRawType or nil
func toJSONValue() -> Any
}

View File

@ -25,6 +25,15 @@ extension Plist: JSONObjectConvertible {
}
}
extension Plist: JSONEncodable {
public func toJSONValue() -> Any {
return [
"path": path,
"properties": properties
]
}
}
extension Plist: PathContainer {
static var pathProperties: [PathProperty] {

View File

@ -230,3 +230,31 @@ extension BuildSettingsContainer {
return configFiles.values.map { Path($0) }
}
}
extension Project: JSONEncodable {
public func toJSONValue() -> Any {
return toJSONDictionary()
}
public func toJSONDictionary() -> JSONDictionary {
let targetPairs = targets.map { ($0.name, $0.toJSONValue()) }
let configsPairs = configs.map { ($0.name, $0.type?.rawValue) }
let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) }
let schemesPairs = schemes.map { ($0.name, $0.toJSONValue()) }
return [
"name": name,
"options": options.toJSONValue(),
"settings": settings.toJSONValue(),
"fileGroups": fileGroups,
"configFiles": configFiles,
"include": include,
"attributes": attributes,
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
"configs": Dictionary(uniqueKeysWithValues: configsPairs),
"aggregateTargets": Dictionary(uniqueKeysWithValues: aggregateTargetsPairs),
"schemes": Dictionary(uniqueKeysWithValues: schemesPairs),
"settingGroups": settingGroups.mapValues { $0.toJSONValue() }
]
}
}

View File

@ -44,6 +44,9 @@ public struct Scheme: Equatable {
}
public struct Build: Equatable {
public static let parallelizeBuildDefault = true
public static let buildImplicitDependenciesDefault = true
public var targets: [BuildTarget]
public var parallelizeBuild: Bool
public var buildImplicitDependencies: Bool
@ -51,8 +54,8 @@ public struct Scheme: Equatable {
public var postActions: [ExecutionAction]
public init(
targets: [BuildTarget],
parallelizeBuild: Bool = true,
buildImplicitDependencies: Bool = true,
parallelizeBuild: Bool = parallelizeBuildDefault,
buildImplicitDependencies: Bool = buildImplicitDependenciesDefault,
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
) {
@ -86,6 +89,8 @@ public struct Scheme: Equatable {
}
public struct Test: BuildAction {
public static let gatherCoverageDataDefault = false
public var config: String?
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
@ -95,30 +100,37 @@ public struct Scheme: Equatable {
public var environmentVariables: [XCScheme.EnvironmentVariable]
public struct TestTarget: Equatable, ExpressibleByStringLiteral {
public static let randomExecutionOrderDefault = false
public static let parallelizableDefault = false
public let name: String
public var randomExecutionOrder: Bool
public var parallelizable: Bool
public var skippedTests: [String]
public init(
name: String,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false
randomExecutionOrder: Bool = randomExecutionOrderDefault,
parallelizable: Bool = parallelizableDefault,
skippedTests: [String] = []
) {
self.name = name
self.randomExecutionOrder = randomExecutionOrder
self.parallelizable = parallelizable
self.skippedTests = skippedTests
}
public init(stringLiteral value: String) {
name = value
randomExecutionOrder = false
parallelizable = false
skippedTests = []
}
}
public init(
config: String,
gatherCoverageData: Bool = false,
gatherCoverageData: Bool = gatherCoverageDataDefault,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false,
commandLineArguments: [String: Bool] = [:],
@ -174,6 +186,8 @@ public struct Scheme: Equatable {
}
public struct Archive: BuildAction {
public static let revealArchiveInOrganizerDefault = true
public var config: String?
public var customArchiveName: String?
public var revealArchiveInOrganizer: Bool
@ -182,7 +196,7 @@ public struct Scheme: Equatable {
public init(
config: String,
customArchiveName: String? = nil,
revealArchiveInOrganizer: Bool = true,
revealArchiveInOrganizer: Bool = revealArchiveInOrganizerDefault,
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
) {
@ -218,6 +232,16 @@ extension Scheme.ExecutionAction: JSONObjectConvertible {
}
}
extension Scheme.ExecutionAction: JSONEncodable {
public func toJSONValue() -> Any {
return [
"script": script,
"name": name,
"settingsTarget": settingsTarget
]
}
}
extension Scheme.Run: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@ -229,11 +253,23 @@ extension Scheme.Run: JSONObjectConvertible {
}
}
extension Scheme.Run: JSONEncodable {
public func toJSONValue() -> Any {
return [
"commandLineArguments": commandLineArguments,
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Test: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
config = jsonDictionary.json(atKeyPath: "config")
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? Scheme.Test.gatherCoverageDataDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
@ -254,12 +290,48 @@ extension Scheme.Test: JSONObjectConvertible {
}
}
extension Scheme.Test: JSONEncodable {
public func toJSONValue() -> Any {
return [
"gatherCoverageData": gatherCoverageData,
"commandLineArguments": commandLineArguments,
"targets": targets.map { $0.toJSONValue() },
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Test.TestTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
name = try jsonDictionary.json(atKeyPath: "name")
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? false
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? false
randomExecutionOrder = jsonDictionary.json(atKeyPath: "randomExecutionOrder") ?? Scheme.Test.TestTarget.randomExecutionOrderDefault
parallelizable = jsonDictionary.json(atKeyPath: "parallelizable") ?? Scheme.Test.TestTarget.parallelizableDefault
skippedTests = jsonDictionary.json(atKeyPath: "skippedTests") ?? []
}
}
extension Scheme.Test.TestTarget: JSONEncodable {
public func toJSONValue() -> Any {
if !randomExecutionOrder && !parallelizable {
return name
}
var dict: JSONDictionary = [
"name": name
]
if randomExecutionOrder != Scheme.Test.TestTarget.randomExecutionOrderDefault {
dict["randomExecutionOrder"] = randomExecutionOrder
}
if parallelizable != Scheme.Test.TestTarget.parallelizableDefault {
dict["parallelizable"] = parallelizable
}
return dict
}
}
@ -274,6 +346,18 @@ extension Scheme.Profile: JSONObjectConvertible {
}
}
extension Scheme.Profile: JSONEncodable {
public func toJSONValue() -> Any {
return [
"commandLineArguments": commandLineArguments,
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"config": config
] as [String: Any?]
}
}
extension Scheme.Analyze: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@ -281,17 +365,42 @@ extension Scheme.Analyze: JSONObjectConvertible {
}
}
extension Scheme.Analyze: JSONEncodable {
public func toJSONValue() -> Any {
return [
"config": config
]
}
}
extension Scheme.Archive: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
config = jsonDictionary.json(atKeyPath: "config")
customArchiveName = jsonDictionary.json(atKeyPath: "customArchiveName")
revealArchiveInOrganizer = jsonDictionary.json(atKeyPath: "revealArchiveInOrganizer") ?? true
revealArchiveInOrganizer = jsonDictionary.json(atKeyPath: "revealArchiveInOrganizer") ?? Scheme.Archive.revealArchiveInOrganizerDefault
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
extension Scheme.Archive: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
"config": config,
"customArchiveName": customArchiveName,
]
if revealArchiveInOrganizer != Scheme.Archive.revealArchiveInOrganizerDefault {
dict["revealArchiveInOrganizer"] = revealArchiveInOrganizer
}
return dict
}
}
extension Scheme: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
@ -305,6 +414,19 @@ extension Scheme: NamedJSONDictionaryConvertible {
}
}
extension Scheme: JSONEncodable {
public func toJSONValue() -> Any {
return [
"build": build.toJSONValue(),
"run": run?.toJSONValue(),
"test": test?.toJSONValue(),
"analyze": analyze?.toJSONValue(),
"profile": profile?.toJSONValue(),
"archive": archive?.toJSONValue(),
] as [String: Any?]
}
}
extension Scheme.Build: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@ -332,8 +454,29 @@ extension Scheme.Build: JSONObjectConvertible {
self.targets = targets.sorted { $0.target < $1.target }
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? true
buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? true
parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? Scheme.Build.parallelizeBuildDefault
buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? Scheme.Build.buildImplicitDependenciesDefault
}
}
extension Scheme.Build: JSONEncodable {
public func toJSONValue() -> Any {
let targetPairs = targets.map { ($0.target, $0.buildTypes.map { $0.toJSONValue() }) }
var dict: JSONDictionary = [
"targets": Dictionary(uniqueKeysWithValues: targetPairs),
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
]
if parallelizeBuild != Scheme.Build.parallelizeBuildDefault {
dict["parallelizeBuild"] = parallelizeBuild
}
if buildImplicitDependencies != Scheme.Build.buildImplicitDependenciesDefault {
dict["buildImplicitDependencies"] = buildImplicitDependencies
}
return dict
}
}
@ -357,7 +500,20 @@ extension BuildType: JSONPrimitiveConvertible {
}
}
extension BuildType: JSONEncodable {
public func toJSONValue() -> Any {
switch self {
case .testing: return "testing"
case .profiling: return "profiling"
case .running: return "running"
case .archiving: return "archiving"
case .analyzing: return "analyzing"
}
}
}
extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
public static let enabledDefault = true
private static func parseValue(_ value: Any) -> String {
if let bool = value as? Bool {
@ -377,7 +533,7 @@ extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
value = try jsonDictionary.json(atKeyPath: "value")
}
let variable: String = try jsonDictionary.json(atKeyPath: "variable")
let enabled: Bool = jsonDictionary.json(atKeyPath: "isEnabled") ?? true
let enabled: Bool = jsonDictionary.json(atKeyPath: "isEnabled") ?? XCScheme.EnvironmentVariable.enabledDefault
self.init(variable: variable, value: value, enabled: enabled)
}
@ -393,3 +549,18 @@ extension XCScheme.EnvironmentVariable: JSONObjectConvertible {
}
}
}
extension XCScheme.EnvironmentVariable: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any] = [
"variable": variable,
"value": value
]
if enabled != XCScheme.EnvironmentVariable.enabledDefault {
dict["isEnabled"] = enabled
}
return dict
}
}

View File

@ -105,3 +105,16 @@ public func += (lhs: inout BuildSettings, rhs: BuildSettings?) {
guard let rhs = rhs else { return }
lhs.merge(rhs)
}
extension Settings: JSONEncodable {
public func toJSONValue() -> Any {
if groups.count > 0 || configSettings.count > 0 {
return [
"base": buildSettings,
"groups": groups,
"configs": configSettings.mapValues { $0.toJSONValue() }
]
}
return buildSettings
}
}

View File

@ -2,6 +2,12 @@ import Foundation
import JSONUtilities
public struct SpecOptions: Equatable {
public static let settingPresetsDefault = SettingPresets.all
public static let createIntermediateGroupsDefault = false
public static let transitivelyLinkDependenciesDefault = false
public static let groupSortPositionDefault = GroupSortPosition.bottom
public static let generateEmptyDirectoriesDefault = false
public static let findCarthageFrameworksDefault = false
public var minimumXcodeGenVersion: Version?
public var carthageBuildPath: String?
@ -62,9 +68,9 @@ public struct SpecOptions: Equatable {
minimumXcodeGenVersion: Version? = nil,
carthageBuildPath: String? = nil,
carthageExecutablePath: String? = nil,
createIntermediateGroups: Bool = false,
createIntermediateGroups: Bool = createIntermediateGroupsDefault,
bundleIdPrefix: String? = nil,
settingPresets: SettingPresets = .all,
settingPresets: SettingPresets = settingPresetsDefault,
developmentLanguage: String? = nil,
indentWidth: UInt? = nil,
tabWidth: UInt? = nil,
@ -73,10 +79,10 @@ public struct SpecOptions: Equatable {
deploymentTarget: DeploymentTarget = .init(),
disabledValidations: [ValidationType] = [],
defaultConfig: String? = nil,
transitivelyLinkDependencies: Bool = false,
groupSortPosition: GroupSortPosition = .bottom,
generateEmptyDirectories: Bool = false,
findCarthageFrameworks: Bool = false
transitivelyLinkDependencies: Bool = transitivelyLinkDependenciesDefault,
groupSortPosition: GroupSortPosition = groupSortPositionDefault,
generateEmptyDirectories: Bool = generateEmptyDirectoriesDefault,
findCarthageFrameworks: Bool = findCarthageFrameworksDefault
) {
self.minimumXcodeGenVersion = minimumXcodeGenVersion
self.carthageBuildPath = carthageBuildPath
@ -109,8 +115,8 @@ extension SpecOptions: JSONObjectConvertible {
carthageBuildPath = jsonDictionary.json(atKeyPath: "carthageBuildPath")
carthageExecutablePath = jsonDictionary.json(atKeyPath: "carthageExecutablePath")
bundleIdPrefix = jsonDictionary.json(atKeyPath: "bundleIdPrefix")
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? .all
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups") ?? false
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? SpecOptions.settingPresetsDefault
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups") ?? SpecOptions.createIntermediateGroupsDefault
developmentLanguage = jsonDictionary.json(atKeyPath: "developmentLanguage")
usesTabs = jsonDictionary.json(atKeyPath: "usesTabs")
xcodeVersion = jsonDictionary.json(atKeyPath: "xcodeVersion")
@ -119,10 +125,46 @@ extension SpecOptions: JSONObjectConvertible {
deploymentTarget = jsonDictionary.json(atKeyPath: "deploymentTarget") ?? DeploymentTarget()
disabledValidations = jsonDictionary.json(atKeyPath: "disabledValidations") ?? []
defaultConfig = jsonDictionary.json(atKeyPath: "defaultConfig")
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies") ?? false
groupSortPosition = jsonDictionary.json(atKeyPath: "groupSortPosition") ?? .bottom
generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? false
findCarthageFrameworks = jsonDictionary.json(atKeyPath: "findCarthageFrameworks") ?? false
transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies") ?? SpecOptions.transitivelyLinkDependenciesDefault
groupSortPosition = jsonDictionary.json(atKeyPath: "groupSortPosition") ?? SpecOptions.groupSortPositionDefault
generateEmptyDirectories = jsonDictionary.json(atKeyPath: "generateEmptyDirectories") ?? SpecOptions.generateEmptyDirectoriesDefault
findCarthageFrameworks = jsonDictionary.json(atKeyPath: "findCarthageFrameworks") ?? SpecOptions.findCarthageFrameworksDefault
}
}
extension SpecOptions: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"deploymentTarget": deploymentTarget.toJSONValue(),
"transitivelyLinkDependencies": transitivelyLinkDependencies,
"groupSortPosition": groupSortPosition.rawValue,
"disabledValidations": disabledValidations.map { $0.rawValue },
"minimumXcodeGenVersion": minimumXcodeGenVersion?.string,
"carthageBuildPath": carthageBuildPath,
"carthageExecutablePath": carthageExecutablePath,
"bundleIdPrefix": bundleIdPrefix,
"developmentLanguage": developmentLanguage,
"usesTabs": usesTabs,
"xcodeVersion": xcodeVersion,
"indentWidth": indentWidth.flatMap { Int($0) },
"tabWidth": tabWidth.flatMap { Int($0) },
"defaultConfig": defaultConfig,
]
if settingPresets != SpecOptions.settingPresetsDefault {
dict["settingPresets"] = settingPresets.rawValue
}
if createIntermediateGroups != SpecOptions.createIntermediateGroupsDefault {
dict["createIntermediateGroups"] = createIntermediateGroups
}
if generateEmptyDirectories != SpecOptions.generateEmptyDirectoriesDefault {
dict["generateEmptyDirectories"] = generateEmptyDirectories
}
if findCarthageFrameworks != SpecOptions.findCarthageFrameworksDefault {
dict["findCarthageFrameworks"] = findCarthageFrameworks
}
return dict
}
}

View File

@ -3,6 +3,8 @@ import JSONUtilities
import xcodeproj
public struct LegacyTarget: Equatable {
public static let passSettingsDefault = false
public var toolPath: String
public var arguments: String?
public var passSettings: Bool
@ -10,7 +12,7 @@ public struct LegacyTarget: Equatable {
public init(
toolPath: String,
passSettings: Bool = false,
passSettings: Bool = passSettingsDefault,
arguments: String? = nil,
workingDirectory: String? = nil
) {
@ -267,11 +269,27 @@ extension LegacyTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
toolPath = try jsonDictionary.json(atKeyPath: "toolPath")
arguments = jsonDictionary.json(atKeyPath: "arguments")
passSettings = jsonDictionary.json(atKeyPath: "passSettings") ?? false
passSettings = jsonDictionary.json(atKeyPath: "passSettings") ?? LegacyTarget.passSettingsDefault
workingDirectory = jsonDictionary.json(atKeyPath: "workingDirectory")
}
}
extension LegacyTarget: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"toolPath": toolPath,
"arguments": arguments,
"workingDirectory": workingDirectory,
]
if passSettings != LegacyTarget.passSettingsDefault {
dict["passSettings"] = passSettings
}
return dict
}
}
extension Target: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
@ -342,3 +360,36 @@ extension Target: NamedJSONDictionaryConvertible {
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
}
}
extension Target: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"type": type.name,
"platform": platform.rawValue,
"settings": settings.toJSONValue(),
"configFiles": configFiles,
"attributes": attributes,
"sources": sources.map { $0.toJSONValue() },
"dependencies": dependencies.map { $0.toJSONValue() },
"postCompileScripts": postCompileScripts.map{ $0.toJSONValue() },
"prebuildScripts": preBuildScripts.map{ $0.toJSONValue() },
"postbuildScripts": postBuildScripts.map{ $0.toJSONValue() },
"buildRules": buildRules.map{ $0.toJSONValue() },
"deploymentTarget": deploymentTarget?.deploymentTarget,
"info": info?.toJSONValue(),
"entitlements": entitlements?.toJSONValue(),
"transitivelyLinkDependencies": transitivelyLinkDependencies,
"directlyEmbedCarthageDependencies": directlyEmbedCarthageDependencies,
"requiresObjCLinking": requiresObjCLinking,
"scheme": scheme?.toJSONValue(),
"legacy": legacy?.toJSONValue(),
]
if productName != name {
dict["productName"] = productName
}
return dict
}
}

View File

@ -3,6 +3,8 @@ import JSONUtilities
import xcodeproj
public struct TargetScheme: Equatable {
public static let gatherCoverageDataDefault = false
public var testTargets: [Scheme.Test.TestTarget]
public var configVariants: [String]
public var gatherCoverageData: Bool
@ -14,7 +16,7 @@ public struct TargetScheme: Equatable {
public init(
testTargets: [Scheme.Test.TestTarget] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
gatherCoverageData: Bool = gatherCoverageDataDefault,
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = [],
preActions: [Scheme.ExecutionAction] = [],
@ -47,10 +49,29 @@ extension TargetScheme: JSONObjectConvertible {
testTargets = []
}
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? TargetScheme.gatherCoverageDataDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
extension TargetScheme: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any] = [
"configVariants": configVariants,
"commandLineArguments": commandLineArguments,
"testTargets": testTargets.map { $0.toJSONValue() },
"environmentVariables": environmentVariables.map { $0.toJSONValue() },
"preActions": preActions.map { $0.toJSONValue() },
"postActions": postActions.map { $0.toJSONValue() },
]
if gatherCoverageData != TargetScheme.gatherCoverageDataDefault {
dict["gatherCoverageData"] = gatherCoverageData
}
return dict
}
}

View File

@ -4,6 +4,7 @@ import PathKit
import xcodeproj
public struct TargetSource: Equatable {
public static let optionalDefault = false
public var path: String
public var name: String?
@ -124,7 +125,7 @@ public struct TargetSource: Equatable {
compilerFlags: [String] = [],
excludes: [String] = [],
type: SourceType? = nil,
optional: Bool = false,
optional: Bool = optionalDefault,
buildPhase: BuildPhase? = nil,
headerVisibility: HeaderVisibility? = nil,
createIntermediateGroups: Bool? = nil,
@ -172,7 +173,7 @@ extension TargetSource: JSONObjectConvertible {
headerVisibility = jsonDictionary.json(atKeyPath: "headerVisibility")
excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
type = jsonDictionary.json(atKeyPath: "type")
optional = jsonDictionary.json(atKeyPath: "optional") ?? false
optional = jsonDictionary.json(atKeyPath: "optional") ?? TargetSource.optionalDefault
if let string: String = jsonDictionary.json(atKeyPath: "buildPhase") {
buildPhase = try BuildPhase(string: string)
@ -185,6 +186,32 @@ extension TargetSource: JSONObjectConvertible {
}
}
extension TargetSource: JSONEncodable {
public func toJSONValue() -> Any {
var dict: [String: Any?] = [
"compilerFlags": compilerFlags,
"excludes": excludes,
"name": name,
"headerVisibility": headerVisibility?.rawValue,
"type": type?.rawValue,
"buildPhase": buildPhase?.toJSONValue(),
"createIntermediateGroups": createIntermediateGroups,
]
if optional != TargetSource.optionalDefault {
dict["optional"] = optional
}
if dict.count == 0 {
return path
}
dict["path"] = path
return dict
}
}
extension TargetSource.BuildPhase {
public init(string: String) throws {
@ -208,6 +235,21 @@ extension TargetSource.BuildPhase: JSONObjectConvertible {
}
}
extension TargetSource.BuildPhase: JSONEncodable {
public func toJSONValue() -> Any {
switch self {
case .sources: return "sources"
case .headers: return "headers"
case .resources: return "resources"
case .copyFiles(let files): return ["copyFiles": files.toJSONValue()]
case .none: return "none"
case .frameworks: fatalError("invalid build phase")
case .runScript: fatalError("invalid build phase")
case .carbonResources: fatalError("invalid build phase")
}
}
}
extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
@ -217,6 +259,15 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible {
}
}
extension TargetSource.BuildPhase.CopyFilesSettings: JSONEncodable {
public func toJSONValue() -> Any {
return [
"destination": destination.rawValue,
"subpath": subpath
]
}
}
extension TargetSource: PathContainer {
static var pathProperties: [PathProperty] {

View File

@ -87,7 +87,7 @@ public class PBXProjGenerator {
PBXProject(
name: project.name,
buildConfigurationList: buildConfigList,
compatibilityVersion: project.compatabilityVersion,
compatibilityVersion: project.compatibilityVersion,
mainGroup: mainGroup,
developmentRegion: project.options.developmentLanguage ?? "en"
)

View File

@ -133,7 +133,8 @@ public class SchemeGenerator {
skipped: false,
parallelizable: testTarget.parallelizable,
randomExecutionOrdering: testTarget.randomExecutionOrder,
buildableReference: testBuilEntries.buildableReference
buildableReference: testBuilEntries.buildableReference,
skippedTests: testTarget.skippedTests.map(XCScheme.SkippedTest.init)
)
}

View File

@ -224,7 +224,8 @@ class SourceGenerator {
"gpx",
"lproj",
"xcfilelist",
"apns":
"apns",
"pch":
return nil
default:
return .resources

View File

@ -11,8 +11,8 @@ extension Project {
return "1.3"
}
var compatabilityVersion: String {
return "Xcode 9.3"
var compatibilityVersion: String {
return "Xcode 10.0"
}
var objectVersion: UInt {

View File

@ -315,7 +315,7 @@
LastUpgradeCheck = 1020;
};
buildConfigurationList = D91E14E36EC0B415578456F264E0161E /* Build configuration list for PBXProject "Project" */;
compatibilityVersion = "Xcode 9.3";
compatibilityVersion = "Xcode 10.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (

View File

@ -1498,7 +1498,7 @@
};
};
buildConfigurationList = D91E14E36EC0B415578456F264E0161E /* Build configuration list for PBXProject "Project" */;
compatibilityVersion = "Xcode 9.3";
compatibilityVersion = "Xcode 10.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (

View File

@ -256,6 +256,248 @@ class ProjectSpecTests: XCTestCase {
}
}
}
func testJSONEncodable() {
describe {
$0.it("encodes to json") {
let proj = Project(basePath: Path.current,
name: "ToJson",
configs: [Config(name: "DevelopmentConfig", type: .debug), Config(name: "ProductionConfig", type: .release)],
targets: [Target(name: "App",
type: .application,
platform: .iOS,
productName: "App",
deploymentTarget: Version(major: 0, minor: 1, patch: 2),
settings: Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"]),
configFiles: ["foo": "bar"],
sources: [TargetSource(path: "Source",
name: "Source",
compilerFlags: ["-Werror"],
excludes: ["foo", "bar"],
type: .folder,
optional: true,
buildPhase: .resources,
headerVisibility: .private,
createIntermediateGroups: true)],
dependencies: [Dependency(type: .carthage(findFrameworks: true),
reference: "reference",
embed: true,
codeSign: true,
link: true,
implicit: true,
weakLink: true)],
info: Plist(path: "info.plist", attributes: ["foo": "bar"]),
entitlements: Plist(path: "entitlements.plist", attributes: ["foo": "bar"]),
transitivelyLinkDependencies: true,
directlyEmbedCarthageDependencies: true,
requiresObjCLinking: true,
preBuildScripts: [BuildScript(script: .script("pwd"),
name: "Foo script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
postCompileScripts: [BuildScript(script: .path("cmd.sh"),
name: "Bar script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
postBuildScripts: [BuildScript(script: .path("cmd.sh"),
name: "an another script",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: true)],
buildRules: [BuildRule(fileType: .pattern("*.xcassets"),
action: .script("pre_process_swift.py"),
name: "My Build Rule",
outputFiles: ["$(SRCROOT)/Generated.swift"],
outputFilesCompilerFlags: ["foo"]),
BuildRule(fileType: .type("sourcecode.swift"),
action: .compilerSpec("com.apple.xcode.tools.swift.compiler"),
name: nil,
outputFiles: ["bar"],
outputFilesCompilerFlags: ["foo"]),],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(name: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
gatherCoverageData: true,
commandLineArguments: ["foo": true],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable",
value: "bar",
enabled: true)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
legacy: LegacyTarget(toolPath: "foo",
passSettings: true,
arguments: "bar",
workingDirectory: "foo"),
attributes: ["foo": "bar"])],
aggregateTargets: [AggregateTarget(name: "aggregate target",
targets: ["App"],
settings: Settings(buildSettings: ["buildSettings": "bar"],
configSettings: ["configSettings": Settings(buildSettings: [:],
configSettings: [:],
groups: [])],
groups: ["foo"]),
configFiles: ["configFiles": "bar"],
buildScripts: [BuildScript(script: .path("script"),
name: "foo",
inputFiles: ["foo"],
outputFiles: ["bar"],
inputFileLists: ["foo.xcfilelist"],
outputFileLists: ["bar.xcfilelist"],
shell: "/bin/bash",
runOnlyWhenInstalling: true,
showEnvVars: false)],
scheme: TargetScheme(testTargets: [Scheme.Test.TestTarget(name: "test target",
randomExecutionOrder: false,
parallelizable: false)],
configVariants: ["foo"],
gatherCoverageData: true,
commandLineArguments: ["foo": true],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "environmentVariable",
value: "bar",
enabled: true)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
attributes: ["foo": "bar"])],
settings: Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"]),
settingGroups: ["foo": Settings(buildSettings: ["foo": "bar"],
configSettings: ["foo" : Settings(buildSettings: ["nested": "config"],
configSettings: [:],
groups: ["config-setting-group"])],
groups: ["setting-group"])],
schemes: [Scheme(name: "scheme",
build: Scheme.Build(targets: [Scheme.BuildTarget(target: "foo",
buildTypes: [.archiving, .analyzing])],
parallelizeBuild: false,
buildImplicitDependencies: false,
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]),
run: Scheme.Run(config: "run config",
commandLineArguments: ["foo": true],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
test: Scheme.Test(config: "Config",
gatherCoverageData: true,
randomExecutionOrder: false,
parallelizable: false,
commandLineArguments: ["foo": true],
targets: [Scheme.Test.TestTarget(name: "foo",
randomExecutionOrder: false,
parallelizable: false)],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
profile: Scheme.Profile(config: "profile config",
commandLineArguments: ["foo": true],
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "foo",
value: "bar",
enabled: false)]),
analyze: Scheme.Analyze(config: "analyze config"),
archive: Scheme.Archive(config: "archive config",
customArchiveName: "customArchiveName",
revealArchiveInOrganizer: true,
preActions: [Scheme.ExecutionAction(name: "preAction",
script: "bar",
settingsTarget: "foo")],
postActions: [Scheme.ExecutionAction(name: "postAction",
script: "bar",
settingsTarget: "foo")]))],
options: SpecOptions(minimumXcodeGenVersion: Version(major: 3, minor: 4, patch: 5),
carthageBuildPath: "carthageBuildPath",
carthageExecutablePath: "carthageExecutablePath",
createIntermediateGroups: true,
bundleIdPrefix: "bundleIdPrefix",
settingPresets: .project,
developmentLanguage: "developmentLanguage",
indentWidth: 123,
tabWidth: 456,
usesTabs: true,
xcodeVersion: "xcodeVersion",
deploymentTarget: DeploymentTarget(iOS: Version(major: 1, minor: 2, patch: 3),
tvOS: nil,
watchOS: Version(major: 4, minor: 5, patch: 6),
macOS: nil),
disabledValidations: [.missingConfigFiles],
defaultConfig: "defaultConfig",
transitivelyLinkDependencies: true,
groupSortPosition: .top,
generateEmptyDirectories: true,
findCarthageFrameworks: false),
fileGroups: ["foo", "bar"],
configFiles: ["configFiles": "bar"],
attributes: ["attributes": "bar"])
let json = proj.toJSONDictionary()
let restoredProj = try Project(basePath: Path.current, jsonDictionary: json)
// Examin some properties to make debugging easier
try expect(proj.aggregateTargets) == restoredProj.aggregateTargets
try expect(proj.configFiles) == restoredProj.configFiles
try expect(proj.settings) == restoredProj.settings
try expect(proj.basePath) == restoredProj.basePath
try expect(proj.fileGroups) == restoredProj.fileGroups
try expect(proj.schemes) == restoredProj.schemes
try expect(proj.options) == restoredProj.options
try expect(proj.settingGroups) == restoredProj.settingGroups
try expect(proj.targets) == restoredProj.targets
try expect(proj) == restoredProj
}
}
}
}
fileprivate func expectValidationError(_ project: Project, _ expectedError: SpecValidationError.ValidationError, file: String = #file, line: Int = #line) throws {

View File

@ -729,6 +729,7 @@ class SpecLoadingTests: XCTestCase {
"name": "Target2",
"parallelizable": true,
"randomExecutionOrder": true,
"skippedTests": ["Test/testExample()"]
],
],
"gatherCoverageData": true,
@ -760,7 +761,8 @@ class SpecLoadingTests: XCTestCase {
Scheme.Test.TestTarget(
name: "Target2",
randomExecutionOrder: true,
parallelizable: true
parallelizable: true,
skippedTests: ["Test/testExample()"]
),
]
)