diff --git a/CHANGELOG.md b/CHANGELOG.md index e681dadf..7f685f4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 40ec1450..1fc9e6e1 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -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 diff --git a/README.md b/README.md index f3ebef18..33297f2d 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/SettingPresets/Products/app-extension.intents-service.yml b/SettingPresets/Products/app-extension.intents-service.yml new file mode 100644 index 00000000..fb762bf9 --- /dev/null +++ b/SettingPresets/Products/app-extension.intents-service.yml @@ -0,0 +1 @@ +LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks @executable_path/../../../../Frameworks" diff --git a/Sources/ProjectSpec/AggregateTarget.swift b/Sources/ProjectSpec/AggregateTarget.swift index e25fb553..d861958f 100644 --- a/Sources/ProjectSpec/AggregateTarget.swift +++ b/Sources/ProjectSpec/AggregateTarget.swift @@ -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] { diff --git a/Sources/ProjectSpec/BuildRule.swift b/Sources/ProjectSpec/BuildRule.swift index 442dda24..3f75e75f 100644 --- a/Sources/ProjectSpec/BuildRule.swift +++ b/Sources/ProjectSpec/BuildRule.swift @@ -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 + } +} diff --git a/Sources/ProjectSpec/BuildScript.swift b/Sources/ProjectSpec/BuildScript.swift index 242a2813..f1eaf33a 100644 --- a/Sources/ProjectSpec/BuildScript.swift +++ b/Sources/ProjectSpec/BuildScript.swift @@ -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 } } diff --git a/Sources/ProjectSpec/Dependency.swift b/Sources/ProjectSpec/Dependency.swift index d9495e44..8da36e6d 100644 --- a/Sources/ProjectSpec/Dependency.swift +++ b/Sources/ProjectSpec/Dependency.swift @@ -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] { diff --git a/Sources/ProjectSpec/DeploymentTarget.swift b/Sources/ProjectSpec/DeploymentTarget.swift index cf797b02..7cba891a 100644 --- a/Sources/ProjectSpec/DeploymentTarget.swift +++ b/Sources/ProjectSpec/DeploymentTarget.swift @@ -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, + ] + } +} diff --git a/Sources/ProjectSpec/Encoding.swift b/Sources/ProjectSpec/Encoding.swift new file mode 100644 index 00000000..6934f0b2 --- /dev/null +++ b/Sources/ProjectSpec/Encoding.swift @@ -0,0 +1,7 @@ +import Foundation +import JSONUtilities + +public protocol JSONEncodable { + // returns JSONDictionary or JSONArray or JSONRawType or nil + func toJSONValue() -> Any +} diff --git a/Sources/ProjectSpec/Plist.swift b/Sources/ProjectSpec/Plist.swift index 1f295106..e174a9ab 100644 --- a/Sources/ProjectSpec/Plist.swift +++ b/Sources/ProjectSpec/Plist.swift @@ -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] { diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 7eb90873..2790d008 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -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() } + ] + } +} diff --git a/Sources/ProjectSpec/Scheme.swift b/Sources/ProjectSpec/Scheme.swift index 6865c284..29ee1241 100644 --- a/Sources/ProjectSpec/Scheme.swift +++ b/Sources/ProjectSpec/Scheme.swift @@ -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 + } +} diff --git a/Sources/ProjectSpec/Settings.swift b/Sources/ProjectSpec/Settings.swift index 95254dd6..c223ab9f 100644 --- a/Sources/ProjectSpec/Settings.swift +++ b/Sources/ProjectSpec/Settings.swift @@ -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 + } +} diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index 1abc8f42..fc55641e 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -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 } } diff --git a/Sources/ProjectSpec/Target.swift b/Sources/ProjectSpec/Target.swift index b7eb1c00..403c5ed9 100644 --- a/Sources/ProjectSpec/Target.swift +++ b/Sources/ProjectSpec/Target.swift @@ -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 + } +} diff --git a/Sources/ProjectSpec/TargetScheme.swift b/Sources/ProjectSpec/TargetScheme.swift index 22b018e9..e059f6c5 100644 --- a/Sources/ProjectSpec/TargetScheme.swift +++ b/Sources/ProjectSpec/TargetScheme.swift @@ -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 + } +} diff --git a/Sources/ProjectSpec/TargetSource.swift b/Sources/ProjectSpec/TargetSource.swift index 3611e2be..8e88bb1d 100644 --- a/Sources/ProjectSpec/TargetSource.swift +++ b/Sources/ProjectSpec/TargetSource.swift @@ -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] { diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index 39ca69b0..8f04353e 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -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" ) diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index fb1b021d..5b6c3d14 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -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) ) } diff --git a/Sources/XcodeGenKit/SourceGenerator.swift b/Sources/XcodeGenKit/SourceGenerator.swift index c6d1c50a..2b8407a8 100644 --- a/Sources/XcodeGenKit/SourceGenerator.swift +++ b/Sources/XcodeGenKit/SourceGenerator.swift @@ -224,7 +224,8 @@ class SourceGenerator { "gpx", "lproj", "xcfilelist", - "apns": + "apns", + "pch": return nil default: return .resources diff --git a/Sources/XcodeGenKit/Version.swift b/Sources/XcodeGenKit/Version.swift index 63904f67..3e122732 100644 --- a/Sources/XcodeGenKit/Version.swift +++ b/Sources/XcodeGenKit/Version.swift @@ -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 { diff --git a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj index b809ab72..6be39879 100644 --- a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj @@ -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 = ( diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj index bf38f623..6675904b 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj @@ -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 = ( diff --git a/Tests/XcodeGenKitTests/ProjectSpecTests.swift b/Tests/XcodeGenKitTests/ProjectSpecTests.swift index 9fa496c6..33aaeba6 100644 --- a/Tests/XcodeGenKitTests/ProjectSpecTests.swift +++ b/Tests/XcodeGenKitTests/ProjectSpecTests.swift @@ -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 { diff --git a/Tests/XcodeGenKitTests/SpecLoadingTests.swift b/Tests/XcodeGenKitTests/SpecLoadingTests.swift index 82fc6c47..10357158 100644 --- a/Tests/XcodeGenKitTests/SpecLoadingTests.swift +++ b/Tests/XcodeGenKitTests/SpecLoadingTests.swift @@ -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()"] ), ] )