Skip to content

Improve handling of minimum deployment target #2710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,21 @@
*/

import PackageDescription
import class Foundation.ProcessInfo

// We default to a 10.10 minimum deployment target for clients of libSwiftPM,
// but allow overriding it when building for a toolchain.

let macOSPlatform: SupportedPlatform
if let deploymentTarget = ProcessInfo.processInfo.environment["SWIFTPM_MACOS_DEPLOYMENT_TARGET"] {
macOSPlatform = .macOS(deploymentTarget)
} else {
macOSPlatform = .macOS(.v10_10)
}

let package = Package(
name: "SwiftPM",
platforms: [macOSPlatform],
products: [
// The `libSwiftPM` set of interfaces to programatically work with Swift
// packages.
Expand Down Expand Up @@ -222,7 +234,6 @@ let package = Package(
// package dependency like this but there is no other good way of expressing
// this right now.

import class Foundation.ProcessInfo

if ProcessInfo.processInfo.environment["SWIFTPM_LLBUILD_FWK"] == nil {
if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Commands/SwiftPackageTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
let builder = PackageBuilder(
manifest: manifest,
path: try getPackageRoot(),
xcTestMinimumDeploymentTargets: [:], // Minimum deployment target does not matter for this operation.
diagnostics: diagnostics
)
let package = try builder.construct()
Expand Down Expand Up @@ -354,6 +355,7 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
let builder = PackageBuilder(
manifest: manifest,
path: try getPackageRoot(),
xcTestMinimumDeploymentTargets: MinimumDeploymentTarget.default.xcTestMinimumDeploymentTargets,
diagnostics: diagnostics
)
let package = try builder.construct()
Expand Down
2 changes: 2 additions & 0 deletions Sources/PackageGraph/PackageGraphLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public struct PackageGraphLoader {
requiredDependencies: Set<PackageReference> = [],
unsafeAllowedPackages: Set<PackageReference> = [],
remoteArtifacts: [RemoteArtifact] = [],
xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion] = MinimumDeploymentTarget.default.xcTestMinimumDeploymentTargets,
diagnostics: DiagnosticsEngine,
fileSystem: FileSystem = localFileSystem,
shouldCreateMultipleTestProducts: Bool = false,
Expand Down Expand Up @@ -172,6 +173,7 @@ public struct PackageGraphLoader {
path: packagePath,
additionalFileRules: additionalFileRules,
remoteArtifacts: remoteArtifacts,
xcTestMinimumDeploymentTargets: xcTestMinimumDeploymentTargets,
fileSystem: fileSystem,
diagnostics: diagnostics,
shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts,
Expand Down
1 change: 1 addition & 0 deletions Sources/PackageLoading/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_library(PackageLoading
Diagnostics.swift
ManifestBuilder.swift
ManifestLoader.swift
MinimumDeploymentTarget.swift
ModuleMapGenerator.swift
PackageBuilder.swift
PackageDescription4Loader.swift
Expand Down
18 changes: 17 additions & 1 deletion Sources/PackageLoading/ManifestLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ public final class ManifestLoader: ManifestLoaderProtocol {
}
}

private static var _packageDescriptionMinimumDeploymentTarget: String?

/// Parse the manifest at the given path to JSON.
fileprivate func parse(
packageIdentity: String,
Expand Down Expand Up @@ -531,6 +533,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
cmd += [resources.swiftCompiler.pathString]
cmd += verbosity.ccArgs

let macOSPackageDescriptionPath: AbsolutePath
// If we got the binDir that means we could be developing SwiftPM in Xcode
// which produces a framework for dynamic package products.
let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks")
Expand All @@ -540,13 +543,27 @@ public final class ManifestLoader: ManifestLoaderProtocol {
"-framework", "PackageDescription",
"-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString,
]

macOSPackageDescriptionPath = packageFrameworkPath.appending(RelativePath("PackageDescription.framework/PackageDescription"))
} else {
cmd += [
"-L", runtimePath.pathString,
"-lPackageDescription",
"-Xlinker", "-rpath", "-Xlinker", runtimePath.pathString
]

// note: this is not correct for all platforms, but we only actually use it on macOS.
macOSPackageDescriptionPath = runtimePath.appending(RelativePath("libPackageDescription.dylib"))
}

// Use the same minimum deployment target as the PackageDescription library (with a fallback of 10.15).
#if os(macOS)
if Self._packageDescriptionMinimumDeploymentTarget == nil {
Self._packageDescriptionMinimumDeploymentTarget = (try MinimumDeploymentTarget.computeMinimumDeploymentTarget(of: macOSPackageDescriptionPath))?.versionString ?? "10.15"
}
let version = Self._packageDescriptionMinimumDeploymentTarget!
cmd += ["-target", "x86_64-apple-macosx\(version)"]
#endif

cmd += compilerFlags
if let moduleCachePath = moduleCachePath {
Expand Down Expand Up @@ -680,7 +697,6 @@ public final class ManifestLoader: ManifestLoaderProtocol {
cmd += ["-swift-version", toolsVersion.swiftLanguageVersion.rawValue]
cmd += ["-I", runtimePath.pathString]
#if os(macOS)
cmd += ["-target", "x86_64-apple-macosx10.10"]
if let sdkRoot = resources.sdkRoot ?? self.sdkRoot() {
cmd += ["-sdk", sdkRoot.pathString]
}
Expand Down
70 changes: 70 additions & 0 deletions Sources/PackageLoading/MinimumDeploymentTarget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2014 - 2020 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 Swift project authors
*/

import PackageModel
import TSCBasic

public struct MinimumDeploymentTarget {
public let xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion]

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

public init() {
xcTestMinimumDeploymentTargets = PlatformRegistry.default.knownPlatforms.reduce([PackageModel.Platform:PlatformVersion]()) {
var dict = $0
dict[$1] = Self.computeXCTestMinimumDeploymentTarget(for: $1)
return dict
}
}

static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath) throws -> PlatformVersion? {
let runResult = try Process.popen(arguments: ["xcrun", "vtool", "-show-build", binaryPath.pathString])
guard let versionString = try runResult.utf8Output().components(separatedBy: "\n").first(where: { $0.contains("minos") })?.components(separatedBy: " ").last else { return nil }
return PlatformVersion(versionString)
}

static func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
guard let sdkName = platform.sdkName else {
return platform.oldestSupportedVersion
}

// On macOS, we are determining the deployment target by looking at the XCTest binary.
#if os(macOS)
do {
let runResult = try Process.popen(arguments: ["xcrun", "--sdk", sdkName, "--show-sdk-platform-path"])
let sdkPath = AbsolutePath(try runResult.utf8Output().spm_chuzzle() ?? "")
let xcTestPath = sdkPath.appending(RelativePath("Developer/Library/Frameworks/XCTest.framework/XCTest"))

if let version = try computeMinimumDeploymentTarget(of: xcTestPath) {
return version
}
} catch { } // we do not treat this a fatal and instead use the fallback minimum deployment target
#endif

return platform.oldestSupportedVersion
}
}

private extension PackageModel.Platform {
var sdkName: String? {
switch self {
case .macOS:
return "macosx"
case .iOS:
return "iphoneos"
case .tvOS:
return "appletvos"
case .watchOS:
return "watchos"
default:
return nil
}
}
}
41 changes: 31 additions & 10 deletions Sources/PackageLoading/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ public final class PackageBuilder {
/// The additionla file detection rules.
private let additionalFileRules: [FileRuleDescription]

/// Minimum deployment target of XCTest per platform.
private let xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion]

/// Create a builder for the given manifest and package `path`.
///
/// - Parameters:
Expand All @@ -238,6 +241,7 @@ public final class PackageBuilder {
path: AbsolutePath,
additionalFileRules: [FileRuleDescription] = [],
remoteArtifacts: [RemoteArtifact] = [],
xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion],
fileSystem: FileSystem = localFileSystem,
diagnostics: DiagnosticsEngine,
shouldCreateMultipleTestProducts: Bool = false,
Expand All @@ -247,6 +251,7 @@ public final class PackageBuilder {
self.packagePath = path
self.additionalFileRules = additionalFileRules
self.remoteArtifacts = remoteArtifacts
self.xcTestMinimumDeploymentTargets = xcTestMinimumDeploymentTargets
self.fileSystem = fileSystem
self.diagnostics = diagnostics
self.shouldCreateMultipleTestProducts = shouldCreateMultipleTestProducts
Expand All @@ -263,6 +268,7 @@ public final class PackageBuilder {
public static func loadPackage(
packagePath: AbsolutePath,
swiftCompiler: AbsolutePath,
xcTestMinimumDeploymentTargets: [PackageModel.Platform:PlatformVersion],
diagnostics: DiagnosticsEngine,
kind: PackageReference.Kind = .root
) throws -> Package {
Expand All @@ -273,6 +279,7 @@ public final class PackageBuilder {
let builder = PackageBuilder(
manifest: manifest,
path: packagePath,
xcTestMinimumDeploymentTargets: xcTestMinimumDeploymentTargets,
diagnostics: diagnostics)
return try builder.construct()
}
Expand Down Expand Up @@ -716,7 +723,7 @@ public final class PackageBuilder {
name: potentialModule.name,
bundleName: bundleName,
defaultLocalization: manifest.defaultLocalization,
platforms: self.platforms(),
platforms: self.platforms(isTest: potentialModule.isTest),
isTest: potentialModule.isTest,
sources: sources,
resources: resources,
Expand All @@ -729,7 +736,7 @@ public final class PackageBuilder {
name: potentialModule.name,
bundleName: bundleName,
defaultLocalization: manifest.defaultLocalization,
platforms: self.platforms(),
platforms: self.platforms(isTest: potentialModule.isTest),
cLanguageStandard: manifest.cLanguageStandard,
cxxLanguageStandard: manifest.cxxLanguageStandard,
includeDir: publicHeadersPath,
Expand Down Expand Up @@ -836,19 +843,25 @@ public final class PackageBuilder {
}

/// Returns the list of platforms supported by the manifest.
func platforms() -> [SupportedPlatform] {
if let platforms = _platforms {
func platforms(isTest: Bool = false) -> [SupportedPlatform] {
if let platforms = _platforms[isTest] {
return platforms
}

var supportedPlatforms: [SupportedPlatform] = []

/// Add each declared platform to the supported platforms list.
for platform in manifest.platforms {
let declaredPlatform = platformRegistry.platformByName[platform.platformName]!
var version = PlatformVersion(platform.version)

if let xcTestMinimumDeploymentTarget = xcTestMinimumDeploymentTargets[declaredPlatform], isTest, version < xcTestMinimumDeploymentTarget {
version = xcTestMinimumDeploymentTarget
}

let supportedPlatform = SupportedPlatform(
platform: platformRegistry.platformByName[platform.platformName]!,
version: PlatformVersion(platform.version),
platform: declaredPlatform,
version: version,
options: platform.options
)

Expand All @@ -862,19 +875,27 @@ public final class PackageBuilder {
for platformName in remainingPlatforms {
let platform = platformRegistry.platformByName[platformName]!

let oldestSupportedVersion: PlatformVersion
if let xcTestMinimumDeploymentTarget = xcTestMinimumDeploymentTargets[platform], isTest {
oldestSupportedVersion = xcTestMinimumDeploymentTarget
} else {
oldestSupportedVersion = platform.oldestSupportedVersion
}

let supportedPlatform = SupportedPlatform(
platform: platform,
version: platform.oldestSupportedVersion,
version: oldestSupportedVersion,
options: []
)

supportedPlatforms.append(supportedPlatform)
}

_platforms = supportedPlatforms
return _platforms!
_platforms[isTest] = supportedPlatforms
return supportedPlatforms
}
private var _platforms: [SupportedPlatform]? = nil
// Keep two sets of supported platforms, based on the `isTest` parameter.
private var _platforms = [Bool:[SupportedPlatform]]()

/// The platform registry instance.
private var platformRegistry: PlatformRegistry {
Expand Down
10 changes: 9 additions & 1 deletion Sources/XCBuildSupport/PIFBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
settings[.SDKROOT] = "auto"
settings[.SDK_VARIANT] = "auto"
settings[.SKIP_INSTALL] = "YES"
let firstTarget = package.targets.first?.underlyingTarget
let firstTarget = package.targets.first(where: { $0.type != .test })?.underlyingTarget ?? package.targets.first?.underlyingTarget
settings[.MACOSX_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .macOS)
settings[.IPHONEOS_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .iOS)
settings[.TVOS_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .tvOS)
Expand Down Expand Up @@ -385,6 +385,14 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
settings[.SWIFT_FORCE_STATIC_LINK_STDLIB] = "NO"
settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB] = "YES"

// Tests can have a custom deployment target based on the minimum supported by XCTest.
if mainTarget.underlyingTarget.type == .test {
settings[.MACOSX_DEPLOYMENT_TARGET] = mainTarget.underlyingTarget.deploymentTarget(for: .macOS)
settings[.IPHONEOS_DEPLOYMENT_TARGET] = mainTarget.underlyingTarget.deploymentTarget(for: .iOS)
settings[.TVOS_DEPLOYMENT_TARGET] = mainTarget.underlyingTarget.deploymentTarget(for: .tvOS)
settings[.WATCHOS_DEPLOYMENT_TARGET] = mainTarget.underlyingTarget.deploymentTarget(for: .watchOS)
}

if product.type == .executable {
// Command-line tools are only supported for the macOS platforms.
settings[.SDKROOT] = "macosx"
Expand Down
5 changes: 3 additions & 2 deletions Tests/BuildTests/BuildPlanTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import TSCBasic
import TSCUtility
import SPMTestSupport
import PackageModel
import PackageLoading
@testable import PackageLoading
import SPMBuildCore
import Build

Expand Down Expand Up @@ -836,14 +836,15 @@ final class BuildPlanTests: XCTestCase {
XCTAssertMatch(fooTests, ["-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", "-g", .equal(j), "-DSWIFT_PACKAGE", "-DDEBUG", "-module-cache-path", "/path/to/build/debug/ModuleCache", .anySequence])

#if os(macOS)
let version = MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString
XCTAssertEqual(try result.buildProduct(for: "PkgPackageTests").linkArguments(), [
"/fake/path/to/swiftc", "-L", "/path/to/build/debug", "-o",
"/path/to/build/debug/PkgPackageTests.xctest/Contents/MacOS/PkgPackageTests", "-module-name",
"PkgPackageTests", "-Xlinker", "-bundle",
"-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../../",
"@/path/to/build/debug/PkgPackageTests.product/Objects.LinkFileList",
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
"-target", "x86_64-apple-macosx10.10",
"-target", "x86_64-apple-macosx\(version)",
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/Foo.swiftmodule",
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/FooTests.swiftmodule",
])
Expand Down
Loading