Skip to content

Verify product dependencies w/ target platform, vs. hardcoded macOS #6963

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
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
158 changes: 87 additions & 71 deletions Sources/Build/BuildPlan/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ import var TSCUtility.verbosity

extension String {
var asSwiftStringLiteralConstant: String {
return unicodeScalars.reduce("", { $0 + $1.escaped(asASCII: false) })
unicodeScalars.reduce("") { $0 + $1.escaped(asASCII: false) }
}
}

extension Array where Element == String {
extension [String] {
/// Converts a set of C compiler flags into an equivalent set to be
/// indirected through the Swift compiler instead.
func asSwiftcCCompilerFlags() -> Self {
Expand Down Expand Up @@ -142,7 +142,8 @@ extension BuildParameters {
return args
}

/// Computes the linker flags to use in order to rename a module-named main function to 'main' for the target platform, or nil if the linker doesn't support it for the platform.
/// Computes the linker flags to use in order to rename a module-named main function to 'main' for the target
/// platform, or nil if the linker doesn't support it for the platform.
func linkerFlagsForRenamingMainFunction(of target: ResolvedTarget) -> [String]? {
let args: [String]
if self.triple.isApple() {
Expand Down Expand Up @@ -209,12 +210,12 @@ public class BuildPlan: SPMBuildCore.BuildPlan {

/// The build targets.
public var targets: AnySequence<TargetBuildDescription> {
return AnySequence(targetMap.values)
AnySequence(self.targetMap.values)
}

/// The products in this plan.
public var buildProducts: AnySequence<SPMBuildCore.ProductBuildDescription> {
return AnySequence(productMap.values.map { $0 as SPMBuildCore.ProductBuildDescription })
AnySequence(self.productMap.values.map { $0 as SPMBuildCore.ProductBuildDescription })
}

/// The results of invoking any build tool plugins used by targets in this build.
Expand Down Expand Up @@ -313,11 +314,12 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
observabilityScope: observabilityScope
)
}
let macroProductsByTarget = productMap.keys.filter { $0.type == .macro }.reduce(into: [ResolvedTarget: ResolvedProduct]()) {
if let target = $1.targets.first {
$0[target] = $1
let macroProductsByTarget = productMap.keys.filter { $0.type == .macro }
.reduce(into: [ResolvedTarget: ResolvedProduct]()) {
if let target = $1.targets.first {
$0[target] = $1
}
}
}

// Create build target description for each target which we need to plan.
// Plugin targets are noted, since they need to be compiled, but they do
Expand Down Expand Up @@ -348,6 +350,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
try BuildPlan.validateDeploymentVersionOfProductDependency(
product: product,
forTarget: target,
buildEnvironment: buildParameters.buildEnvironment,
observabilityScope: self.observabilityScope
)
}
Expand All @@ -372,45 +375,50 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
shouldGenerateTestObservation = false // Only generate the code once.
}

targetMap[target] = try .swift(SwiftTargetBuildDescription(
package: package,
target: target,
toolsVersion: toolsVersion,
additionalFileRules: additionalFileRules,
buildParameters: buildParameters,
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
prebuildCommandResults: prebuildCommandResults[target] ?? [],
requiredMacroProducts: requiredMacroProducts,
shouldGenerateTestObservation: generateTestObservation,
disableSandbox: self.disableSandbox,
fileSystem: fileSystem,
observabilityScope: observabilityScope)
targetMap[target] = try .swift(
SwiftTargetBuildDescription(
package: package,
target: target,
toolsVersion: toolsVersion,
additionalFileRules: additionalFileRules,
buildParameters: buildParameters,
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
prebuildCommandResults: prebuildCommandResults[target] ?? [],
requiredMacroProducts: requiredMacroProducts,
shouldGenerateTestObservation: generateTestObservation,
disableSandbox: self.disableSandbox,
fileSystem: fileSystem,
observabilityScope: observabilityScope
)
)
case is ClangTarget:
targetMap[target] = try .clang(ClangTargetBuildDescription(
target: target,
toolsVersion: toolsVersion,
additionalFileRules: additionalFileRules,
buildParameters: buildParameters,
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
prebuildCommandResults: prebuildCommandResults[target] ?? [],
fileSystem: fileSystem,
observabilityScope: observabilityScope)
targetMap[target] = try .clang(
ClangTargetBuildDescription(
target: target,
toolsVersion: toolsVersion,
additionalFileRules: additionalFileRules,
buildParameters: buildParameters,
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
prebuildCommandResults: prebuildCommandResults[target] ?? [],
fileSystem: fileSystem,
observabilityScope: observabilityScope
)
)
case is PluginTarget:
guard let package = graph.package(for: target) else {
throw InternalError("package not found for \(target)")
}
try pluginDescriptions.append(PluginDescription(
target: target,
products: package.products.filter{ $0.targets.contains(target) },
products: package.products.filter { $0.targets.contains(target) },
package: package,
toolsVersion: toolsVersion,
fileSystem: fileSystem))
fileSystem: fileSystem
))
case is SystemLibraryTarget, is BinaryTarget:
break
break
default:
throw InternalError("unhandled \(target.underlying)")
throw InternalError("unhandled \(target.underlying)")
}
}

Expand Down Expand Up @@ -443,7 +451,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
derivedTestTargets.append(discoveryTargetBuildDescription.target)
}

