Skip to content

Commit 4e8cd3e

Browse files
committed
Reduce XCTest minimum deployment target computation
We never need this for any platform that we're not building for and we also don't really need it for most commands. So we can just move the computation to SwiftTool and leave these empty for all other cases. rdar://64596106 (cherry picked from commit 019a6fb)
1 parent 71633ac commit 4e8cd3e

File tree

11 files changed

+134
-146
lines changed

11 files changed

+134
-146
lines changed

Sources/Build/BuildDescription/ProductBuildDescription.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,16 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
274274

275275
// When deploying to macOS prior to macOS 12, add an rpath to the
276276
// back-deployed concurrency libraries.
277-
if useStdlibRpath, self.buildParameters.triple.isDarwin(),
278-
let macOSSupportedPlatform = self.package.platforms.getDerived(for: .macOS),
279-
macOSSupportedPlatform.version.major < 12
280-
{
281-
let backDeployedStdlib = try buildParameters.toolchain.macosSwiftStdlib
282-
.parentDirectory
283-
.parentDirectory
284-
.appending("swift-5.5")
285-
.appending("macosx")
286-
args += ["-Xlinker", "-rpath", "-Xlinker", backDeployedStdlib.pathString]
277+
if useStdlibRpath, self.buildParameters.triple.isDarwin() {
278+
let macOSSupportedPlatform = self.package.platforms.getDerived(for: .macOS, usingXCTest: product.isLinkingXCTest)
279+
if macOSSupportedPlatform.version.major < 12 {
280+
let backDeployedStdlib = try buildParameters.toolchain.macosSwiftStdlib
281+
.parentDirectory
282+
.parentDirectory
283+
.appending("swift-5.5")
284+
.appending("macosx")
285+
args += ["-Xlinker", "-rpath", "-Xlinker", backDeployedStdlib.pathString]
286+
}
287287
}
288288
}
289289

