Skip to content

Commit a6cf331

Browse files
authored
Merge pull request #1189 from hartbit/build-plan-refactor
Refactor SwiftTool building to accept a buildPlan
2 parents 43cc5da + 39d6927 commit a6cf331

File tree

4 files changed

+53
-62
lines changed

4 files changed

+53
-62
lines changed

Sources/Commands/Options.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@
1010

1111
import Basic
1212
import Utility
13+
import Build
1314

1415
public class ToolOptions {
1516
/// Custom arguments to pass to C compiler, swift compiler and the linker.
1617
public var buildFlags = BuildFlags()
18+
19+
/// Build configuration.
20+
public var configuration: Build.Configuration = .debug
1721

1822
/// The custom build directory, if provided.
1923
public var buildPath: AbsolutePath?

Sources/Commands/SwiftBuildTool.swift

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ public class SwiftBuildTool: SwiftTool<BuildToolOptions> {
3333
checkClangVersion()
3434
#endif
3535

36-
let graph = try loadPackageGraph()
37-
// If we don't have any targets in root package, we're done.
38-
guard !graph.rootPackages[0].targets.isEmpty else { break }
39-
try build(graph: graph, includingTests: options.buildTests, config: options.config)
36+
try build(includingTests: options.buildTests)
4037

4138
case .version:
4239
print(Versioning.currentVersion.completeDisplayString)
@@ -48,11 +45,6 @@ public class SwiftBuildTool: SwiftTool<BuildToolOptions> {
4845
option: parser.add(option: "--build-tests", kind: Bool.self,
4946
usage: "Build the both source and test targets"),
5047
to: { $0.buildTests = $1 })
51-
52-
binder.bind(
53-
option: parser.add(option: "--configuration", shortName: "-c", kind: Build.Configuration.self,
54-
usage: "Build with configuration (debug|release) [default: debug]"),
55-
to: { $0.config = $1 })
5648
}
5749

5850
private func checkClangVersion() {
@@ -82,9 +74,6 @@ public class BuildToolOptions: ToolOptions {
8274

8375
/// If the test should be built.
8476
var buildTests = false
85-
86-
/// Build configuration.
87-
var config: Build.Configuration = .debug
8877
}
8978

9079
public enum BuildToolMode {
@@ -94,10 +83,3 @@ public enum BuildToolMode {
9483
/// Print the version.
9584
case version
9685
}
97-
98-
extension Build.Configuration: StringEnumArgument {
99-
public static var completion: ShellCompletion = .values([
100-
(debug.rawValue, "build with DEBUG configuration"),
101-
(release.rawValue, "build with RELEASE configuration"),
102-
])
103-
}

Sources/Commands/SwiftTestTool.swift

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ struct SpecifierDeprecatedDiagnostic: DiagnosticData {
3131

3232
private enum TestError: Swift.Error {
3333
case invalidListTestJSONData
34-
case multipleTestProducts
3534
case testsExecutableNotFound
3635
}
3736

@@ -42,8 +41,6 @@ extension TestError: CustomStringConvertible {
4241
return "no tests found to execute, create a target in your `Tests' directory"
4342
case .invalidListTestJSONData:
4443
return "Invalid list test JSON structure."
45-
case .multipleTestProducts:
46-
return "cannot test packages with multiple test products defined"
4744
}
4845
}
4946
}
@@ -70,9 +67,6 @@ public class TestToolOptions: ToolOptions {
7067
/// If the test target should be built before testing.
7168
var shouldBuildTests = true
7269

73-
/// Build configuration.
74-
var config: Build.Configuration = .debug
75-
7670
/// If tests should run in parallel mode.
7771
var shouldRunInParallel = false
7872

@@ -170,40 +164,25 @@ public class SwiftTestTool: SwiftTool<TestToolOptions> {
170164
///
171165
/// - Returns: The path to the test binary.
172166
private func buildTestsIfNeeded(_ options: TestToolOptions) throws -> AbsolutePath {
173-
let graph = try loadPackageGraph()
167+
let buildPlan = try self.buildPlan()
174168
if options.shouldBuildTests {
175-
try build(graph: graph, includingTests: true, config: options.config)
169+
try build(plan: buildPlan, includingTests: true)
176170
}
177171

178-
// See the logic in `PackageLoading`'s `PackageExtensions.swift`.
179-
//
180-
// FIXME: We should also check if the package has any test
181-
// targets, which isn't trivial (yet).
182-
let testProducts = graph.products.filter({
183-
if case .test = $0.type {
184-
return true
185-
} else {
186-
return false
187-
}
188-
})
189-
if testProducts.count == 0 {
172+
guard let testProduct = buildPlan.buildProducts.first(where: { $0.product.type == .test }) else {
190173
throw TestError.testsExecutableNotFound
191-
} else if testProducts.count > 1 {
192-
throw TestError.multipleTestProducts
193-
} else {
194-
return buildPath
195-
.appending(RelativePath(options.config.dirname))
196-
.appending(component: testProducts[0].name + ".xctest")
197174
}
175+
176+
// Go up the folder hierarchy until we find the .xctest bundle.
177+
return sequence(first: testProduct.binary, next: {
178+
$0.isRoot ? nil : $0.parentDirectory
179+
}).first(where: {
180+
$0.basename.hasSuffix(".xctest")
181+
})!
198182
}
199183

200184
override class func defineArguments(parser: ArgumentParser, binder: ArgumentBinder<TestToolOptions>) {
201185

202-
binder.bind(
203-
option: parser.add(option: "--configuration", shortName: "-c", kind: Build.Configuration.self,
204-
usage: "Build with configuration (debug|release) [default: debug]"),
205-
to: { $0.config = $1 })
206-
207186
binder.bind(
208187
option: parser.add(option: "--skip-build", kind: Bool.self,
209188
usage: "Skip building the test target"),

Sources/Commands/SwiftTool.swift

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ public class SwiftTool<Options: ToolOptions> {
127127
usage: "Pass flag through to all linker invocations"),
128128
to: { $0.buildFlags = BuildFlags(xcc: $1, xswiftc: $2, xlinker: $3) })
129129

130+
binder.bind(
131+
option: parser.add(
132+
option: "--configuration", shortName: "-c", kind: Build.Configuration.self,
133+
usage: "Build with configuration (debug|release) [default: debug]"),
134+
to: { $0.configuration = $1 })
135+
130136
binder.bind(
131137
option: parser.add(
132138
option: "--build-path", kind: PathArgument.self,
@@ -318,19 +324,20 @@ public class SwiftTool<Options: ToolOptions> {
318324
}
319325

320326
/// Build the package graph using swift-build-tool.
321-
func build(graph: PackageGraph, includingTests: Bool, config: Build.Configuration) throws {
322-
// Create build parameters.
323-
let buildParameters = BuildParameters(
324-
dataPath: buildPath,
325-
configuration: config,
326-
toolchain: try getToolchain(),
327-
flags: options.buildFlags
328-
)
329-
let yaml = buildPath.appending(component: config.dirname + ".yaml")
330-
// Create build plan.
331-
let buildPlan = try BuildPlan(buildParameters: buildParameters, graph: graph, delegate: self)
327+
func build(includingTests: Bool) throws {
328+
try build(plan: buildPlan(), includingTests: includingTests)
329+
}
330+
331+
/// Build the package graph using swift-build-tool.
332+
func build(plan: BuildPlan, includingTests: Bool) throws {
333+
guard !plan.graph.rootPackages[0].targets.isEmpty else {
334+
warning(message: "no targets to build in package")
335+
return
336+
}
337+
338+
let yaml = buildPath.appending(component: plan.buildParameters.configuration.dirname + ".yaml")
332339
// Generate llbuild manifest.
333-
let llbuild = LLbuildManifestGenerator(buildPlan)
340+
let llbuild = LLbuildManifestGenerator(plan)
334341
try llbuild.generateManifest(at: yaml)
335342
assert(isFile(yaml), "llbuild manifest not present: \(yaml.asString)")
336343

@@ -366,6 +373,18 @@ public class SwiftTool<Options: ToolOptions> {
366373
}
367374
}
368375

376+
/// Generates a BuildPlan based on the tool's options.
377+
func buildPlan() throws -> BuildPlan {
378+
return try BuildPlan(
379+
buildParameters: BuildParameters(
380+
dataPath: buildPath,
381+
configuration: options.configuration,
382+
toolchain: try getToolchain(),
383+
flags: options.buildFlags),
384+
graph: try loadPackageGraph(),
385+
delegate: self)
386+
}
387+
369388
/// Lazily compute the destination toolchain.
370389
private lazy var _destinationToolchain: Result<UserToolchain, AnyError> = {
371390
// Create custom toolchain if present.
@@ -456,3 +475,10 @@ private func sandboxProfile(allowedDirectories: [AbsolutePath]) -> String {
456475
stream <<< ")" <<< "\n"
457476
return stream.bytes.asString!
458477
}
478+
479+
extension Build.Configuration: StringEnumArgument {
480+
public static var completion: ShellCompletion = .values([
481+
(debug.rawValue, "build with DEBUG configuration"),
482+
(release.rawValue, "build with RELEASE configuration"),
483+
])
484+
}

0 commit comments

Comments
 (0)