Skip to content

Commit 6ab5ef1

Browse files
committed
Implementation of the conditional target dependency proposal
1 parent b443bf0 commit 6ab5ef1

22 files changed

+1052
-245
lines changed

Sources/Build/BuildParameters.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public struct BuildParameters: Encodable {
8181
/// module to finish building.
8282
public var emitSwiftModuleSeparately: Bool
8383

84+
/// The current build environment.
85+
public var buildEnvironment: BuildEnvironment {
86+
BuildEnvironment(platform: currentPlatform, configuration: configuration)
87+
}
88+
8489
public init(
8590
dataPath: AbsolutePath,
8691
configuration: BuildConfiguration,

Sources/Build/BuildPlan.swift

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension BuildParameters {
7676
}
7777

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

8989
/// Returns the scoped view of build settings for a given target.
9090
fileprivate func createScope(for target: ResolvedTarget) -> BuildSettings.Scope {
91-
return BuildSettings.Scope(
92-
target.underlyingTarget.buildSettings,
93-
environment: BuildEnvironment(platform: currentPlatform, configuration: configuration)
94-
)
91+
return BuildSettings.Scope(target.underlyingTarget.buildSettings, environment: buildEnvironment)
9592
}
9693
}
9794

@@ -148,6 +145,10 @@ public final class ClangTargetBuildDescription {
148145
/// The build parameters.
149146
let buildParameters: BuildParameters
150147

148+
var buildEnvironment: BuildEnvironment {
149+
buildParameters.buildEnvironment
150+
}
151+
151152
/// Path to the bundle generated for this module (if any).
152153
var bundlePath: AbsolutePath? {
153154
buildParameters.bundlePath(for: target)
@@ -1039,6 +1040,10 @@ public class BuildPlan {
10391040
/// The build parameters.
10401041
public let buildParameters: BuildParameters
10411042

1043+
private var buildEnvironment: BuildEnvironment {
1044+
buildParameters.buildEnvironment
1045+
}
1046+
10421047
/// The package graph.
10431048
public let graph: PackageGraph
10441049

@@ -1107,10 +1112,10 @@ public class BuildPlan {
11071112
let swiftTarget = SwiftTarget(
11081113
testDiscoverySrc: src,
11091114
name: testProduct.name,
1110-
dependencies: testProduct.underlyingProduct.targets)
1115+
dependencies: testProduct.underlyingProduct.targets.map({ .target($0, conditions: []) }))
11111116
let linuxMainTarget = ResolvedTarget(
11121117
target: swiftTarget,
1113-
dependencies: testProduct.targets.map(ResolvedTarget.Dependency.target)
1118+
dependencies: testProduct.targets.map({ .target($0, conditions: []) })
11141119
)
11151120

11161121
let target = try SwiftTargetBuildDescription(
@@ -1145,7 +1150,7 @@ public class BuildPlan {
11451150
for dependency in target.dependencies {
11461151
switch dependency {
11471152
case .target: break
1148-
case .product(let product):
1153+
case .product(let product, _):
11491154
if buildParameters.triple.isDarwin() {
11501155
BuildPlan.validateDeploymentVersionOfProductDependency(
11511156
product, forTarget: target, diagnostics: diagnostics)
@@ -1320,19 +1325,19 @@ public class BuildPlan {
13201325
) {
13211326

13221327
// Sort the product targets in topological order.
1323-
let nodes = product.targets.map(ResolvedTarget.Dependency.target)
1328+
let nodes: [ResolvedTarget.Dependency] = product.targets.map({ .target($0, conditions: []) })
13241329
let allTargets = try! topologicalSort(nodes, successors: { dependency in
13251330
switch dependency {
13261331
// Include all the depenencies of a target.
1327-
case .target(let target):
1328-
return target.dependencies
1332+
case .target(let target, _):
1333+
return target.dependencies.filter({ $0.satisfies(self.buildEnvironment) })
13291334

13301335
// For a product dependency, we only include its content only if we
13311336
// need to statically link it.
1332-
case .product(let product):
1337+
case .product(let product, _):
13331338
switch product.type {
13341339
case .library(.automatic), .library(.static):
1335-
return product.targets.map(ResolvedTarget.Dependency.target)
1340+
return product.targets.map({ .target($0, conditions: []) })
13361341
case .library(.dynamic), .test, .executable:
13371342
return []
13381343
}
@@ -1346,7 +1351,7 @@ public class BuildPlan {
13461351

13471352
for dependency in allTargets {
13481353
switch dependency {
1349-
case .target(let target):
1354+
case .target(let target, _):
13501355
switch target.type {
13511356
// Include executable and tests only if they're top level contents
13521357
// of the product. Otherwise they are just build time dependency.
@@ -1362,7 +1367,7 @@ public class BuildPlan {
13621367
systemModules.append(target)
13631368
}
13641369

1365-
case .product(let product):
1370+
case .product(let product, _):
13661371
// Add the dynamic products to array of libraries to link.
13671372
if product.type == .library(.dynamic) {
13681373
linkLibraries.append(product)
@@ -1381,10 +1386,11 @@ public class BuildPlan {
13811386

13821387
/// Plan a Clang target.
13831388
private func plan(clangTarget: ClangTargetBuildDescription) {
1384-
for dependency in clangTarget.target.recursiveDependencies() {
1385-
switch dependency.underlyingTarget {
1389+
let recursiveBuildTargets = clangTarget.target.recursiveBuildTargetDependencies(in: buildEnvironment)
1390+
for targetDependency in recursiveBuildTargets {
1391+
switch targetDependency.underlyingTarget {
13861392
case is SwiftTarget:
1387-
if case let .swift(dependencyTargetDescription)? = targetMap[dependency] {
1393+
if case let .swift(dependencyTargetDescription)? = targetMap[targetDependency] {
13881394
if let moduleMap = dependencyTargetDescription.moduleMap {
13891395
clangTarget.additionalFlags += ["-fmodule-map-file=\(moduleMap.pathString)"]
13901396
}
@@ -1395,7 +1401,7 @@ public class BuildPlan {
13951401
clangTarget.additionalFlags += ["-I", target.includeDir.pathString]
13961402

13971403
// Add the modulemap of the dependency if it has one.
1398-
if case let .clang(dependencyTargetDescription)? = targetMap[dependency] {
1404+
if case let .clang(dependencyTargetDescription)? = targetMap[targetDependency] {
13991405
if let moduleMap = dependencyTargetDescription.moduleMap {
14001406
clangTarget.additionalFlags += ["-fmodule-map-file=\(moduleMap.pathString)"]
14011407
}
@@ -1412,10 +1418,13 @@ public class BuildPlan {
14121418
private func plan(swiftTarget: SwiftTargetBuildDescription) throws {
14131419
// We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target
14141420
// depends on.
1415-
for dependency in swiftTarget.target.recursiveDependencies() {
1416-
switch dependency.underlyingTarget {
1421+
let recursiveBuildTargets = swiftTarget.target
1422+
.recursiveBuildDependencies(in: buildEnvironment)
1423+
.compactMap({ $0.target })
1424+
for targetDependency in recursiveBuildTargets {
1425+
switch targetDependency.underlyingTarget {
14171426
case let underlyingTarget as ClangTarget where underlyingTarget.type == .library:
1418-
guard case let .clang(target)? = targetMap[dependency] else {
1427+
guard case let .clang(target)? = targetMap[targetDependency] else {
14191428
fatalError("unexpected clang target \(underlyingTarget)")
14201429
}
14211430
// Add the path to modulemap of the dependency. Currently we require that all Clang targets have a

Sources/Build/ManifestBuilder.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class LLBuildManifestBuilder {
3636

3737
var buildConfig: String { buildParameters.configuration.dirname }
3838
var buildParameters: BuildParameters { plan.buildParameters }
39+
var buildEnvironment: BuildEnvironment { buildParameters.buildEnvironment }
3940

4041
/// Create a new builder with a build plan.
4142
public init(_ plan: BuildPlan) {
@@ -259,12 +260,12 @@ extension LLBuildManifestBuilder {
259260
}
260261
}
261262

262-
for dependency in target.target.dependencies {
263+
for dependency in target.target.buildDependencies(in: buildEnvironment) {
263264
switch dependency {
264-
case .target(let target):
265+
case .target(let target, _):
265266
addStaticTargetInputs(target)
266267

267-
case .product(let product):
268+
case .product(let product, _):
268269
switch product.type {
269270
case .executable, .library(.dynamic):
270271
// Establish a dependency on binary of the product.
@@ -350,12 +351,12 @@ extension LLBuildManifestBuilder {
350351
}
351352
}
352353

353-
for dependency in target.target.dependencies {
354+
for dependency in target.target.buildDependencies(in: buildEnvironment) {
354355
switch dependency {
355-
case .target(let target):
356+
case .target(let target, _):
356357
addStaticTargetInputs(target)
357358

358-
case .product(let product):
359+
case .product(let product, _):
359360
switch product.type {
360361
case .executable, .library(.dynamic):
361362
// Establish a dependency on binary of the product.

Sources/PackageGraph/PackageGraph.swift

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public struct PackageGraph {
4949
return rootPackages.contains(package)
5050
}
5151

52+
private let inputPackages: [ResolvedPackage]
53+
5254
/// Construct a package graph directly.
5355
public init(
5456
rootPackages: [ResolvedPackage],
@@ -57,7 +59,7 @@ public struct PackageGraph {
5759
) {
5860
self.rootPackages = rootPackages
5961
self.requiredDependencies = requiredDependencies
60-
let inputPackages = rootPackages + rootDependencies
62+
self.inputPackages = rootPackages + rootDependencies
6163
self.packages = try! topologicalSort(inputPackages, successors: { $0.dependencies })
6264

6365
allTargets = Set(packages.flatMap({ package -> [ResolvedTarget] in
@@ -80,26 +82,29 @@ public struct PackageGraph {
8082
}
8183
}))
8284

83-
// Compute the input targets.
84-
let inputTargets = inputPackages.flatMap({ $0.targets }).map(ResolvedTarget.Dependency.target)
85-
// Find all the dependencies of the root targets.
86-
let dependencies = try! topologicalSort(inputTargets, successors: { $0.dependencies })
87-
88-
// Separate out the products and targets but maintain their topological order.
89-
var reachableTargets: Set<ResolvedTarget> = []
90-
var reachableProducts = Set(inputPackages.flatMap({ $0.products }))
91-
92-
for dependency in dependencies {
93-
switch dependency {
94-
case .target(let target):
95-
reachableTargets.insert(target)
96-
case .product(let product):
97-
reachableProducts.insert(product)
98-
}
99-
}
85+
// Compute the reachable targets and products.
86+
let inputTargets = inputPackages.lazy.flatMap({ $0.targets })
87+
let inputProducts = inputPackages.lazy.flatMap({ $0.products })
88+
let recursiveDependencies = inputTargets.flatMap({ $0.recursiveDependencies() })
89+
90+
self.reachableTargets = Set(inputTargets).union(recursiveDependencies.compactMap({ $0.target }))
91+
self.reachableProducts = Set(inputProducts).union(recursiveDependencies.compactMap({ $0.product }))
92+
}
93+
94+
public func reachableBuildTargets(in environment: BuildEnvironment) -> Set<ResolvedTarget> {
95+
let inputTargets = inputPackages.lazy.flatMap({ $0.targets })
96+
let recursiveBuildTargetDependencies = inputTargets
97+
.flatMap({ $0.recursiveBuildTargetDependencies(in: environment) })
98+
return Set(inputTargets).union(recursiveBuildTargetDependencies)
99+
}
100100

101-
self.reachableTargets = reachableTargets
102-
self.reachableProducts = reachableProducts
101+
public func reachableBuildProducts(in environment: BuildEnvironment) -> Set<ResolvedProduct> {
102+
let recursiveBuildProductDependencies = inputPackages
103+
.lazy
104+
.flatMap({ $0.targets })
105+
.flatMap({ $0.recursiveBuildDependencies(in: environment) })
106+
.compactMap({ $0.product })
107+
return Set(inputPackages.flatMap({ $0.products })).union(recursiveBuildProductDependencies)
103108
}
104109

105110
/// Computes a map from each executable target in any of the root packages to the corresponding test targets.
@@ -119,7 +124,7 @@ public struct PackageGraph {
119124
for target in rootTargets where target.type == .executable {
120125
// Find all dependencies of this target within its package.
121126
let dependencies = try! topologicalSort(target.dependencies, successors: {
122-
$0.dependencies.compactMap({ $0.target }).map(ResolvedTarget.Dependency.target)
127+
$0.dependencies.compactMap({ $0.target }).map({ .target($0, conditions: []) })
123128
}).compactMap({ $0.target })
124129

125130
// Include the test targets whose dependencies intersect with the

Sources/PackageGraph/PackageGraphLoader.swift

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], _ di
168168
let productDependencies: Set<ResolvedProduct> = Set(package.targets.flatMap({ target in
169169
return target.dependencies.compactMap({ targetDependency in
170170
switch targetDependency {
171-
case .product(let product):
171+
case .product(let product, _):
172172
return product
173173
case .target:
174174
return nil
@@ -242,7 +242,13 @@ private func createResolvedPackages(
242242
// Establish dependencies between the targets. A target can only depend on another target present in the same package.
243243
let targetMap = targetBuilders.spm_createDictionary({ ($0.target, $0) })
244244
for targetBuilder in targetBuilders {
245-
targetBuilder.dependencies += targetBuilder.target.dependencies.map({ targetMap[$0]! })
245+
targetBuilder.dependencies += targetBuilder.target.dependencies.compactMap({ dependency in
246+
if case .target(let target, let conditions) = dependency {
247+
return .target(targetMap[target]!, conditions: conditions)
248+
} else {
249+
return nil
250+
}
251+
})
246252
}
247253

248254
// Create product builders for each product in the package. A product can only contain a target present in the same package.
@@ -308,10 +314,10 @@ private func createResolvedPackages(
308314
foundDuplicateTarget = foundDuplicateTarget || !allTargetNames.insert(targetBuilder.target.name).inserted
309315

310316
// Directly add all the system module dependencies.
311-
targetBuilder.dependencies += implicitSystemTargetDeps
317+
targetBuilder.dependencies += implicitSystemTargetDeps.map({ .target($0, conditions: []) })
312318

313319
// Establish product dependencies.
314-
for productRef in targetBuilder.target.productDependencies {
320+
for case .product(let productRef, let conditions) in targetBuilder.target.dependencies {
315321
// Find the product in this package's dependency products.
316322
guard let product = productDependencyMap[productRef.name] else {
317323
// Only emit a diagnostic if there are no other diagnostics.
@@ -337,7 +343,7 @@ private func createResolvedPackages(
337343
}
338344
}
339345

340-
targetBuilder.productDeps.append(product)
346+
targetBuilder.dependencies.append(.product(product, conditions: conditions))
341347
}
342348
}
343349
}
@@ -410,14 +416,16 @@ private final class ResolvedProductBuilder: ResolvedBuilder<ResolvedProduct> {
410416
/// Builder for resolved target.
411417
private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> {
412418

419+
enum Dependency {
420+
case target(_ target: ResolvedTargetBuilder, conditions: [ManifestCondition])
421+
case product(_ product: ResolvedProductBuilder, conditions: [ManifestCondition])
422+
}
423+
413424
/// The target reference.
414425
let target: Target
415426

416427
/// The target dependencies of this target.
417-
var dependencies: [ResolvedTargetBuilder] = []
418-
419-
/// The product dependencies of this target.
420-
var productDeps: [ResolvedProductBuilder] = []
428+
var dependencies: [Dependency] = []
421429

422430
/// The diagnostics engine.
423431
let diagnostics: DiagnosticsEngine
@@ -441,24 +449,19 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> {
441449
}
442450

443451
override func constructImpl() -> ResolvedTarget {
444-
var deps: [ResolvedTarget.Dependency] = []
445-
for dependency in dependencies {
446-
deps.append(.target(dependency.construct()))
447-
}
448-
for dependency in productDeps {
449-
let product = dependency.construct()
450-
451-
if !dependency.packageBuilder.isAllowedToVendUnsafeProducts {
452-
diagnoseInvalidUseOfUnsafeFlags(product)
452+
let dependencies = self.dependencies.map({ dependency -> ResolvedTarget.Dependency in
453+
switch dependency {
454+
case .target(let targetBuilder, let conditions):
455+
return .target(targetBuilder.construct(), conditions: conditions)
456+
case .product(let productBuilder, let conditions):
457+
let product = productBuilder.construct()
458+
if !productBuilder.packageBuilder.isAllowedToVendUnsafeProducts {
459+
diagnoseInvalidUseOfUnsafeFlags(product)
460+
}
461+
return .product(product, conditions: conditions)
453462
}
454-
455-
deps.append(.product(product))
456-
}
457-
458-
return ResolvedTarget(
459-
target: target,
460-
dependencies: deps
461-
)
463+
})
464+
return ResolvedTarget(target: target, dependencies: dependencies)
462465
}
463466
}
464467

Sources/PackageLoading/ModuleMapGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public struct ModuleMapGenerator {
173173

174174
// If the file exists with the identical contents, we don't need to rewrite it.
175175
// Otherwise, compiler will recompile even if nothing else has changed.
176-
if let contents = try? localFileSystem.readFileContents(file), contents == stream.bytes {
176+
if let contents = try? fileSystem.readFileContents(file), contents == stream.bytes {
177177
return
178178
}
179179
try fileSystem.writeFileContents(file, bytes: stream.bytes)

0 commit comments

Comments
 (0)