Sources/Build/BuildPlan.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,7 @@ extension BuildParameters {
140140
var args = ["-target"]
141141
// Compute the triple string for Darwin platform using the platform version.
142142
if triple.isDarwin() {
143-
guard let macOSSupportedPlatform = target.platforms.getDerived(for: .macOS) else {
144-
throw StringError("the target \(target) doesn't support building for macOS")
145-
}
143+
let macOSSupportedPlatform = target.platforms.getDerived(for: .macOS, usingXCTest: target.type == .test)
146144
args += [triple.tripleString(forPlatformVersion: macOSSupportedPlatform.version.versionString)]
147145
} else {
148146
args += [triple.tripleString]
@@ -305,7 +303,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
305303
target: discoveryTarget,
306304
dependencies: testProduct.targets.map { .target($0, conditions: []) },
307305
defaultLocalization: .none, // safe since this is a derived target
308-
platforms: .init(declared: [], derived: []) // safe since this is a derived target
306+
platforms: .init(declared: [], derivedXCTestPlatformProvider: .none) // safe since this is a derived target
309307
)
310308
let discoveryTargetBuildDescription = try SwiftTargetBuildDescription(
311309
package: package,
@@ -338,7 +336,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
338336
target: entryPointTarget,
339337
dependencies: testProduct.targets.map { .target($0, conditions: []) } + [.target(discoveryResolvedTarget, conditions: [])],
340338
defaultLocalization: .none, // safe since this is a derived target
341-
platforms: .init(declared: [], derived: []) // safe since this is a derived target
339+
platforms: .init(declared: [], derivedXCTestPlatformProvider: .none) // safe since this is a derived target
342340
)
343341
return try SwiftTargetBuildDescription(
344342
package: package,
@@ -367,7 +365,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
367365
target: entryPointTarget,
368366
dependencies: entryPointResolvedTarget.dependencies + [.target(discoveryTargets.resolved, conditions: [])],
369367
defaultLocalization: .none, // safe since this is a derived target
370-
platforms: .init(declared: [], derived: []) // safe since this is a derived target
368+
platforms: .init(declared: [], derivedXCTestPlatformProvider: .none) // safe since this is a derived target
371369
)
372370
let entryPointTargetBuildDescription = try SwiftTargetBuildDescription(
373371
package: package,
@@ -574,12 +572,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
574572
) throws {
575573
// Supported platforms are defined at the package level.
576574
// This will need to become a bit complicated once we have target-level or product-level platform support.
577-
guard let productPlatform = product.platforms.getDerived(for: .macOS) else {
578-
throw StringError("Expected supported platform macOS in product \(product)")
579-
}
580-
guard let targetPlatform = target.platforms.getDerived(for: .macOS) else {
581-
throw StringError("Expected supported platform macOS in target \(target)")
582-
}
575+
let productPlatform = product.platforms.getDerived(for: .macOS, usingXCTest: product.isLinkingXCTest)
576+
let targetPlatform = target.platforms.getDerived(for: .macOS, usingXCTest: product.isLinkingXCTest)
583577

584578
// Check if the version requirement is satisfied.
585579
//

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,13 @@ extension PackageGraph {
144144
rootManifests: root.manifests,
145145
unsafeAllowedPackages: unsafeAllowedPackages,
146146
platformRegistry: customPlatformsRegistry ?? .default,
147-
xcTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets ?? MinimumDeploymentTarget.default.xcTestMinimumDeploymentTargets,
147+
derivedXCTestPlatformProvider: { declared in
148+
if let customXCTestMinimumDeploymentTargets {
149+
return customXCTestMinimumDeploymentTargets[declared]
150+
} else {
151+
return MinimumDeploymentTarget.default.computeXCTestMinimumDeploymentTarget(for: declared)
152+
}
153+
},
148154
fileSystem: fileSystem,
149155
observabilityScope: observabilityScope
150156
)
@@ -225,7 +231,7 @@ private func createResolvedPackages(
225231
rootManifests: [PackageIdentity: Manifest],
226232
unsafeAllowedPackages: Set<PackageReference>,
227233
platformRegistry: PlatformRegistry,
228-
xcTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion],
234+
derivedXCTestPlatformProvider: @escaping (_ declared: PackageModel.Platform) -> PlatformVersion?,
229235
fileSystem: FileSystem,
230236
observabilityScope: ObservabilityScope
231237
) throws -> [ResolvedPackage] {
@@ -348,16 +354,8 @@ private func createResolvedPackages(
348354

349355
packageBuilder.platforms = computePlatforms(
350356
package: package,
351-
usingXCTest: false,
352-
platformRegistry: platformRegistry,
353-
xcTestMinimumDeploymentTargets: xcTestMinimumDeploymentTargets
354-
)
355-
356-
let testPlatforms = computePlatforms(
357-
package: package,
358-
usingXCTest: true,
359357
platformRegistry: platformRegistry,
360-
xcTestMinimumDeploymentTargets: xcTestMinimumDeploymentTargets
358+
derivedXCTestPlatformProvider: derivedXCTestPlatformProvider
361359
)
362360

363361
// Create target builders for each target in the package.
@@ -379,7 +377,7 @@ private func createResolvedPackages(
379377
}
380378
}
381379
targetBuilder.defaultLocalization = packageBuilder.defaultLocalization
382-
targetBuilder.platforms = targetBuilder.target.type == .test ? testPlatforms : packageBuilder.platforms
380+
targetBuilder.platforms = packageBuilder.platforms
383381
}
384382

385383
// Create product builders for each product in the package. A product can only contain a target present in the same package.
@@ -686,9 +684,8 @@ private class DuplicateProductsChecker {
686684

687685
private func computePlatforms(
688686
package: Package,
689-
usingXCTest: Bool,
690687
platformRegistry: PlatformRegistry,
691-
xcTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]
688+
derivedXCTestPlatformProvider: @escaping (_ declared: PackageModel.Platform) -> PlatformVersion?
692689
) -> SupportedPlatforms {
693690

694691
// the supported platforms as declared in the manifest
@@ -702,67 +699,9 @@ private func computePlatforms(
702699
)
703700
}
704701

705-
// the derived platforms based on known minimum deployment target logic
706-
var derivedPlatforms = [SupportedPlatform]()
707-
708-
/// Add each declared platform to the supported platforms list.
709-
for platform in package.manifest.platforms {
710-
let declaredPlatform = platformRegistry.platformByName[platform.platformName]
711-
?? PackageModel.Platform.custom(name: platform.platformName, oldestSupportedVersion: platform.version)
712-
var version = PlatformVersion(platform.version)
713-
714-
if usingXCTest, let xcTestMinimumDeploymentTarget = xcTestMinimumDeploymentTargets[declaredPlatform], version < xcTestMinimumDeploymentTarget {
715-
version = xcTestMinimumDeploymentTarget
716-
}
717-
718-
// If the declared version is smaller than the oldest supported one, we raise the derived version to that.
719-
if version < declaredPlatform.oldestSupportedVersion {
720-
version = declaredPlatform.oldestSupportedVersion
721-
}
722-
723-
let supportedPlatform = SupportedPlatform(
724-
platform: declaredPlatform,
725-
version: version,
726-
options: platform.options
727-
)
728-
729-
derivedPlatforms.append(supportedPlatform)
730-
}
731-
732-
// Find the undeclared platforms.
733-
let remainingPlatforms = Set(platformRegistry.platformByName.keys).subtracting(derivedPlatforms.map({ $0.platform.name }))
734-
735-
/// Start synthesizing for each undeclared platform.
736-
for platformName in remainingPlatforms.sorted() {
737-
let platform = platformRegistry.platformByName[platformName]!
738-
739-
let minimumSupportedVersion: PlatformVersion
740-
if usingXCTest, let xcTestMinimumDeploymentTarget = xcTestMinimumDeploymentTargets[platform], xcTestMinimumDeploymentTarget > platform.oldestSupportedVersion {
741-
minimumSupportedVersion = xcTestMinimumDeploymentTarget
742-
} else {
743-
minimumSupportedVersion = platform.oldestSupportedVersion
744-
}
745-
746-
let oldestSupportedVersion: PlatformVersion
747-
if platform == .macCatalyst, let iOS = derivedPlatforms.first(where: { $0.platform == .iOS }) {
748-
// If there was no deployment target specified for Mac Catalyst, fall back to the iOS deployment target.
749-
oldestSupportedVersion = max(minimumSupportedVersion, iOS.version)
750-
} else {
751-
oldestSupportedVersion = minimumSupportedVersion
752-
}
753-
754-
let supportedPlatform = SupportedPlatform(
755-
platform: platform,
756-
version: oldestSupportedVersion,
757-
options: []
758-
)
759-
760-
derivedPlatforms.append(supportedPlatform)
761-
}
762-
763702
return SupportedPlatforms(
764703
declared: declaredPlatforms.sorted(by: { $0.platform.name < $1.platform.name }),
765-
derived: derivedPlatforms.sorted(by: { $0.platform.name < $1.platform.name })
704+
derivedXCTestPlatformProvider: derivedXCTestPlatformProvider
766705
)
767706
}
768707

