Skip to content

Commit

Permalink
Add swift build --enable-code-coverage (#7518)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
grynspan authored Apr 30, 2024
1 parent 656563f commit fe28c77
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
26 changes: 21 additions & 5 deletions Sources/Commands/SwiftBuildCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ struct BuildCommandOptions: ParsableArguments {
@Flag(help: "Build both source and test targets")
var buildTests: Bool = false

/// Whether to enable code coverage.
@Flag(name: .customLong("code-coverage"),
inversion: .prefixedEnableDisable,
help: "Enable code coverage")
var enableCodeCoverage: Bool = false

/// If the binary output path should be printed.
@Flag(name: .customLong("show-bin-path"), help: "Print the binary output path")
var shouldPrintBinPath: Bool = false
Expand Down Expand Up @@ -148,6 +154,18 @@ package struct SwiftBuildCommand: AsyncSwiftCommand {
guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else {
throw ExitCode.failure
}

var productsBuildParameters = try swiftCommandState.productsBuildParameters
var toolsBuildParameters = try swiftCommandState.toolsBuildParameters

// Clean out the code coverage directory that may contain stale
// profraw files from a previous run of the code coverage tool.
if self.options.enableCodeCoverage {
try swiftCommandState.fileSystem.removeFileTree(swiftCommandState.productsBuildParameters.codeCovPath)
productsBuildParameters.testingParameters.enableCodeCoverage = true
toolsBuildParameters.testingParameters.enableCodeCoverage = true
}

if case .allIncludingTests = subset {
func updateTestingParameters(of buildParameters: inout BuildParameters, library: BuildParameters.Testing.Library) {
buildParameters.testingParameters = .init(
Expand All @@ -161,23 +179,21 @@ package struct SwiftBuildCommand: AsyncSwiftCommand {
library: library
)
}
var productsBuildParameters = try swiftCommandState.productsBuildParameters
var toolsBuildParameters = try swiftCommandState.toolsBuildParameters
for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) {
updateTestingParameters(of: &productsBuildParameters, library: library)
updateTestingParameters(of: &toolsBuildParameters, library: library)
try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters)
}
} else {
try build(swiftCommandState, subset: subset, productsBuildParameters: nil, toolsBuildParameters: nil)
try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters)
}
}

private func build(
_ swiftCommandState: SwiftCommandState,
subset: BuildSubset,
productsBuildParameters: BuildParameters?,
toolsBuildParameters: BuildParameters?
productsBuildParameters: BuildParameters,
toolsBuildParameters: BuildParameters
) throws {
let buildSystem = try swiftCommandState.createBuildSystem(
explicitProduct: options.product,
Expand Down
17 changes: 17 additions & 0 deletions Tests/CommandsTests/BuildCommandTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -664,4 +664,21 @@ final class BuildCommandTests: CommandsTestCase {
}
}
#endif

func testCodeCoverage() throws {
// Test that no codecov directory is created if not specified when building.
try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in
let buildResult = try self.build(["--build-tests"], packagePath: path, cleanAfterward: false)
XCTAssertThrowsError(try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path))
}

// Test that enabling code coverage during building produces the expected folder.
try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in
let buildResult = try self.build(["--build-tests", "--enable-code-coverage"], packagePath: path, cleanAfterward: false)
try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path)
let codeCovPath = buildResult.binPath.appending("codecov")
let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath)
XCTAssertGreaterThan(codeCovFiles.count, 0)
}
}
}

0 comments on commit fe28c77

Please sign in to comment.