Skip to content

Commit

Permalink
[6.0] Build one test product for both Swift Testing and XCTest (with …
Browse files Browse the repository at this point in the history
…Swift Testing enabled by default.) (#7794)

**Explanation:** Refactors the previously-experimental Swift Testing
support logic so that only a single build product is produced when using
both XCTest and Swift Testing, and detection of Swift Testing usage is
no longer needed at compile time.
**Scope:** Implementation of Swift Testing/SwiftPM integration; touches
mostly `swift test` but affects `swift build` and `swift package init`
as well.
**Issue:** rdar://120864035
**Original PR:**
#7777,
#7789,
#7796
**Risk:** Moderate—refactors how we build and run tests.
**Testing:** Existing test coverage in the package as well as test
coverage in the Swift Testing package; additional integration testing
will be added once Swift Testing is in the toolchain.
**Reviewer:** @bnbarham @xedin @briancroom @dennisweissmann

---------

Co-authored-by: Pavel Yaskevich <xedin@apache.org>
  • Loading branch information
grynspan and xedin authored Jul 19, 2024
1 parent b46e02a commit 547f649
Show file tree
Hide file tree
Showing 21 changed files with 580 additions and 579 deletions.
8 changes: 2 additions & 6 deletions Sources/Build/BuildManifest/LLBuildManifestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ public class LLBuildManifestBuilder {
}
}

if self.plan.destinationBuildParameters.testingParameters.library == .xctest {
try self.addTestDiscoveryGenerationCommand()
}
try self.addTestDiscoveryGenerationCommand()
try self.addTestEntryPointGenerationCommand()

// Create command for all products in the plan.
Expand Down Expand Up @@ -310,9 +308,7 @@ extension LLBuildManifestBuilder {

let outputs = testEntryPointTarget.target.sources.paths

let mainFileName = TestEntryPointTool.mainFileName(
for: self.plan.destinationBuildParameters.testingParameters.library
)
let mainFileName = TestEntryPointTool.mainFileName
guard let mainOutput = (outputs.first { $0.basename == mainFileName }) else {
throw InternalError("main output (\(mainFileName)) not found")
}
Expand Down
6 changes: 2 additions & 4 deletions Sources/Build/BuildPlan/BuildPlan+Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,8 @@ extension BuildPlan {
}

// Add derived test targets, if necessary
if buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets {
if product.type == .test, let derivedTestTargets = derivedTestTargetsMap[product.id] {
staticTargets.append(contentsOf: derivedTestTargets)
}
if product.type == .test, let derivedTestTargets = derivedTestTargetsMap[product.id] {
staticTargets.append(contentsOf: derivedTestTargets)
}

return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, providedLibraries, availableTools)
Expand Down
26 changes: 12 additions & 14 deletions Sources/Build/BuildPlan/BuildPlan+Test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ extension BuildPlan {
_ fileSystem: FileSystem,
_ observabilityScope: ObservabilityScope
) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftModuleBuildDescription?, entryPointTargetBuildDescription: SwiftModuleBuildDescription)] {
guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets,
case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) =
destinationBuildParameters.testingParameters.testProductStyle
else {
throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets")
var explicitlyEnabledDiscovery = false
var explicitlySpecifiedPath: AbsolutePath?
if case let .entryPointExecutable(caseExplicitlyEnabledDiscovery, caseExplicitlySpecifiedPath) = destinationBuildParameters.testingParameters.testProductStyle {
explicitlyEnabledDiscovery = caseExplicitlyEnabledDiscovery
explicitlySpecifiedPath = caseExplicitlySpecifiedPath
}

let isEntryPointPathSpecifiedExplicitly = explicitlySpecifiedPath != nil

var isDiscoveryEnabledRedundantly = explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly
Expand Down Expand Up @@ -116,7 +115,7 @@ extension BuildPlan {
resolvedTargetDependencies: [ResolvedModule.Dependency]
) throws -> SwiftModuleBuildDescription {
let entryPointDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(testProduct.name).derived")
let entryPointMainFileName = TestEntryPointTool.mainFileName(for: destinationBuildParameters.testingParameters.library)
let entryPointMainFileName = TestEntryPointTool.mainFileName
let entryPointMainFile = entryPointDerivedDir.appending(component: entryPointMainFileName)
let entryPointSources = Sources(paths: [entryPointMainFile], root: entryPointDerivedDir)

Expand Down Expand Up @@ -153,18 +152,17 @@ extension BuildPlan {
let swiftTargetDependencies: [Module.Dependency]
let resolvedTargetDependencies: [ResolvedModule.Dependency]

switch destinationBuildParameters.testingParameters.library {
case .xctest:
if destinationBuildParameters.triple.isDarwin() {
discoveryTargets = nil
swiftTargetDependencies = []
resolvedTargetDependencies = []
} else {
discoveryTargets = try generateDiscoveryTargets()
swiftTargetDependencies = [.module(discoveryTargets!.target, conditions: [])]
resolvedTargetDependencies = [.module(discoveryTargets!.resolved, conditions: [])]
case .swiftTesting:
discoveryTargets = nil
swiftTargetDependencies = testProduct.modules.map { .module($0.underlying, conditions: []) }
resolvedTargetDependencies = testProduct.modules.map { .module($0, conditions: []) }
}

if let entryPointResolvedTarget = testProduct.testEntryPointModule {
if !destinationBuildParameters.triple.isDarwin(), let entryPointResolvedTarget = testProduct.testEntryPointModule {
if isEntryPointPathSpecifiedExplicitly || explicitlyEnabledDiscovery {
if isEntryPointPathSpecifiedExplicitly {
// Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery modules.
Expand Down
42 changes: 20 additions & 22 deletions Sources/Build/BuildPlan/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -443,31 +443,29 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
}

// Plan the derived test targets, if necessary.
if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets {
let derivedTestTargets = try Self.makeDerivedTestTargets(
testProducts: productMap.values.filter {
$0.product.type == .test
},
destinationBuildParameters: destinationBuildParameters,
toolsBuildParameters: toolsBuildParameters,
shouldDisableSandbox: self.shouldDisableSandbox,
self.fileSystem,
self.observabilityScope
)
for item in derivedTestTargets {
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]

targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
item.entryPointTargetBuildDescription
)
let derivedTestTargets = try Self.makeDerivedTestTargets(
testProducts: productMap.values.filter {
$0.product.type == .test
},
destinationBuildParameters: destinationBuildParameters,
toolsBuildParameters: toolsBuildParameters,
shouldDisableSandbox: self.shouldDisableSandbox,
self.fileSystem,
self.observabilityScope
)
for item in derivedTestTargets {
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]

if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
derivedTestTargets.append(discoveryTargetBuildDescription.target)
}
targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
item.entryPointTargetBuildDescription
)

self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
derivedTestTargets.append(discoveryTargetBuildDescription.target)
}

self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
}

self.productMap = productMap.mapValues(\.buildDescription)
Expand Down
Loading

0 comments on commit 547f649

Please sign in to comment.