@@ -886,7 +825,7 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> {
886825
var defaultLocalization: String? = nil
887826

888827
/// The platforms supported by this package.
889-
var platforms: SupportedPlatforms = .init(declared: [], derived: [])
828+
var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none)
890829

891830
init(
892831
target: Target,
@@ -978,7 +917,7 @@ private final class ResolvedPackageBuilder: ResolvedBuilder<ResolvedPackage> {
978917
var defaultLocalization: String? = nil
979918

980919
/// The platforms supported by this package.
981-
var platforms: SupportedPlatforms = .init(declared: [], derived: [])
920+
var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none)
982921

983922
/// If the given package's source is a registry release, this provides additional metadata and signature information.
984923
var registryMetadata: RegistryReleaseMetadata?

Sources/PackageGraph/ResolvedProduct.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public final class ResolvedProduct {
7171
target: swiftTarget,
7272
dependencies: targets.map { .target($0, conditions: []) },
7373
defaultLocalization: .none, // safe since this is a derived product
74-
platforms: .init(declared: [], derived: []) // safe since this is a derived product
74+
platforms: .init(declared: [], derivedXCTestPlatformProvider: .none) // safe since this is a derived product
7575
)
7676
}
7777

@@ -119,16 +119,13 @@ public final class ResolvedProduct {
119119
merge(into: &partial, platforms: item.platforms.declared)
120120
}
121121

122-
let derived = targets.reduce(into: [SupportedPlatform]()) { partial, item in
123-
merge(into: &partial, platforms: item.platforms.derived)
124-
}
125-
126122
return SupportedPlatforms(
127-
declared: declared.sorted(by: { $0.platform.name < $1.platform.name }),
128-
derived: derived.sorted(by: { $0.platform.name < $1.platform.name })
129-
)
130-
131-
123+
declared: declared.sorted(by: { $0.platform.name < $1.platform.name })) { declared in
124+
let platforms = targets.reduce(into: [SupportedPlatform]()) { partial, item in
125+
merge(into: &partial, platforms: [item.platforms.getDerived(for: declared, usingXCTest: item.type == .test)])
126+
}
127+
return platforms.first!.version
128+
}
132129
}
133130
}
134131

