Skip to content
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

Conditional target dependencies proposal implementation #2428

Merged
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
5 changes: 5 additions & 0 deletions Sources/Build/BuildParameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public struct BuildParameters: Encodable {
/// module to finish building.
public var emitSwiftModuleSeparately: Bool

/// The current build environment.
public var buildEnvironment: BuildEnvironment {
BuildEnvironment(platform: currentPlatform, configuration: configuration)
}

public init(
dataPath: AbsolutePath,
configuration: BuildConfiguration,
Expand Down
53 changes: 34 additions & 19 deletions Sources/Build/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ extension BuildParameters {
}

/// The current platform we're building for.
fileprivate var currentPlatform: PackageModel.Platform {
var currentPlatform: PackageModel.Platform {
if self.triple.isDarwin() {
return .macOS
} else if self.triple.isAndroid() {
Expand All @@ -88,7 +88,7 @@ extension BuildParameters {

/// Returns the scoped view of build settings for a given target.
fileprivate func createScope(for target: ResolvedTarget) -> BuildSettings.Scope {
return BuildSettings.Scope(target.underlyingTarget.buildSettings, boundCondition: (currentPlatform, configuration))
return BuildSettings.Scope(target.underlyingTarget.buildSettings, environment: buildEnvironment)
}
}

Expand Down Expand Up @@ -155,6 +155,11 @@ public final class ClangTargetBuildDescription {
/// The build parameters.
let buildParameters: BuildParameters

/// The build environment.
var buildEnvironment: BuildEnvironment {
buildParameters.buildEnvironment
}

/// Path to the bundle generated for this module (if any).
var bundlePath: AbsolutePath? {
buildParameters.bundlePath(for: target)
Expand Down Expand Up @@ -1132,6 +1137,11 @@ public class BuildPlan {
/// The build parameters.
public let buildParameters: BuildParameters

/// The build environment.
private var buildEnvironment: BuildEnvironment {
buildParameters.buildEnvironment
}

/// The package graph.
public let graph: PackageGraph

Expand Down Expand Up @@ -1200,10 +1210,11 @@ public class BuildPlan {
let swiftTarget = SwiftTarget(
testDiscoverySrc: src,
name: testProduct.name,
dependencies: testProduct.underlyingProduct.targets)
dependencies: testProduct.underlyingProduct.targets.map { .target($0, conditions: []) }
)
let linuxMainTarget = ResolvedTarget(
target: swiftTarget,
dependencies: testProduct.targets.map(ResolvedTarget.Dependency.target)
dependencies: testProduct.targets.map { .target($0, conditions: []) }
)

let target = try SwiftTargetBuildDescription(
Expand Down Expand Up @@ -1238,7 +1249,7 @@ public class BuildPlan {
for dependency in target.dependencies {
switch dependency {
case .target: break
case .product(let product):
case .product(let product, _):
if buildParameters.triple.isDarwin() {
BuildPlan.validateDeploymentVersionOfProductDependency(
product, forTarget: target, diagnostics: diagnostics)
Expand Down Expand Up @@ -1426,19 +1437,19 @@ public class BuildPlan {
) {

// Sort the product targets in topological order.
let nodes = product.targets.map(ResolvedTarget.Dependency.target)
let nodes: [ResolvedTarget.Dependency] = product.targets.map { .target($0, conditions: []) }
let allTargets = try! topologicalSort(nodes, successors: { dependency in
switch dependency {
// Include all the depenencies of a target.
case .target(let target):
return target.dependencies
case .target(let target, _):
return target.dependencies.filter { $0.satisfies(self.buildEnvironment) }

// For a product dependency, we only include its content only if we
// need to statically link it.
case .product(let product):
case .product(let product, _):
switch product.type {
case .library(.automatic), .library(.static):
return product.targets.map(ResolvedTarget.Dependency.target)
return product.targets.map { .target($0, conditions: []) }
case .library(.dynamic), .test, .executable:
return []
}
Expand All @@ -1453,7 +1464,7 @@ public class BuildPlan {

for dependency in allTargets {
switch dependency {
case .target(let target):
case .target(let target, _):
switch target.type {
// Include executable and tests only if they're top level contents
// of the product. Otherwise they are just build time dependency.
Expand All @@ -1477,7 +1488,7 @@ public class BuildPlan {
}
}

case .product(let product):
case .product(let product, _):
// Add the dynamic products to array of libraries to link.
if product.type == .library(.dynamic) {
linkLibraries.append(product)
Expand All @@ -1496,10 +1507,11 @@ public class BuildPlan {

/// Plan a Clang target.
private func plan(clangTarget: ClangTargetBuildDescription) {
for dependency in clangTarget.target.recursiveDependencies() {
switch dependency.underlyingTarget {
let recursiveBuildTargets = clangTarget.target.recursiveBuildTargetDependencies(in: buildEnvironment)
for targetDependency in recursiveBuildTargets {
switch targetDependency.underlyingTarget {
case is SwiftTarget:
if case let .swift(dependencyTargetDescription)? = targetMap[dependency] {
if case let .swift(dependencyTargetDescription)? = targetMap[targetDependency] {
if let moduleMap = dependencyTargetDescription.moduleMap {
clangTarget.additionalFlags += ["-fmodule-map-file=\(moduleMap.pathString)"]
}
Expand All @@ -1510,7 +1522,7 @@ public class BuildPlan {
clangTarget.additionalFlags += ["-I", target.includeDir.pathString]

// Add the modulemap of the dependency if it has one.
if case let .clang(dependencyTargetDescription)? = targetMap[dependency] {
if case let .clang(dependencyTargetDescription)? = targetMap[targetDependency] {
if let moduleMap = dependencyTargetDescription.moduleMap {
clangTarget.additionalFlags += ["-fmodule-map-file=\(moduleMap.pathString)"]
}
Expand All @@ -1534,10 +1546,13 @@ public class BuildPlan {
private func plan(swiftTarget: SwiftTargetBuildDescription) throws {
// We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target
// depends on.
for dependency in swiftTarget.target.recursiveDependencies() {
switch dependency.underlyingTarget {
let recursiveBuildTargets = swiftTarget.target
.recursiveBuildDependencies(in: buildEnvironment)
.compactMap { $0.target }
for targetDependency in recursiveBuildTargets {
switch targetDependency.underlyingTarget {
case let underlyingTarget as ClangTarget where underlyingTarget.type == .library:
guard case let .clang(target)? = targetMap[dependency] else {
guard case let .clang(target)? = targetMap[targetDependency] else {
fatalError("unexpected clang target \(underlyingTarget)")
}
// Add the path to modulemap of the dependency. Currently we require that all Clang targets have a
Expand Down
13 changes: 7 additions & 6 deletions Sources/Build/ManifestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class LLBuildManifestBuilder {

var buildConfig: String { buildParameters.configuration.dirname }
var buildParameters: BuildParameters { plan.buildParameters }
var buildEnvironment: BuildEnvironment { buildParameters.buildEnvironment }

/// Create a new builder with a build plan.
public init(_ plan: BuildPlan) {
Expand Down Expand Up @@ -268,12 +269,12 @@ extension LLBuildManifestBuilder {
}
}

for dependency in target.target.dependencies {
for dependency in target.target.buildDependencies(in: buildEnvironment) {
switch dependency {
case .target(let target):
case .target(let target, _):
addStaticTargetInputs(target)

case .product(let product):
case .product(let product, _):
switch product.type {
case .executable, .library(.dynamic):
// Establish a dependency on binary of the product.
Expand Down Expand Up @@ -368,12 +369,12 @@ extension LLBuildManifestBuilder {
}
}

for dependency in target.target.dependencies {
for dependency in target.target.buildDependencies(in: buildEnvironment) {
switch dependency {
case .target(let target):
case .target(let target, _):
addStaticTargetInputs(target)

case .product(let product):
case .product(let product, _):
switch product.type {
case .executable, .library(.dynamic):
// Establish a dependency on binary of the product.
Expand Down
4 changes: 2 additions & 2 deletions Sources/PackageDescription/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public struct BuildConfiguration: Encodable {
/// .define("ENABLE_SOMETHING", .when(configuration: .release)),
/// ],
/// linkerSettings: [
/// .linkLibrary("openssl", .when(platforms: [.linux])),
/// .linkedLibrary("openssl", .when(platforms: [.linux])),
/// ]
/// ),
public struct BuildSettingCondition: Encodable {
Expand Down Expand Up @@ -228,7 +228,7 @@ public struct SwiftSetting: Encodable {
/// Use compilation conditions to only compile statements if a certain condition is true.
/// For example, the Swift compiler will only compile the
/// statements inside the `#if` block when `ENABLE_SOMETHING` is defined:
///
///
/// #if ENABLE_SOMETHING
/// ...
/// #endif
Expand Down
10 changes: 7 additions & 3 deletions Sources/PackageDescription/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ extension Target.Dependency: Encodable {
case type
case name
case package
case condition
}

private enum Kind: String, Codable {
Expand All @@ -477,16 +478,19 @@ extension Target.Dependency: Encodable {
}
#else
switch self {
case ._targetItem(let name):
case ._targetItem(let name, let condition):
try container.encode(Kind.target, forKey: .type)
try container.encode(name, forKey: .name)
case ._productItem(let name, let package):
try container.encode(condition, forKey: .condition)
case ._productItem(let name, let package, let condition):
try container.encode(Kind.product, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(package, forKey: .package)
case ._byNameItem(let name):
try container.encode(condition, forKey: .condition)
case ._byNameItem(let name, let condition):
try container.encode(Kind.byName, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(condition, forKey: .condition)
}
#endif
}
Expand Down
77 changes: 58 additions & 19 deletions Sources/PackageDescription/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ public final class Target {
case productItem(name: String, package: String?)
case byNameItem(name: String)
#else
case _targetItem(name: String)
case _productItem(name: String, package: String?)
case _byNameItem(name: String)
case _targetItem(name: String, condition: BuildSettingCondition?)
case _productItem(name: String, package: String?, condition: BuildSettingCondition?)
case _byNameItem(name: String, condition: BuildSettingCondition?)
#endif
}

Expand Down Expand Up @@ -599,11 +599,12 @@ extension Target.Dependency {
///
/// - parameters:
/// - name: The name of the target.
@available(_PackageDescription, obsoleted: 999)
public static func target(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .targetItem(name: name)
#else
return ._targetItem(name: name)
return ._targetItem(name: name, condition: nil)
#endif
}

Expand All @@ -617,36 +618,74 @@ extension Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .productItem(name: name, package: package)
#else
return ._productItem(name: name, package: package)
return ._productItem(name: name, package: package, condition: nil)
#endif
}

/// Creates a dependency on a product from a package dependency.
/// Creates a by-name dependency that resolves to either a target or a product but
/// after the package graph has been loaded.
///
/// - parameters:
/// - name: The name of the product.
/// - package: The name of the package.
@available(_PackageDescription, introduced: 5.2)
public static func product(name: String, package: String) -> Target.Dependency {
/// - name: The name of the dependency, either a target or a product.
@available(_PackageDescription, obsoleted: 999)
public static func byName(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .productItem(name: name, package: package)
return .byNameItem(name: name)
#else
return ._productItem(name: name, package: package)
return ._byNameItem(name: name, condition: nil)
#endif
}

#if !PACKAGE_DESCRIPTION_4
/// Creates a dependency on a product from a package dependency.
///
/// - parameters:
/// - name: The name of the product.
/// - package: The name of the package.
@available(_PackageDescription, introduced: 5.2, obsoleted: 999)
public static func product(
name: String,
package: String
) -> Target.Dependency {
return ._productItem(name: name, package: package, condition: nil)
}

/// Creates a dependency on a target in the same package.
///
/// - parameters:
/// - name: The name of the target.
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 999)
public static func target(name: String, condition: BuildSettingCondition? = nil) -> Target.Dependency {
return ._targetItem(name: name, condition: condition)
}

/// Creates a dependency on a product from a package dependency.
///
/// - parameters:
/// - name: The name of the product.
/// - package: The name of the package.
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 999)
public static func product(
name: String,
package: String,
condition: BuildSettingCondition? = nil
) -> Target.Dependency {
return ._productItem(name: name, package: package, condition: condition)
}

/// Creates a by-name dependency that resolves to either a target or a product but
/// after the package graph has been loaded.
///
/// - parameters:
/// - name: The name of the dependency, either a target or a product.
public static func byName(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .byNameItem(name: name)
#else
return ._byNameItem(name: name)
#endif
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 999)
public static func byName(name: String, condition: BuildSettingCondition? = nil) -> Target.Dependency {
return ._byNameItem(name: name, condition: condition)
}
#endif
}

// MARK: ExpressibleByStringLiteral
Expand All @@ -661,7 +700,7 @@ extension Target.Dependency: ExpressibleByStringLiteral {
#if PACKAGE_DESCRIPTION_4
self = .byNameItem(name: value)
#else
self = ._byNameItem(name: value)
self = ._byNameItem(name: value, condition: nil)
#endif
}
}
Loading