derivedTestTargetsMap[item.product] = derivedTestTargets
self.derivedTestTargetsMap[item.product] = derivedTestTargets
}
}

Expand All @@ -452,18 +460,25 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
self.pluginDescriptions = pluginDescriptions

// Finally plan these targets.
try plan()
try self.plan()
}

static func validateDeploymentVersionOfProductDependency(
product: ResolvedProduct,
forTarget target: ResolvedTarget,
buildEnvironment: BuildEnvironment,
observabilityScope: ObservabilityScope
) throws {
// Supported platforms are defined at the package level.
// Supported platforms are defined at the package (e.g., build environment) level.
// This will need to become a bit complicated once we have target-level or product-level platform support.
let productPlatform = product.getSupportedPlatform(for: .macOS, usingXCTest: product.isLinkingXCTest)
let targetPlatform = target.getSupportedPlatform(for: .macOS, usingXCTest: target.type == .test)
let productPlatform = product.getSupportedPlatform(
for: buildEnvironment.platform,
usingXCTest: product.isLinkingXCTest
)
let targetPlatform = target.getSupportedPlatform(
for: buildEnvironment.platform,
usingXCTest: target.type == .test
)

// Check if the version requirement is satisfied.
//
Expand All @@ -481,7 +496,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
/// Plan the targets and products.
private func plan() throws {
// Plan targets.
for buildTarget in targets {
for buildTarget in self.targets {
switch buildTarget {
case .swift(let target):
try self.plan(swiftTarget: target)
Expand All @@ -491,8 +506,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
}

// Plan products.
for buildProduct in buildProducts {
try plan(buildProduct: buildProduct as! ProductBuildDescription)
for buildProduct in self.buildProducts {
try self.plan(buildProduct: buildProduct as! ProductBuildDescription)
}
// FIXME: We need to find out if any product has a target on which it depends
// both static and dynamically and then issue a suitable diagnostic or auto
Expand Down Expand Up @@ -523,7 +538,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
arguments += extraSwiftCFlags

// Add search paths to the directories containing module maps and Swift modules.
for target in targets {
for target in self.targets {
switch target {
case .swift(let targetDescription):
arguments += ["-I", targetDescription.moduleOutputPath.parentDirectory.pathString]
Expand Down Expand Up @@ -553,16 +568,16 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
var arguments = ["repl", "-I" + buildPath, "-L" + buildPath]

// Link the special REPL product that contains all of the library targets.
let replProductName = graph.rootPackages[0].identity.description + Product.replProductSuffix
let replProductName = self.graph.rootPackages[0].identity.description + Product.replProductSuffix
arguments.append("-l" + replProductName)

// The graph should have the REPL product.
assert(graph.allProducts.first(where: { $0.name == replProductName }) != nil)
assert(self.graph.allProducts.first(where: { $0.name == replProductName }) != nil)

// Add the search path to the directory containing the modulemap file.
for target in targets {
for target in self.targets {
switch target {
case .swift: break
case .swift: break
case .clang(let targetDescription):
if let includeDir = targetDescription.moduleMap?.parentDirectory {
arguments += ["-I\(includeDir.pathString)"]
Expand All @@ -585,16 +600,15 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
// If we already have these flags, we're done.
if let flags = pkgConfigCache[target] {
return flags
}
else {
pkgConfigCache[target] = ([], [])
} else {
self.pkgConfigCache[target] = ([], [])
}
let results = try pkgConfigArgs(
for: target,
pkgConfigDirectories: self.destinationBuildParameters.pkgConfigDirectories,
sdkRootPath: self.destinationBuildParameters.toolchain.sdkRootPath,
fileSystem: fileSystem,
observabilityScope: observabilityScope
fileSystem: self.fileSystem,
observabilityScope: self.observabilityScope
)
var ret: [(cFlags: [String], libs: [String])] = []
for result in results {
Expand All @@ -613,21 +627,23 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
}

let result = ([String](cflagsCache), libsCache)
pkgConfigCache[target] = result
self.pkgConfigCache[target] = result
return result
}

/// Extracts the library information from an XCFramework.
func parseXCFramework(for binaryTarget: BinaryTarget, triple: Basics.Triple) throws -> [LibraryInfo] {
try self.externalLibrariesCache.memoize(key: binaryTarget) {
return try binaryTarget.parseXCFrameworks(for: triple, fileSystem: self.fileSystem)
try binaryTarget.parseXCFrameworks(for: triple, fileSystem: self.fileSystem)
}
}
}

extension Basics.Diagnostic {
static var swiftBackDeployError: Self {
.warning("Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an optional Swift library package that can be downloaded from \"More Downloads\" for Apple Developers at https://developer.apple.com/download/more/")
.warning(
"Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an optional Swift library package that can be downloaded from \"More Downloads\" for Apple Developers at https://developer.apple.com/download/more/"
)
}

static func productRequiresHigherPlatformVersion(
Expand All @@ -637,15 +653,15 @@ extension Basics.Diagnostic {
productPlatform: SupportedPlatform
) -> Self {
.error("""
the \(target.type.rawValue) '\(target.name)' requires \
\(targetPlatform.platform.name) \(targetPlatform.version.versionString), \
but depends on the product '\(product)' which requires \
\(productPlatform.platform.name) \(productPlatform.version.versionString); \
consider changing the \(target.type.rawValue) '\(target.name)' to require \
\(productPlatform.platform.name) \(productPlatform.version.versionString) or later, \
or the product '\(product)' to require \
\(targetPlatform.platform.name) \(targetPlatform.version.versionString) or earlier.
""")
the \(target.type.rawValue) '\(target.name)' requires \
\(targetPlatform.platform.name) \(targetPlatform.version.versionString), \
but depends on the product '\(product)' which requires \
\(productPlatform.platform.name) \(productPlatform.version.versionString); \
consider changing the \(target.type.rawValue) '\(target.name)' to require \
\(productPlatform.platform.name) \(productPlatform.version.versionString) or later, \
or the product '\(product)' to require \
\(targetPlatform.platform.name) \(targetPlatform.version.versionString) or earlier.
""")
}

static func binaryTargetsNotSupported() -> Self {
Expand All @@ -656,7 +672,7 @@ extension Basics.Diagnostic {
extension BuildParameters {
/// Returns a named bundle's path inside the build directory.
func bundlePath(named name: String) -> AbsolutePath {
return buildPath.appending(component: name + self.triple.nsbundleExtension)
buildPath.appending(component: name + self.triple.nsbundleExtension)
}
}

Expand Down Expand Up @@ -705,20 +721,20 @@ extension ResolvedPackage {

extension ResolvedProduct {
private var isAutomaticLibrary: Bool {
return self.type == .library(.automatic)
self.type == .library(.automatic)
}

private var isBinaryOnly: Bool {
return self.targets.filter({ !($0.underlying is BinaryTarget) }).isEmpty
}

private var isPlugin: Bool {
return self.type == .plugin
self.type == .plugin
}

// We shouldn't create product descriptions for automatic libraries, plugins or products which consist solely of binary targets, because they don't produce any output.
// We shouldn't create product descriptions for automatic libraries, plugins or products which consist solely of
// binary targets, because they don't produce any output.
fileprivate var shouldCreateProductDescription: Bool {
return !isAutomaticLibrary && !isBinaryOnly && !isPlugin
!self.isAutomaticLibrary && !self.isBinaryOnly && !self.isPlugin
}
}

Loading