@@ -147,3 +144,10 @@ extension ResolvedProduct: CustomStringConvertible {
147144
return "<ResolvedProduct: \(name)>"
148145
}
149146
}
147+
148+
extension ResolvedProduct {
149+
public var isLinkingXCTest: Bool {
150+
// To retain existing behavior, we have to check both the product type, as well as the types of all of its targets.
151+
return self.type == .test || self.targets.contains(where: { $0.type == .test })
152+
}
153+
}

Sources/PackageModel/MinimumDeploymentTarget.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import Basics
1314
import TSCBasic
1415

1516
public struct MinimumDeploymentTarget {
16-
public let xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion]
17+
public let xcTestMinimumDeploymentTargets = ThreadSafeKeyValueStore<PackageModel.Platform,PlatformVersion>()
1718

1819
public static let `default`: MinimumDeploymentTarget = .init()
1920

20-
public init() {
21-
xcTestMinimumDeploymentTargets = PlatformRegistry.default.knownPlatforms.reduce([PackageModel.Platform:PlatformVersion]()) {
22-
var dict = $0
23-
dict[$1] = Self.computeXCTestMinimumDeploymentTarget(for: $1)
24-
return dict
21+
private init() {
22+
}
23+
24+
public func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
25+
self.xcTestMinimumDeploymentTargets.memoize(platform) {
26+
return Self.computeXCTestMinimumDeploymentTarget(for: platform)
2527
}
2628
}
2729

Sources/PackageModel/Platform.swift

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,56 @@ public struct Platform: Equatable, Hashable, Codable {
5050

5151
public struct SupportedPlatforms {
5252
public let declared: [SupportedPlatform]
53-
public let derived: [SupportedPlatform]
53+
private let derivedXCTestPlatformProvider: ((Platform) -> PlatformVersion?)?
5454

55-
public init(declared: [SupportedPlatform], derived: [SupportedPlatform]) {
55+
public init(declared: [SupportedPlatform], derivedXCTestPlatformProvider: ((_ declared: Platform) -> PlatformVersion?)?) {
5656
self.declared = declared
57-
self.derived = derived
57+
self.derivedXCTestPlatformProvider = derivedXCTestPlatformProvider
5858
}
5959

6060
/// Returns the supported platform instance for the given platform.
61-
public func getDerived(for platform: Platform) -> SupportedPlatform? {
62-
return self.derived.first(where: { $0.platform == platform })
61+
public func getDerived(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform {
62+
// derived platform based on known minimum deployment target logic
63+
if let declaredPlatform = self.declared.first(where: { $0.platform == platform }) {
64+
var version = declaredPlatform.version
65+
66+
if usingXCTest, let xcTestMinimumDeploymentTarget = derivedXCTestPlatformProvider?(platform), version < xcTestMinimumDeploymentTarget {
67+
version = xcTestMinimumDeploymentTarget
68+
}
69+
70+
// If the declared version is smaller than the oldest supported one, we raise the derived version to that.
71+
if version < platform.oldestSupportedVersion {
72+
version = platform.oldestSupportedVersion
73+
}
74+
75+
return SupportedPlatform(
76+
platform: declaredPlatform.platform,
77+
version: version,
78+
options: declaredPlatform.options
79+
)
80+
} else {
81+
let minimumSupportedVersion: PlatformVersion
82+
if usingXCTest, let xcTestMinimumDeploymentTarget = derivedXCTestPlatformProvider?(platform), xcTestMinimumDeploymentTarget > platform.oldestSupportedVersion {
83+
minimumSupportedVersion = xcTestMinimumDeploymentTarget
84+
} else {
85+
minimumSupportedVersion = platform.oldestSupportedVersion
86+
}
87+
88+
let oldestSupportedVersion: PlatformVersion
89+
if platform == .macCatalyst {
90+
let iOS = getDerived(for: .iOS, usingXCTest: usingXCTest)
91+
// If there was no deployment target specified for Mac Catalyst, fall back to the iOS deployment target.
92+
oldestSupportedVersion = max(minimumSupportedVersion, iOS.version)
93+
} else {
94+
oldestSupportedVersion = minimumSupportedVersion
95+
}
96+
97+
return SupportedPlatform(
98+
platform: platform,
99+
version: oldestSupportedVersion,
100+
options: []
101+
)
102+
}
63103
}
64104
}
65105

0 commit comments

Comments
 (0)