Skip to content

Commit

Permalink
Implementation of the conditional target dependency proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
hartbit committed Feb 4, 2020
1 parent e705518 commit 85a38c1
Show file tree
Hide file tree
Showing 36 changed files with 1,380 additions and 402 deletions.
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

0 comments on commit 85a38c1

Please sign in to comment.