Skip to content

Commit d04986e

Browse files
committed
Permit scheme build stage to be optional
1 parent 3327c44 commit d04986e

File tree

5 files changed

+64
-29
lines changed

5 files changed

+64
-29
lines changed

Sources/ProjectSpec/Scheme.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public typealias BuildType = XCScheme.BuildAction.Entry.BuildFor
88
public struct Scheme: Equatable {
99

1010
public var name: String
11-
public var build: Build
11+
public var build: Build!
1212
public var run: Run?
1313
public var archive: Archive?
1414
public var analyze: Analyze?
@@ -17,7 +17,7 @@ public struct Scheme: Equatable {
1717

1818
public init(
1919
name: String,
20-
build: Build,
20+
build: Build? = nil,
2121
run: Run? = nil,
2222
test: Test? = nil,
2323
profile: Profile? = nil,
@@ -712,7 +712,7 @@ extension Scheme: NamedJSONDictionaryConvertible {
712712

713713
public init(name: String, jsonDictionary: JSONDictionary) throws {
714714
self.name = name
715-
build = try jsonDictionary.json(atKeyPath: "build")
715+
build = jsonDictionary.json(atKeyPath: "build")
716716
run = jsonDictionary.json(atKeyPath: "run")
717717
test = jsonDictionary.json(atKeyPath: "test")
718718
analyze = jsonDictionary.json(atKeyPath: "analyze")
@@ -724,7 +724,7 @@ extension Scheme: NamedJSONDictionaryConvertible {
724724
extension Scheme: JSONEncodable {
725725
public func toJSONValue() -> Any {
726726
[
727-
"build": build.toJSONValue(),
727+
"build": build?.toJSONValue(),
728728
"run": run?.toJSONValue(),
729729
"test": test?.toJSONValue(),
730730
"analyze": analyze?.toJSONValue(),

Sources/ProjectSpec/SpecValidation.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,11 @@ extension Project {
182182
}
183183

184184
for scheme in schemes {
185-
errors.append(
186-
contentsOf: scheme.build.targets.compactMap { validationError(for: $0.target, in: scheme, action: "build") }
187-
)
185+
if let action = scheme.build {
186+
errors.append(contentsOf: action.targets.compactMap {
187+
validationError(for: $0.target, in: scheme, action: "build")
188+
})
189+
}
188190
if let action = scheme.run, let config = action.config, getConfig(config) == nil {
189191
errors.append(.invalidSchemeConfig(scheme: scheme.name, config: config))
190192
}

Sources/XcodeGenKit/SchemeGenerator.swift

+34-22
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public class SchemeGenerator {
167167

168168
let testBuildTargetEntries = try testBuildTargets.map(getBuildEntry)
169169

170-
let buildActionEntries: [XCScheme.BuildAction.Entry] = try scheme.build.targets.map(getBuildEntry)
170+
let buildActionEntries: [XCScheme.BuildAction.Entry] = try scheme.build?.targets.map(getBuildEntry) ?? []
171171

172172
func getExecutionAction(_ action: Scheme.ExecutionAction) -> XCScheme.ExecutionAction {
173173
// ExecutionActions can require the use of build settings. Xcode allows the settings to come from a build or test target.
@@ -183,27 +183,42 @@ public class SchemeGenerator {
183183

184184
if let targetName = scheme.run?.executable {
185185
schemeTarget = project.getTarget(targetName)
186-
} else {
187-
guard let firstTarget = scheme.build.targets.first else {
188-
throw SchemeGenerationError.missingBuildTargets(scheme.name)
189-
}
190-
let name = scheme.build.targets.first { $0.buildTypes.contains(.running) }?.target.name ?? firstTarget.target.name
186+
} else if
187+
let targets = scheme.build?.targets,
188+
let firstTarget = targets.first
189+
{
190+
let name = targets.first { $0.buildTypes.contains(.running) }?.target.name ?? firstTarget.target.name
191191
schemeTarget = target ?? project.getTarget(name)
192+
} else {
193+
schemeTarget = nil
192194
}
193195

194196
let shouldExecuteOnLaunch = schemeTarget?.shouldExecuteOnLaunch == true
197+
let buildableReference: XCScheme.BuildableReference?
198+
let buildAction: XCScheme.BuildAction?
199+
let runnables: (launch: XCScheme.Runnable, profile: XCScheme.BuildableProductRunnable)?
200+
201+
if let buildableReferenceCandidate = buildActionEntries.first(where: { $0.buildableReference.blueprintName == schemeTarget?.name })?.buildableReference ?? buildActionEntries.first?.buildableReference
202+
{
203+
runnables = makeProductRunnables(for: schemeTarget, buildableReference: buildableReferenceCandidate)
204+
205+
buildableReference = buildableReferenceCandidate
206+
buildAction = scheme.build.flatMap { build in
207+
XCScheme.BuildAction(
208+
buildActionEntries: buildActionEntries,
209+
preActions: build.preActions.map(getExecutionAction),
210+
postActions: build.postActions.map(getExecutionAction),
211+
parallelizeBuild: build.parallelizeBuild,
212+
buildImplicitDependencies: build.buildImplicitDependencies,
213+
runPostActionsOnFailure: build.runPostActionsOnFailure
214+
)
215+
}
216+
} else {
217+
buildableReference = nil
218+
buildAction = nil
219+
runnables = nil
220+
}
195221

196-
let buildableReference = buildActionEntries.first(where: { $0.buildableReference.blueprintName == schemeTarget?.name })?.buildableReference ?? buildActionEntries.first!.buildableReference
197-
let runnables = makeProductRunnables(for: schemeTarget, buildableReference: buildableReference)
198-
199-
let buildAction = XCScheme.BuildAction(
200-
buildActionEntries: buildActionEntries,
201-
preActions: scheme.build.preActions.map(getExecutionAction),
202-
postActions: scheme.build.postActions.map(getExecutionAction),
203-
parallelizeBuild: scheme.build.parallelizeBuild,
204-
buildImplicitDependencies: scheme.build.buildImplicitDependencies,
205-
runPostActionsOnFailure: scheme.build.runPostActionsOnFailure
206-
)
207222

208223
let testables: [XCScheme.TestableReference] = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuildEntries in
209224

@@ -298,7 +313,7 @@ public class SchemeGenerator {
298313
}
299314

300315
let launchAction = XCScheme.LaunchAction(
301-
runnable: shouldExecuteOnLaunch ? runnables.launch : nil,
316+
runnable: shouldExecuteOnLaunch ? runnables?.launch : nil,
302317
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
303318
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
304319
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
@@ -321,7 +336,7 @@ public class SchemeGenerator {
321336
)
322337

323338
let profileAction = XCScheme.ProfileAction(
324-
buildableProductRunnable: shouldExecuteOnLaunch ? runnables.profile : nil,
339+
buildableProductRunnable: shouldExecuteOnLaunch ? runnables?.profile : nil,
325340
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
326341
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
327342
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
@@ -403,16 +418,13 @@ enum SchemeGenerationError: Error, CustomStringConvertible {
403418
case missingTarget(TargetReference, projectPath: String)
404419
case missingPackage(String)
405420
case missingProject(String)
406-
case missingBuildTargets(String)
407421

408422
var description: String {
409423
switch self {
410424
case .missingTarget(let target, let projectPath):
411425
return "Unable to find target named \"\(target)\" in \"\(projectPath)\""
412426
case .missingProject(let project):
413427
return "Unable to find project reference named \"\(project)\" in project.yml"
414-
case .missingBuildTargets(let name):
415-
return "Unable to find at least one build target in scheme \"\(name)\""
416428
case .missingPackage(let package):
417429
return "Unable to find swift package named \"\(package)\" in project.yml"
418430
}

Tests/Fixtures/test_only.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: TestOnly
2+
targets:
3+
Target:
4+
type: application
5+
platform: iOS
6+
sources:
7+
- Target
8+
schemes:
9+
TestOnly:
10+
test:
11+
targets:
12+
- Target

Tests/ProjectSpecTests/SpecLoadingTests.swift

+9
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ class SpecLoadingTests: XCTestCase {
7575
]
7676
}
7777

78+
$0.it("accepts schemes that have no build phase") {
79+
let path = fixturePath + "test_only.yml"
80+
let project = try loadSpec(path: path)
81+
try expect(project.name) == "TestOnly"
82+
try expect(project.schemes.count) == 1
83+
try expect(project.schemes.first?.build).to.beNil()
84+
try expect(project.schemes.first?.test?.targets.first?.name) == "Target"
85+
}
86+
7887
$0.it("expands directories") {
7988
let path = fixturePath + "paths_test.yml"
8089
let project = try loadSpec(path: path)

0 commit comments

Comments
 (0)