Skip to content

Commit fe28c77

Browse files
authored
Add swift build --enable-code-coverage (#7518)
This PR adds the `--enable-code-coverage` flag (from `swift test`) to `swift build` so that code coverage can be used in a two-stage build process (build, then execute later.) Resolves rdar://127309781.
1 parent 656563f commit fe28c77

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

Sources/Commands/SwiftBuildCommand.swift

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ struct BuildCommandOptions: ParsableArguments {
7373
@Flag(help: "Build both source and test targets")
7474
var buildTests: Bool = false
7575

76+
/// Whether to enable code coverage.
77+
@Flag(name: .customLong("code-coverage"),
78+
inversion: .prefixedEnableDisable,
79+
help: "Enable code coverage")
80+
var enableCodeCoverage: Bool = false
81+
7682
/// If the binary output path should be printed.
7783
@Flag(name: .customLong("show-bin-path"), help: "Print the binary output path")
7884
var shouldPrintBinPath: Bool = false
@@ -148,6 +154,18 @@ package struct SwiftBuildCommand: AsyncSwiftCommand {
148154
guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else {
149155
throw ExitCode.failure
150156
}
157+
158+
var productsBuildParameters = try swiftCommandState.productsBuildParameters
159+
var toolsBuildParameters = try swiftCommandState.toolsBuildParameters
160+
161+
// Clean out the code coverage directory that may contain stale
162+
// profraw files from a previous run of the code coverage tool.
163+
if self.options.enableCodeCoverage {
164+
try swiftCommandState.fileSystem.removeFileTree(swiftCommandState.productsBuildParameters.codeCovPath)
165+
productsBuildParameters.testingParameters.enableCodeCoverage = true
166+
toolsBuildParameters.testingParameters.enableCodeCoverage = true
167+
}
168+
151169
if case .allIncludingTests = subset {
152170
func updateTestingParameters(of buildParameters: inout BuildParameters, library: BuildParameters.Testing.Library) {
153171
buildParameters.testingParameters = .init(
@@ -161,23 +179,21 @@ package struct SwiftBuildCommand: AsyncSwiftCommand {
161179
library: library
162180
)
163181
}
164-
var productsBuildParameters = try swiftCommandState.productsBuildParameters
165-
var toolsBuildParameters = try swiftCommandState.toolsBuildParameters
166182
for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) {
167183
updateTestingParameters(of: &productsBuildParameters, library: library)
168184
updateTestingParameters(of: &toolsBuildParameters, library: library)
169185
try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters)
170186
}
171187
} else {
172-
try build(swiftCommandState, subset: subset, productsBuildParameters: nil, toolsBuildParameters: nil)
188+
try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters)
173189
}
174190
}
175191

176192
private func build(
177193
_ swiftCommandState: SwiftCommandState,
178194
subset: BuildSubset,
179-
productsBuildParameters: BuildParameters?,
180-
toolsBuildParameters: BuildParameters?
195+
productsBuildParameters: BuildParameters,
196+
toolsBuildParameters: BuildParameters
181197
) throws {
182198
let buildSystem = try swiftCommandState.createBuildSystem(
183199
explicitProduct: options.product,

Tests/CommandsTests/BuildCommandTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,4 +664,21 @@ final class BuildCommandTests: CommandsTestCase {
664664
}
665665
}
666666
#endif
667+
668+
func testCodeCoverage() throws {
669+
// Test that no codecov directory is created if not specified when building.
670+
try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in
671+
let buildResult = try self.build(["--build-tests"], packagePath: path, cleanAfterward: false)
672+
XCTAssertThrowsError(try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path))
673+
}
674+
675+
// Test that enabling code coverage during building produces the expected folder.
676+
try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in
677+
let buildResult = try self.build(["--build-tests", "--enable-code-coverage"], packagePath: path, cleanAfterward: false)
678+
try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path)
679+
let codeCovPath = buildResult.binPath.appending("codecov")
680+
let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath)
681+
XCTAssertGreaterThan(codeCovFiles.count, 0)
682+
}
683+
}
667684
}

0 commit comments

Comments
 (0)