diff --git a/CHANGELOG.md b/CHANGELOG.md index f4886713..bacd3ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Added support for Swift package `traits` on remote and local package references, so generated projects preserve the configured trait selections #1629 @philprime +- Added scheme build architecture overrides #1642 @arhxam ### Changed - Targets in the generated project now follow the declaration order from the source spec (Xcode sidebar, `xcodebuild -list` output). Previously they were always sorted alphabetically. Applies to both YAML and JSON specs. Declaration order is now also preserved for targets whose `platform`/`name` come from a target template and for targets whose key is a `${VARIABLE}`. #1619 @mirkokg diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index d8c42780..27c5e7a9 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -1019,6 +1019,7 @@ Schemes allows for more control than the convenience [Target Scheme](#target-sch - [ ] **runPostActionsOnFailure**: **Bool** - Flag to determine if Xcode should run post scripts despite failure build. By default this is `false` if not set. - `true`: Run post scripts even if build is failed - `false`: Only run post scripts if build success +- [ ] **buildArchitectures**: **String** - Overrides the architectures built for all targets in the scheme. Supported values are `matchRunDestination`, `universal`, and `useTargetSettings`. By default this is `useTargetSettings` if not set. ```yaml @@ -1027,6 +1028,7 @@ targets: FooLib/FooTarget: [test, run] parallelizeBuild: true buildImplicitDependencies: true +buildArchitectures: matchRunDestination ``` ### Common Build Action options diff --git a/Sources/ProjectSpec/Scheme.swift b/Sources/ProjectSpec/Scheme.swift index a19951fe..8120c2b7 100644 --- a/Sources/ProjectSpec/Scheme.swift +++ b/Sources/ProjectSpec/Scheme.swift @@ -4,6 +4,7 @@ import PathKit import XcodeProj public typealias BuildType = XCScheme.BuildAction.Entry.BuildFor +public typealias BuildArchitectures = XCScheme.BuildAction.Architectures public struct Scheme: Equatable { @@ -101,6 +102,7 @@ public struct Scheme: Equatable { public static let parallelizeBuildDefault = true public static let buildImplicitDependenciesDefault = true public static let runPostActionsOnFailureDefault = false + public static let buildArchitecturesDefault = BuildArchitectures.useTargetSettings public var targets: [BuildTarget] public var parallelizeBuild: Bool @@ -108,6 +110,7 @@ public struct Scheme: Equatable { public var preActions: [ExecutionAction] public var postActions: [ExecutionAction] public var runPostActionsOnFailure: Bool + public var buildArchitectures: BuildArchitectures public init( targets: [BuildTarget], @@ -115,7 +118,8 @@ public struct Scheme: Equatable { buildImplicitDependencies: Bool = buildImplicitDependenciesDefault, preActions: [ExecutionAction] = [], postActions: [ExecutionAction] = [], - runPostActionsOnFailure: Bool = false + runPostActionsOnFailure: Bool = runPostActionsOnFailureDefault, + buildArchitectures: BuildArchitectures = buildArchitecturesDefault ) { self.targets = targets self.parallelizeBuild = parallelizeBuild @@ -123,6 +127,7 @@ public struct Scheme: Equatable { self.preActions = preActions self.postActions = postActions self.runPostActionsOnFailure = runPostActionsOnFailure + self.buildArchitectures = buildArchitectures } } @@ -946,6 +951,7 @@ extension Scheme.Build: JSONObjectConvertible { parallelizeBuild = jsonDictionary.json(atKeyPath: "parallelizeBuild") ?? Scheme.Build.parallelizeBuildDefault buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? Scheme.Build.buildImplicitDependenciesDefault runPostActionsOnFailure = jsonDictionary.json(atKeyPath: "runPostActionsOnFailure") ?? Scheme.Build.runPostActionsOnFailureDefault + buildArchitectures = jsonDictionary.json(atKeyPath: "buildArchitectures") ?? Scheme.Build.buildArchitecturesDefault } } @@ -968,6 +974,9 @@ extension Scheme.Build: JSONEncodable { if runPostActionsOnFailure != Scheme.Build.runPostActionsOnFailureDefault { dict["runPostActionsOnFailure"] = runPostActionsOnFailure } + if buildArchitectures != Scheme.Build.buildArchitecturesDefault { + dict["buildArchitectures"] = buildArchitectures.toJSONValue() + } return dict } @@ -1005,6 +1014,30 @@ extension BuildType: JSONEncodable { } } +extension BuildArchitectures: JSONUtilities.JSONPrimitiveConvertible { + + public typealias JSONType = String + + public static func from(jsonValue: String) -> BuildArchitectures? { + switch jsonValue { + case "matchRunDestination": return .matchRunDestination + case "universal": return .universal + case "useTargetSettings": return .useTargetSettings + default: return nil + } + } +} + +extension BuildArchitectures: JSONEncodable { + public func toJSONValue() -> Any { + switch self { + case .matchRunDestination: return "matchRunDestination" + case .universal: return "universal" + case .useTargetSettings: return "useTargetSettings" + } + } +} + extension XCScheme.EnvironmentVariable: JSONUtilities.JSONObjectConvertible { public static let enabledDefault = true diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index bb5205c2..38a1be65 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -239,7 +239,8 @@ public class SchemeGenerator { postActions: scheme.build.postActions.map(getExecutionAction), parallelizeBuild: scheme.build.parallelizeBuild, buildImplicitDependencies: scheme.build.buildImplicitDependencies, - runPostActionsOnFailure: scheme.build.runPostActionsOnFailure + runPostActionsOnFailure: scheme.build.runPostActionsOnFailure, + buildArchitectures: scheme.build.buildArchitectures ) let testables: [XCScheme.TestableReference] = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuildEntries in diff --git a/Tests/ProjectSpecTests/SpecLoadingTests.swift b/Tests/ProjectSpecTests/SpecLoadingTests.swift index 54875e75..58421f90 100644 --- a/Tests/ProjectSpecTests/SpecLoadingTests.swift +++ b/Tests/ProjectSpecTests/SpecLoadingTests.swift @@ -1026,6 +1026,7 @@ class SpecLoadingTests: XCTestCase { "parallelizeBuild": false, "buildImplicitDependencies": false, "runPostActionsOnFailure": true, + "buildArchitectures": "matchRunDestination", "targets": [ "Target1": "all", "Target2": "testing", @@ -1100,6 +1101,7 @@ class SpecLoadingTests: XCTestCase { try expect(scheme.build.parallelizeBuild) == false try expect(scheme.build.buildImplicitDependencies) == false try expect(scheme.build.runPostActionsOnFailure) == true + try expect(scheme.build.buildArchitectures) == .matchRunDestination let expectedRun = Scheme.Run( config: "debug", @@ -1137,6 +1139,24 @@ class SpecLoadingTests: XCTestCase { try expect(scheme.management) == expectedManagement } + $0.it("encodes scheme build architecture overrides") { + let architectureOptions: [(BuildArchitectures, String)] = [ + (.matchRunDestination, "matchRunDestination"), + (.universal, "universal"), + ] + + for (option, expected) in architectureOptions { + let build = Scheme.Build(targets: [], buildArchitectures: option) + let dictionary = try unwrap(build.toJSONValue() as? [String: Any]) + + try expect(dictionary["buildArchitectures"] as? String) == expected + } + + let defaultBuild = Scheme.Build(targets: []) + let defaultDictionary = try unwrap(defaultBuild.toJSONValue() as? [String: Any]) + try expect(defaultDictionary["buildArchitectures"]).to.beNil() + } + $0.it("parses alternate test schemes") { let schemeDictionary: [String: Any] = [ "build": [ diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift index d876b3f8..2d18a3e9 100644 --- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift @@ -125,6 +125,28 @@ class SchemeGeneratorTests: XCTestCase { try expect(xcscheme.testAction?.testables[1].locationScenarioReference?.identifier) == "New York, NY, USA" } + $0.it("generates scheme build architecture overrides") { + let architectureOptions: [(String, XCScheme.BuildAction.Architectures)] = [ + ("matchRunDestination", .matchRunDestination), + ("universal", .universal), + ("useTargetSettings", .useTargetSettings), + ] + + for (option, expected) in architectureOptions { + let scheme = try Scheme(name: "MyScheme", jsonDictionary: [ + "build": [ + "targets": [app.name: "all"], + "buildArchitectures": option, + ], + ]) + let project = Project(name: "test", targets: [app, framework], schemes: [scheme]) + let xcodeProject = try project.generateXcodeProject() + let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) + + try expect(xcscheme.buildAction?.buildArchitectures) == expected + } + } + let frameworkTarget = Scheme.BuildTarget(target: .local(framework.name), buildTypes: [.archiving]) $0.it("generates a scheme with the first runnable selected") { let scheme = Scheme(