diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 4d11412723d..5f1c65a667b 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -286,7 +286,8 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // When deploying to macOS prior to macOS 12, add an rpath to the // back-deployed concurrency libraries. if useStdlibRpath, triple.isMacOSX { - let macOSSupportedPlatform = self.package.platforms.getDerived(for: .macOS, usingXCTest: product.isLinkingXCTest) + let macOSSupportedPlatform = self.package.getSupportedPlatform(for: .macOS, usingXCTest: product.isLinkingXCTest) + if macOSSupportedPlatform.version.major < 12 { let backDeployedStdlib = try buildParameters.toolchain.macosSwiftStdlib .parentDirectory diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 0b5427d134f..6c4bed6a3ef 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -87,7 +87,8 @@ extension BuildPlan { target: discoveryTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) }, defaultLocalization: testProduct.defaultLocalization, - platforms: testProduct.platforms + supportedPlatforms: testProduct.supportedPlatforms, + platformVersionProvider: testProduct.platformVersionProvider ) let discoveryTargetBuildDescription = try SwiftTargetBuildDescription( package: package, @@ -124,7 +125,8 @@ extension BuildPlan { target: entryPointTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) } + resolvedTargetDependencies, defaultLocalization: testProduct.defaultLocalization, - platforms: testProduct.platforms + supportedPlatforms: testProduct.supportedPlatforms, + platformVersionProvider: testProduct.platformVersionProvider ) return try SwiftTargetBuildDescription( package: package, @@ -166,7 +168,8 @@ extension BuildPlan { target: entryPointTarget, dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, defaultLocalization: testProduct.defaultLocalization, - platforms: testProduct.platforms + supportedPlatforms: testProduct.supportedPlatforms, + platformVersionProvider: testProduct.platformVersionProvider ) let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( package: package, diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 92c888ff178..849f32f8ab5 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -134,7 +134,7 @@ extension BuildParameters { // Compute the triple string for Darwin platform using the platform version. if self.triple.isDarwin() { let platform = buildEnvironment.platform - let supportedPlatform = target.platforms.getDerived(for: platform, usingXCTest: target.type == .test) + let supportedPlatform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) args += [self.triple.tripleString(forPlatformVersion: supportedPlatform.version.versionString)] } else { args += [self.triple.tripleString] @@ -455,8 +455,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan { ) throws { // Supported platforms are defined at the package level. // This will need to become a bit complicated once we have target-level or product-level platform support. - let productPlatform = product.platforms.getDerived(for: .macOS, usingXCTest: product.isLinkingXCTest) - let targetPlatform = target.platforms.getDerived(for: .macOS, usingXCTest: target.type == .test) + let productPlatform = product.getSupportedPlatform(for: .macOS, usingXCTest: product.isLinkingXCTest) + let targetPlatform = target.getSupportedPlatform(for: .macOS, usingXCTest: target.type == .test) // Check if the version requirement is satisfied. // diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index 30dce2f22a3..3e432482f01 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -32,6 +32,7 @@ add_library(PackageGraph Resolution/DependencyResolverBinding.swift Resolution/DependencyResolverDelegate.swift Resolution/DependencyResolverError.swift + Resolution/PlatformVersionProvider.swift Resolution/ResolvedPackage.swift Resolution/ResolvedProduct.swift Resolution/ResolvedTarget.swift diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/PackageGraph+Loading.swift index 5fe6b21ec30..d3f7cf4859b 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/PackageGraph+Loading.swift @@ -145,6 +145,13 @@ extension PackageGraph { } } + let platformVersionProvider: PlatformVersionProvider + if let customXCTestMinimumDeploymentTargets { + platformVersionProvider = .init(implementation: .customXCTestMinimumDeploymentTargets(customXCTestMinimumDeploymentTargets)) + } else { + platformVersionProvider = .init(implementation: .minimumDeploymentTargetDefault) + } + // Resolve dependencies and create resolved packages. let resolvedPackages = try createResolvedPackages( nodes: allNodes, @@ -153,13 +160,7 @@ extension PackageGraph { rootManifests: root.manifests, unsafeAllowedPackages: unsafeAllowedPackages, platformRegistry: customPlatformsRegistry ?? .default, - derivedXCTestPlatformProvider: { declared in - if let customXCTestMinimumDeploymentTargets { - return customXCTestMinimumDeploymentTargets[declared] - } else { - return MinimumDeploymentTarget.default.computeXCTestMinimumDeploymentTarget(for: declared) - } - }, + platformVersionProvider: platformVersionProvider, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -240,7 +241,7 @@ private func createResolvedPackages( rootManifests: [PackageIdentity: Manifest], unsafeAllowedPackages: Set<PackageReference>, platformRegistry: PlatformRegistry, - derivedXCTestPlatformProvider: @escaping (_ declared: PackageModel.Platform) -> PlatformVersion?, + platformVersionProvider: PlatformVersionProvider, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> [ResolvedPackage] { @@ -257,7 +258,8 @@ private func createResolvedPackages( package, productFilter: node.productFilter, isAllowedToVendUnsafeProducts: isAllowedToVendUnsafeProducts, - allowedToOverride: allowedToOverride + allowedToOverride: allowedToOverride, + platformVersionProvider: platformVersionProvider ) } @@ -361,14 +363,19 @@ private func createResolvedPackages( packageBuilder.defaultLocalization = package.manifest.defaultLocalization - packageBuilder.platforms = computePlatforms( + packageBuilder.supportedPlatforms = computePlatforms( package: package, - platformRegistry: platformRegistry, - derivedXCTestPlatformProvider: derivedXCTestPlatformProvider + platformRegistry: platformRegistry ) // Create target builders for each target in the package. - let targetBuilders = package.targets.map{ ResolvedTargetBuilder(target: $0, observabilityScope: packageObservabilityScope) } + let targetBuilders = package.targets.map { + ResolvedTargetBuilder( + target: $0, + observabilityScope: packageObservabilityScope, + platformVersionProvider: platformVersionProvider + ) + } packageBuilder.targets = targetBuilders // Establish dependencies between the targets. A target can only depend on another target present in the same package. @@ -386,7 +393,7 @@ private func createResolvedPackages( } } targetBuilder.defaultLocalization = packageBuilder.defaultLocalization - targetBuilder.platforms = packageBuilder.platforms + targetBuilder.supportedPlatforms = packageBuilder.supportedPlatforms } // Create product builders for each product in the package. A product can only contain a target present in the same package. @@ -743,10 +750,8 @@ private class DuplicateProductsChecker { private func computePlatforms( package: Package, - platformRegistry: PlatformRegistry, - derivedXCTestPlatformProvider: @escaping (_ declared: PackageModel.Platform) -> PlatformVersion? -) -> SupportedPlatforms { - + platformRegistry: PlatformRegistry +) -> [SupportedPlatform] { // the supported platforms as declared in the manifest let declaredPlatforms: [SupportedPlatform] = package.manifest.platforms.map { platform in let declaredPlatform = platformRegistry.platformByName[platform.platformName] @@ -758,10 +763,7 @@ private func computePlatforms( ) } - return SupportedPlatforms( - declared: declaredPlatforms.sorted(by: { $0.platform.name < $1.platform.name }), - derivedXCTestPlatformProvider: derivedXCTestPlatformProvider - ) + return declaredPlatforms.sorted(by: { $0.platform.name < $1.platform.name }) } // Track and override module aliases specified for targets in a package graph @@ -888,11 +890,14 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> { var defaultLocalization: String? = nil /// The platforms supported by this package. - var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none) + var supportedPlatforms: [SupportedPlatform] = [] + + let platformVersionProvider: PlatformVersionProvider init( target: Target, - observabilityScope: ObservabilityScope + observabilityScope: ObservabilityScope, + platformVersionProvider: PlatformVersionProvider ) { self.target = target self.diagnosticsEmitter = observabilityScope.makeDiagnosticsEmitter() { @@ -900,6 +905,7 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> { metadata.targetName = target.name return metadata } + self.platformVersionProvider = platformVersionProvider } func diagnoseInvalidUseOfUnsafeFlags(_ product: ResolvedProduct) throws { @@ -934,7 +940,8 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> { target: self.target, dependencies: dependencies, defaultLocalization: self.defaultLocalization, - platforms: self.platforms + supportedPlatforms: self.supportedPlatforms, + platformVersionProvider: self.platformVersionProvider ) } } @@ -983,27 +990,37 @@ private final class ResolvedPackageBuilder: ResolvedBuilder<ResolvedPackage> { var defaultLocalization: String? = nil /// The platforms supported by this package. - var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none) + var supportedPlatforms: [SupportedPlatform] = [] /// If the given package's source is a registry release, this provides additional metadata and signature information. var registryMetadata: RegistryReleaseMetadata? - init(_ package: Package, productFilter: ProductFilter, isAllowedToVendUnsafeProducts: Bool, allowedToOverride: Bool) { + let platformVersionProvider: PlatformVersionProvider + + init( + _ package: Package, + productFilter: ProductFilter, + isAllowedToVendUnsafeProducts: Bool, + allowedToOverride: Bool, + platformVersionProvider: PlatformVersionProvider + ) { self.package = package self.productFilter = productFilter self.isAllowedToVendUnsafeProducts = isAllowedToVendUnsafeProducts self.allowedToOverride = allowedToOverride + self.platformVersionProvider = platformVersionProvider } override func constructImpl() throws -> ResolvedPackage { return ResolvedPackage( package: self.package, defaultLocalization: self.defaultLocalization, - platforms: self.platforms, + supportedPlatforms: self.supportedPlatforms, dependencies: try self.dependencies.map{ try $0.construct() }, targets: try self.targets.map{ try $0.construct() }, products: try self.products.map{ try $0.construct() }, - registryMetadata: self.registryMetadata + registryMetadata: self.registryMetadata, + platformVersionProvider: self.platformVersionProvider ) } } diff --git a/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift b/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift new file mode 100644 index 00000000000..d35da0e0238 --- /dev/null +++ b/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct PackageModel.MinimumDeploymentTarget +import struct PackageModel.Platform +import struct PackageModel.PlatformVersion +import struct PackageModel.SupportedPlatform + +/// Merging two sets of supported platforms, preferring the max constraint +func merge(into partial: inout [SupportedPlatform], platforms: [SupportedPlatform]) { + for platformSupport in platforms { + if let existing = partial.firstIndex(where: { $0.platform == platformSupport.platform }) { + if partial[existing].version < platformSupport.version { + partial.remove(at: existing) + partial.append(platformSupport) + } + } else { + partial.append(platformSupport) + } + } +} + +public struct PlatformVersionProvider: Hashable { + public enum Implementation: Hashable { + case mergingFromTargets([ResolvedTarget]) + case customXCTestMinimumDeploymentTargets([PackageModel.Platform: PlatformVersion]) + case minimumDeploymentTargetDefault + } + + private let implementation: Implementation + + public init(implementation: Implementation) { + self.implementation = implementation + } + + func derivedXCTestPlatformProvider(_ declared: PackageModel.Platform) -> PlatformVersion? { + switch self.implementation { + case .mergingFromTargets(let targets): + let platforms = targets.reduce(into: [SupportedPlatform]()) { partial, item in + merge( + into: &partial, + platforms: [item.getSupportedPlatform(for: declared, usingXCTest: item.type == .test)] + ) + } + return platforms.first!.version + + case .customXCTestMinimumDeploymentTargets(let customXCTestMinimumDeploymentTargets): + return customXCTestMinimumDeploymentTargets[declared] + + case .minimumDeploymentTargetDefault: + return MinimumDeploymentTarget.default.computeXCTestMinimumDeploymentTarget(for: declared) + } + } + + /// Returns the supported platform instance for the given platform. + func getDerived(declared: [SupportedPlatform], for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { + // derived platform based on known minimum deployment target logic + if let declaredPlatform = declared.first(where: { $0.platform == platform }) { + var version = declaredPlatform.version + + if usingXCTest, + let xcTestMinimumDeploymentTarget = self.derivedXCTestPlatformProvider(platform), + version < xcTestMinimumDeploymentTarget + { + version = xcTestMinimumDeploymentTarget + } + + // If the declared version is smaller than the oldest supported one, we raise the derived version to that. + if version < platform.oldestSupportedVersion { + version = platform.oldestSupportedVersion + } + + return SupportedPlatform( + platform: declaredPlatform.platform, + version: version, + options: declaredPlatform.options + ) + } else { + let minimumSupportedVersion: PlatformVersion + if usingXCTest, + let xcTestMinimumDeploymentTarget = self.derivedXCTestPlatformProvider(platform), + xcTestMinimumDeploymentTarget > platform.oldestSupportedVersion + { + minimumSupportedVersion = xcTestMinimumDeploymentTarget + } else { + minimumSupportedVersion = platform.oldestSupportedVersion + } + + let oldestSupportedVersion: PlatformVersion + if platform == .macCatalyst { + let iOS = self.getDerived(declared: declared, for: .iOS, usingXCTest: usingXCTest) + // If there was no deployment target specified for Mac Catalyst, fall back to the iOS deployment target. + oldestSupportedVersion = max(minimumSupportedVersion, iOS.version) + } else { + oldestSupportedVersion = minimumSupportedVersion + } + + return SupportedPlatform( + platform: platform, + version: oldestSupportedVersion, + options: [] + ) + } + } +} diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index fe7939c499f..481f5c7cda5 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -46,27 +46,39 @@ public final class ResolvedPackage { public let defaultLocalization: String? /// The list of platforms that are supported by this target. - public let platforms: SupportedPlatforms + public let supportedPlatforms: [SupportedPlatform] /// If the given package's source is a registry release, this provides additional metadata and signature information. public let registryMetadata: RegistryReleaseMetadata? + private let platformVersionProvider: PlatformVersionProvider + public init( package: Package, defaultLocalization: String?, - platforms: SupportedPlatforms, + supportedPlatforms: [SupportedPlatform], dependencies: [ResolvedPackage], targets: [ResolvedTarget], products: [ResolvedProduct], - registryMetadata: RegistryReleaseMetadata? + registryMetadata: RegistryReleaseMetadata?, + platformVersionProvider: PlatformVersionProvider ) { self.underlyingPackage = package self.defaultLocalization = defaultLocalization - self.platforms = platforms + self.supportedPlatforms = supportedPlatforms self.dependencies = dependencies self.targets = targets self.products = products self.registryMetadata = registryMetadata + self.platformVersionProvider = platformVersionProvider + } + + public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { + self.platformVersionProvider.getDerived( + declared: self.supportedPlatforms, + for: platform, + usingXCTest: usingXCTest + ) } } diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index 4bfb99029a3..489fe468db4 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -37,7 +37,9 @@ public final class ResolvedProduct { public let defaultLocalization: String? /// The list of platforms that are supported by this product. - public let platforms: SupportedPlatforms + public let supportedPlatforms: [SupportedPlatform] + + public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved product should be compiled for. public let buildTriple: BuildTriple @@ -67,10 +69,11 @@ public final class ResolvedProduct { let defaultLocalization = self.targets.first?.defaultLocalization self.defaultLocalization = defaultLocalization - let platforms = Self.computePlatforms(targets: targets) - self.platforms = platforms + let (platforms, platformVersionProvider) = Self.computePlatforms(targets: targets) + self.supportedPlatforms = platforms + self.platformVersionProvider = platformVersionProvider - self.testEntryPointTarget = underlyingProduct.testEntryPointPath.map { testEntryPointPath in + self.testEntryPointTarget = product.testEntryPointPath.map { testEntryPointPath in // Create an executable resolved target with the entry point file, adding product's targets as dependencies. let dependencies: [Target.Dependency] = product.targets.map { .target($0, conditions: []) } let swiftTarget = SwiftTarget(name: product.name, @@ -81,7 +84,8 @@ public final class ResolvedProduct { target: swiftTarget, dependencies: targets.map { .target($0, conditions: []) }, defaultLocalization: defaultLocalization ?? .none, // safe since this is a derived product - platforms: platforms + supportedPlatforms: platforms, + platformVersionProvider: platformVersionProvider ) } @@ -105,33 +109,23 @@ public final class ResolvedProduct { let recursiveDependencies = try targets.lazy.flatMap { try $0.recursiveTargetDependencies() } return Array(Set(targets).union(recursiveDependencies)) } - - private static func computePlatforms(targets: [ResolvedTarget]) -> SupportedPlatforms { - // merging two sets of supported platforms, preferring the max constraint - func merge(into partial: inout [SupportedPlatform], platforms: [SupportedPlatform]) { - for platformSupport in platforms { - if let existing = partial.firstIndex(where: { $0.platform == platformSupport.platform }) { - if partial[existing].version < platformSupport.version { - partial.remove(at: existing) - partial.append(platformSupport) - } - } else { - partial.append(platformSupport) - } - } + private static func computePlatforms(targets: [ResolvedTarget]) -> ([SupportedPlatform], PlatformVersionProvider) { + let declaredPlatforms = targets.reduce(into: [SupportedPlatform]()) { partial, item in + merge(into: &partial, platforms: item.supportedPlatforms) } - let declared = targets.reduce(into: [SupportedPlatform]()) { partial, item in - merge(into: &partial, platforms: item.platforms.declared) - } - - return SupportedPlatforms( - declared: declared.sorted(by: { $0.platform.name < $1.platform.name })) { declared in - let platforms = targets.reduce(into: [SupportedPlatform]()) { partial, item in - merge(into: &partial, platforms: [item.platforms.getDerived(for: declared, usingXCTest: item.type == .test)]) - } - return platforms.first!.version - } + return ( + declaredPlatforms.sorted(by: { $0.platform.name < $1.platform.name }), + PlatformVersionProvider(implementation: .mergingFromTargets(targets)) + ) + } + + public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { + self.platformVersionProvider.getDerived( + declared: self.supportedPlatforms, + for: platform, + usingXCTest: usingXCTest + ) } } diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedTarget.swift index eb10eb173c4..7b73ec323f8 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedTarget.swift @@ -139,7 +139,9 @@ public final class ResolvedTarget { public let defaultLocalization: String? /// The list of platforms that are supported by this target. - public let platforms: SupportedPlatforms + public let supportedPlatforms: [SupportedPlatform] + + private let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. public let buildTriple: BuildTriple @@ -149,14 +151,24 @@ public final class ResolvedTarget { target: Target, dependencies: [Dependency], defaultLocalization: String?, - platforms: SupportedPlatforms + supportedPlatforms: [SupportedPlatform], + platformVersionProvider: PlatformVersionProvider ) { self.underlyingTarget = target self.dependencies = dependencies self.defaultLocalization = defaultLocalization - self.platforms = platforms + self.supportedPlatforms = supportedPlatforms + self.platformVersionProvider = platformVersionProvider self.buildTriple = .destination } + + public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { + self.platformVersionProvider.getDerived( + declared: self.supportedPlatforms, + for: platform, + usingXCTest: usingXCTest + ) + } } extension ResolvedTarget: Hashable { diff --git a/Sources/PackageModel/Platform.swift b/Sources/PackageModel/Platform.swift index b74d94b93f9..8cce1a3dc8b 100644 --- a/Sources/PackageModel/Platform.swift +++ b/Sources/PackageModel/Platform.swift @@ -52,61 +52,6 @@ public struct Platform: Equatable, Hashable, Codable { } -public struct SupportedPlatforms { - public let declared: [SupportedPlatform] - private let derivedXCTestPlatformProvider: ((Platform) -> PlatformVersion?)? - - public init(declared: [SupportedPlatform], derivedXCTestPlatformProvider: ((_ declared: Platform) -> PlatformVersion?)?) { - self.declared = declared - self.derivedXCTestPlatformProvider = derivedXCTestPlatformProvider - } - - /// Returns the supported platform instance for the given platform. - public func getDerived(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { - // derived platform based on known minimum deployment target logic - if let declaredPlatform = self.declared.first(where: { $0.platform == platform }) { - var version = declaredPlatform.version - - if usingXCTest, let xcTestMinimumDeploymentTarget = derivedXCTestPlatformProvider?(platform), version < xcTestMinimumDeploymentTarget { - version = xcTestMinimumDeploymentTarget - } - - // If the declared version is smaller than the oldest supported one, we raise the derived version to that. - if version < platform.oldestSupportedVersion { - version = platform.oldestSupportedVersion - } - - return SupportedPlatform( - platform: declaredPlatform.platform, - version: version, - options: declaredPlatform.options - ) - } else { - let minimumSupportedVersion: PlatformVersion - if usingXCTest, let xcTestMinimumDeploymentTarget = derivedXCTestPlatformProvider?(platform), xcTestMinimumDeploymentTarget > platform.oldestSupportedVersion { - minimumSupportedVersion = xcTestMinimumDeploymentTarget - } else { - minimumSupportedVersion = platform.oldestSupportedVersion - } - - let oldestSupportedVersion: PlatformVersion - if platform == .macCatalyst { - let iOS = getDerived(for: .iOS, usingXCTest: usingXCTest) - // If there was no deployment target specified for Mac Catalyst, fall back to the iOS deployment target. - oldestSupportedVersion = max(minimumSupportedVersion, iOS.version) - } else { - oldestSupportedVersion = minimumSupportedVersion - } - - return SupportedPlatform( - platform: platform, - version: oldestSupportedVersion, - options: [] - ) - } - } -} - /// Represents a platform supported by a target. public struct SupportedPlatform: Equatable, Codable { /// The platform. diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index 981f7fc91e0..0d480a6ea96 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -174,7 +174,7 @@ public final class ResolvedTargetResult { } public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary(uniqueKeysWithValues: target.platforms.declared.map({ ($0.platform.name, $0.version.versionString) })) + let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } @@ -182,7 +182,7 @@ public final class ResolvedTargetResult { let derived = platforms.map { let platform = PlatformRegistry.default.platformByName[$0.key] ?? PackageModel.Platform .custom(name: $0.key, oldestSupportedVersion: $0.value) - return self.target.platforms.getDerived(for: platform, usingXCTest: self.target.type == .test) + return self.target.getSupportedPlatform(for: platform, usingXCTest: self.target.type == .test) } let targetPlatforms = Dictionary( uniqueKeysWithValues: derived @@ -192,7 +192,7 @@ public final class ResolvedTargetResult { } public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { - let platform = target.platforms.getDerived(for: platform, usingXCTest: target.type == .test) + let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } } @@ -233,21 +233,21 @@ public final class ResolvedProductResult { } public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary(uniqueKeysWithValues: product.platforms.declared.map({ ($0.platform.name, $0.version.versionString) })) + let targetPlatforms = Dictionary(uniqueKeysWithValues: product.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } public func checkDerivedPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { let derived = platforms.map { let platform = PlatformRegistry.default.platformByName[$0.key] ?? PackageModel.Platform.custom(name: $0.key, oldestSupportedVersion: $0.value) - return product.platforms.getDerived(for: platform, usingXCTest: product.isLinkingXCTest) + return product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) } let targetPlatforms = Dictionary(uniqueKeysWithValues: derived.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { - let platform = product.platforms.getDerived(for: platform, usingXCTest: product.isLinkingXCTest) + let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } } diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index ab6d29e78e8..58beb74531d 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -271,13 +271,13 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.SDKROOT] = "auto" settings[.SDK_VARIANT] = "auto" settings[.SKIP_INSTALL] = "YES" - settings[.MACOSX_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .macOS) - settings[.IPHONEOS_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .iOS) - settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst] = package.platforms.deploymentTarget(for: .macCatalyst) - settings[.TVOS_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .tvOS) - settings[.WATCHOS_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .watchOS) - settings[.XROS_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .visionOS) - settings[.DRIVERKIT_DEPLOYMENT_TARGET] = package.platforms.deploymentTarget(for: .driverKit) + settings[.MACOSX_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .macOS) + settings[.IPHONEOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .iOS) + settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst] = package.deploymentTarget(for: .macCatalyst) + settings[.TVOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .tvOS) + settings[.WATCHOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .watchOS) + settings[.XROS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .visionOS) + settings[.DRIVERKIT_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .driverKit) settings[.DYLIB_INSTALL_NAME_BASE] = "@rpath" settings[.USE_HEADERMAP] = "NO" settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS] = ["$(inherited)", "SWIFT_PACKAGE"] @@ -301,7 +301,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { PlatformRegistry.default.knownPlatforms.forEach { guard let platform = PIF.BuildSettings.Platform.from(platform: $0) else { return } - let supportedPlatform = package.platforms.getDerived(for: $0, usingXCTest: false) + let supportedPlatform = package.getSupportedPlatform(for: $0, usingXCTest: false) if !supportedPlatform.options.isEmpty { settings[.SPECIALIZATION_SDK_OPTIONS, for: platform] = supportedPlatform.options } @@ -430,11 +430,11 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // Tests can have a custom deployment target based on the minimum supported by XCTest. if mainTarget.underlyingTarget.type == .test { - settings[.MACOSX_DEPLOYMENT_TARGET] = mainTarget.platforms.deploymentTarget(for: .macOS, usingXCTest: true) - settings[.IPHONEOS_DEPLOYMENT_TARGET] = mainTarget.platforms.deploymentTarget(for: .iOS, usingXCTest: true) - settings[.TVOS_DEPLOYMENT_TARGET] = mainTarget.platforms.deploymentTarget(for: .tvOS, usingXCTest: true) - settings[.WATCHOS_DEPLOYMENT_TARGET] = mainTarget.platforms.deploymentTarget(for: .watchOS, usingXCTest: true) - settings[.XROS_DEPLOYMENT_TARGET] = mainTarget.platforms.deploymentTarget(for: .visionOS, usingXCTest: true) + settings[.MACOSX_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .macOS, usingXCTest: true) + settings[.IPHONEOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .iOS, usingXCTest: true) + settings[.TVOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .tvOS, usingXCTest: true) + settings[.WATCHOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .watchOS, usingXCTest: true) + settings[.XROS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .visionOS, usingXCTest: true) } if product.type == .executable { @@ -1417,9 +1417,15 @@ extension Array where Element == ResolvedTarget.Dependency { } } -extension SupportedPlatforms { +extension ResolvedPackage { + func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { + return self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString + } +} + +extension ResolvedTarget { func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { - return self.getDerived(for: platform, usingXCTest: usingXCTest).version.versionString + return self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString } } diff --git a/Tests/PackageGraphTests/TargetTests.swift b/Tests/PackageGraphTests/TargetTests.swift index c8c1e4a2f05..0df61ae3c52 100644 --- a/Tests/PackageGraphTests/TargetTests.swift +++ b/Tests/PackageGraphTests/TargetTests.swift @@ -30,7 +30,8 @@ private extension ResolvedTarget { ), dependencies: deps.map { .target($0, conditions: []) }, defaultLocalization: nil, - platforms: .init(declared: [], derivedXCTestPlatformProvider: .none) + supportedPlatforms: [], + platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault) ) } }