Skip to content

Commit 081cb28

Browse files
grynspanxedin
andcommitted
Build one test product for both Swift Testing and XCTest (with Swift Testing enabled by default.) (#7789)
This PR 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. On macOS, Xcode 16 is responsible for hosting Swift Testing content, so additional changes may be needed in Xcode to support this refactoring. Such changes are beyond the purview of the Swift open source project. This PR includes changes from the previous PRs numbered #7766, #7783, Resolves rdar://120864035. Co-authored-by: Pavel Yaskevich <[email protected]>
1 parent 31ef59b commit 081cb28

21 files changed

+510
-582
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@ public class LLBuildManifestBuilder {
112112
}
113113
}
114114

115-
if self.plan.destinationBuildParameters.testingParameters.library == .xctest {
116-
try self.addTestDiscoveryGenerationCommand()
117-
}
115+
try self.addTestDiscoveryGenerationCommand()
118116
try self.addTestEntryPointGenerationCommand()
119117

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

311309
let outputs = testEntryPointTarget.target.sources.paths
312310

313-
let mainFileName = TestEntryPointTool.mainFileName(
314-
for: self.plan.destinationBuildParameters.testingParameters.library
315-
)
311+
let mainFileName = TestEntryPointTool.mainFileName
316312
guard let mainOutput = (outputs.first { $0.basename == mainFileName }) else {
317313
throw InternalError("main output (\(mainFileName)) not found")
318314
}

Sources/Build/BuildPlan/BuildPlan+Product.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,8 @@ extension BuildPlan {
275275
}
276276

277277
// Add derived test targets, if necessary
278-
if buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets {
279-
if product.type == .test, let derivedTestTargets = derivedTestTargetsMap[product.id] {
280-
staticTargets.append(contentsOf: derivedTestTargets)
281-
}
278+
if product.type == .test, let derivedTestTargets = derivedTestTargetsMap[product.id] {
279+
staticTargets.append(contentsOf: derivedTestTargets)
282280
}
283281

284282
return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, providedLibraries, availableTools)

Sources/Build/BuildPlan/BuildPlan+Test.swift

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ extension BuildPlan {
3434
_ fileSystem: FileSystem,
3535
_ observabilityScope: ObservabilityScope
3636
) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftModuleBuildDescription?, entryPointTargetBuildDescription: SwiftModuleBuildDescription)] {
37-
guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets,
38-
case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) =
39-
destinationBuildParameters.testingParameters.testProductStyle
40-
else {
41-
throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets")
37+
var explicitlyEnabledDiscovery = false
38+
var explicitlySpecifiedPath: AbsolutePath?
39+
if case let .entryPointExecutable(caseExplicitlyEnabledDiscovery, caseExplicitlySpecifiedPath) = destinationBuildParameters.testingParameters.testProductStyle {
40+
explicitlyEnabledDiscovery = caseExplicitlyEnabledDiscovery
41+
explicitlySpecifiedPath = caseExplicitlySpecifiedPath
4242
}
43-
4443
let isEntryPointPathSpecifiedExplicitly = explicitlySpecifiedPath != nil
4544

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

@@ -153,18 +152,17 @@ extension BuildPlan {
153152
let swiftTargetDependencies: [Module.Dependency]
154153
let resolvedTargetDependencies: [ResolvedModule.Dependency]
155154

156-
switch destinationBuildParameters.testingParameters.library {
157-
case .xctest:
155+
if destinationBuildParameters.triple.isDarwin() {
156+
discoveryTargets = nil
157+
swiftTargetDependencies = []
158+
resolvedTargetDependencies = []
159+
} else {
158160
discoveryTargets = try generateDiscoveryTargets()
159161
swiftTargetDependencies = [.module(discoveryTargets!.target, conditions: [])]
160162
resolvedTargetDependencies = [.module(discoveryTargets!.resolved, conditions: [])]
161-
case .swiftTesting:
162-
discoveryTargets = nil
163-
swiftTargetDependencies = testProduct.modules.map { .module($0.underlying, conditions: []) }
164-
resolvedTargetDependencies = testProduct.modules.map { .module($0, conditions: []) }
165163
}
166164

167-
if let entryPointResolvedTarget = testProduct.testEntryPointModule {
165+
if !destinationBuildParameters.triple.isDarwin(), let entryPointResolvedTarget = testProduct.testEntryPointModule {
168166
if isEntryPointPathSpecifiedExplicitly || explicitlyEnabledDiscovery {
169167
if isEntryPointPathSpecifiedExplicitly {
170168
// Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery modules.

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -443,31 +443,29 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
443443
}
444444

445445
// Plan the derived test targets, if necessary.
446-
if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets {
447-
let derivedTestTargets = try Self.makeDerivedTestTargets(
448-
testProducts: productMap.values.filter {
449-
$0.product.type == .test
450-
},
451-
destinationBuildParameters: destinationBuildParameters,
452-
toolsBuildParameters: toolsBuildParameters,
453-
shouldDisableSandbox: self.shouldDisableSandbox,
454-
self.fileSystem,
455-
self.observabilityScope
456-
)
457-
for item in derivedTestTargets {
458-
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]
459-
460-
targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
461-
item.entryPointTargetBuildDescription
462-
)
446+
let derivedTestTargets = try Self.makeDerivedTestTargets(
447+
testProducts: productMap.values.filter {
448+
$0.product.type == .test
449+
},
450+
destinationBuildParameters: destinationBuildParameters,
451+
toolsBuildParameters: toolsBuildParameters,
452+
shouldDisableSandbox: self.shouldDisableSandbox,
453+
self.fileSystem,
454+
self.observabilityScope
455+
)
456+
for item in derivedTestTargets {
457+
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]
463458

464-
if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
465-
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
466-
derivedTestTargets.append(discoveryTargetBuildDescription.target)
467-
}
459+
targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
460+
item.entryPointTargetBuildDescription
461+
)
468462

469-
self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
463+
if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
464+
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
465+
derivedTestTargets.append(discoveryTargetBuildDescription.target)
470466
}
467+
468+
self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
471469
}
472470

473471
self.productMap = productMap.mapValues(\.buildDescription)

0 commit comments

Comments
 (0)