Skip to content

Commit 844bd13

Browse files
grynspanxedin
andauthored
Build one test product for both Swift Testing and XCTest (with Swift Testing enabled by default.) (#7766)
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. Resolves rdar://120864035. --------- Co-authored-by: Pavel Yaskevich <[email protected]>
1 parent 21535dc commit 844bd13

21 files changed

+471
-575
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

+2-6
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

+2-4
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

+12-14
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

+20-22
Original file line numberDiff line numberDiff line change
@@ -473,31 +473,29 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
473473
}
474474

475475
// Plan the derived test targets, if necessary.
476-
if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets {
477-
let derivedTestTargets = try Self.makeDerivedTestTargets(
478-
testProducts: productMap.values.filter {
479-
$0.product.type == .test
480-
},
481-
destinationBuildParameters: destinationBuildParameters,
482-
toolsBuildParameters: toolsBuildParameters,
483-
shouldDisableSandbox: self.shouldDisableSandbox,
484-
self.fileSystem,
485-
self.observabilityScope
486-
)
487-
for item in derivedTestTargets {
488-
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]
489-
490-
targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
491-
item.entryPointTargetBuildDescription
492-
)
476+
let derivedTestTargets = try Self.makeDerivedTestTargets(
477+
testProducts: productMap.values.filter {
478+
$0.product.type == .test
479+
},
480+
destinationBuildParameters: destinationBuildParameters,
481+
toolsBuildParameters: toolsBuildParameters,
482+
shouldDisableSandbox: self.shouldDisableSandbox,
483+
self.fileSystem,
484+
self.observabilityScope
485+
)
486+
for item in derivedTestTargets {
487+
var derivedTestTargets = [item.entryPointTargetBuildDescription.target]
493488

494-
if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
495-
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
496-
derivedTestTargets.append(discoveryTargetBuildDescription.target)
497-
}
489+
targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(
490+
item.entryPointTargetBuildDescription
491+
)
498492

499-
self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
493+
if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription {
494+
targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription)
495+
derivedTestTargets.append(discoveryTargetBuildDescription.target)
500496
}
497+
498+
self.derivedTestTargetsMap[item.product.id] = derivedTestTargets
501499
}
502500

503501
self.buildToolPluginInvocationResults = buildToolPluginInvocationResults

0 commit comments

Comments
 (0)