11
11
//===----------------------------------------------------------------------===//
12
12
13
13
import struct Basics. AbsolutePath
14
- import struct Basics. Triple
14
+ import func Basics. depthFirstSearch
15
15
import struct Basics. InternalError
16
- import struct PackageGraph . ResolvedProduct
16
+ import struct Basics . Triple
17
17
import struct PackageGraph. ResolvedModule
18
+ import struct PackageGraph. ResolvedProduct
18
19
import class PackageModel. BinaryModule
19
20
import class PackageModel. ClangModule
20
21
@@ -31,10 +32,7 @@ extension BuildPlan {
31
32
/// Plan a product.
32
33
func plan( buildProduct: ProductBuildDescription ) throws {
33
34
// Compute the product's dependency.
34
- let dependencies = try computeDependencies (
35
- of: buildProduct. product,
36
- buildParameters: buildProduct. buildParameters
37
- )
35
+ let dependencies = try computeDependencies ( of: buildProduct)
38
36
39
37
// Add flags for system targets.
40
38
for systemModule in dependencies. systemModules {
@@ -62,8 +60,8 @@ extension BuildPlan {
62
60
if !buildProduct. product. modules. contains ( where: \. underlying. isEmbeddedSwiftTarget) {
63
61
// Link C++ if needed.
64
62
// Note: This will come from build settings in future.
65
- for target in dependencies. staticTargets {
66
- if case let target as ClangModule = target . underlying, target. isCXX {
63
+ for description in dependencies. staticTargets {
64
+ if case let target as ClangModule = description . module . underlying, target. isCXX {
67
65
let triple = buildProduct. buildParameters. triple
68
66
if triple. isDarwin ( ) {
69
67
buildProduct. additionalFlags += [ " -lc++ " ]
@@ -77,12 +75,12 @@ extension BuildPlan {
77
75
}
78
76
}
79
77
80
- for module in dependencies. staticTargets {
81
- switch module. underlying {
78
+ for description in dependencies. staticTargets {
79
+ switch description . module. underlying {
82
80
case is SwiftModule :
83
81
// Swift targets are guaranteed to have a corresponding Swift description.
84
- guard case . swift( let description) = self . description ( for : module , context : buildProduct . destination ) else {
85
- throw InternalError ( " unknown module \( module) " )
82
+ guard case . swift( let description) = description else {
83
+ throw InternalError ( " Expected a Swift module: \( description . module) " )
86
84
}
87
85
88
86
// Based on the debugging strategy, we either need to pass swiftmodule paths to the
@@ -101,176 +99,231 @@ extension BuildPlan {
101
99
}
102
100
}
103
101
104
- buildProduct. staticTargets = dependencies. staticTargets
105
- buildProduct. dylibs = try dependencies. dylibs. map {
106
- guard let product = self . description ( for: $0, context: buildProduct. destination) else {
107
- throw InternalError ( " unknown product \( $0) " )
108
- }
109
- return product
110
- }
111
- buildProduct. objects += try dependencies. staticTargets. flatMap { module -> [ AbsolutePath ] in
112
- guard let description = self . description ( for: module, context: buildProduct. destination) else {
113
- throw InternalError ( " unknown module \( module) " )
114
- }
115
- return try description. objects
116
- }
102
+ buildProduct. staticTargets = dependencies. staticTargets. map ( \. module)
103
+ buildProduct. dylibs = dependencies. dylibs
104
+ buildProduct. objects += try dependencies. staticTargets. flatMap { try $0. objects }
117
105
buildProduct. libraryBinaryPaths = dependencies. libraryBinaryPaths
118
-
119
106
buildProduct. availableTools = dependencies. availableTools
120
107
}
121
108
122
109
/// Computes the dependencies of a product.
123
110
private func computeDependencies(
124
- of product: ResolvedProduct ,
125
- buildParameters: BuildParameters
111
+ of productDescription: ProductBuildDescription
126
112
) throws -> (
127
- dylibs: [ ResolvedProduct ] ,
128
- staticTargets: [ ResolvedModule ] ,
113
+ dylibs: [ ProductBuildDescription ] ,
114
+ staticTargets: [ ModuleBuildDescription ] ,
129
115
systemModules: [ ResolvedModule ] ,
130
116
libraryBinaryPaths: Set < AbsolutePath > ,
131
117
availableTools: [ String : AbsolutePath ]
132
118
) {
119
+ let product = productDescription. product
133
120
/* Prior to tools-version 5.9, we used to erroneously recursively traverse executable/plugin dependencies and statically include their
134
121
targets. For compatibility reasons, we preserve that behavior for older tools-versions. */
135
- let shouldExcludePlugins : Bool
136
- if let toolsVersion = self . graph. package ( for: product) ? . manifest. toolsVersion {
137
- shouldExcludePlugins = toolsVersion >= . v5_9
138
- } else {
139
- shouldExcludePlugins = false
140
- }
122
+ let shouldExcludePlugins = productDescription. package . manifest. toolsVersion >= . v5_9
141
123
142
- // For test targets, we need to consider the first level of transitive dependencies since the first level is always test targets.
143
- let topLevelDependencies : [ PackageModel . Module ]
144
- if product. type == . test {
145
- topLevelDependencies = product. modules. flatMap { $0 . underlying. dependencies } . compactMap {
124
+ // For test targets, we need to consider the first level of transitive dependencies since the first level is
125
+ // always test targets.
126
+ let topLevelDependencies : [ PackageModel . Module ] = if product. type == . test {
127
+ product. modules. flatMap ( \ . underlying. dependencies) . compactMap {
146
128
switch $0 {
147
129
case . product:
148
- return nil
130
+ nil
149
131
case . module( let target, _) :
150
- return target
132
+ target
151
133
}
152
134
}
153
135
} else {
154
- topLevelDependencies = [ ]
136
+ [ ]
155
137
}
156
138
157
139
// get the dynamic libraries for explicitly linking rdar://108561857
158
- func recursiveDynamicLibraries( for product : ResolvedProduct ) throws -> [ ResolvedProduct ] {
159
- let dylibs = try computeDependencies ( of: product , buildParameters : buildParameters ) . dylibs
140
+ func recursiveDynamicLibraries( for description : ProductBuildDescription ) throws -> [ ProductBuildDescription ] {
141
+ let dylibs = try computeDependencies ( of: description ) . dylibs
160
142
return try dylibs + dylibs. flatMap { try recursiveDynamicLibraries ( for: $0) }
161
143
}
162
144
163
145
// Sort the product targets in topological order.
164
- let nodes : [ ResolvedModule . Dependency ] = product. modules. map { . module( $0, conditions: [ ] ) }
165
- let allTargets = try topologicalSort ( nodes, successors: { dependency in
166
- switch dependency {
167
- // Include all the dependencies of a target.
168
- case . module( let target, _) :
169
- let isTopLevel = topLevelDependencies. contains ( target. underlying) || product. modules. contains ( id: target. id)
170
- let topLevelIsMacro = isTopLevel && product. type == . macro
171
- let topLevelIsPlugin = isTopLevel && product. type == . plugin
172
- let topLevelIsTest = isTopLevel && product. type == . test
146
+ var allDependencies : [ ModuleBuildDescription . Dependency ] = [ ]
173
147
174
- if !topLevelIsMacro && !topLevelIsTest && target. type == . macro {
175
- return [ ]
176
- }
177
- if shouldExcludePlugins, !topLevelIsPlugin && !topLevelIsTest && target. type == . plugin {
178
- return [ ]
148
+ do {
149
+ func successors(
150
+ for product: ResolvedProduct ,
151
+ destination: BuildParameters . Destination
152
+ ) throws -> [ TraversalNode ] {
153
+ let productDependencies : [ TraversalNode ] = product. modules. map {
154
+ . init( module: $0, context: destination)
179
155
}
180
- return target. dependencies. filter { $0. satisfies ( buildParameters. buildEnvironment) }
181
156
182
- // For a product dependency, we only include its content only if we
183
- // need to statically link it.
184
- case . product( let product, _) :
185
- guard dependency. satisfies ( buildParameters. buildEnvironment) else {
186
- return [ ]
187
- }
188
-
189
- let productDependencies : [ ResolvedModule . Dependency ] = product. modules. map { . module( $0, conditions: [ ] ) }
190
157
switch product. type {
191
158
case . library( . automatic) , . library( . static) :
192
159
return productDependencies
193
160
case . plugin:
194
161
return shouldExcludePlugins ? [ ] : productDependencies
195
162
case . library( . dynamic) :
196
- return try recursiveDynamicLibraries ( for: product) . map { . product( $0, conditions: [ ] ) }
163
+ guard let description = self . description ( for: product, context: destination) else {
164
+ throw InternalError ( " Could not find a description for product: \( product) " )
165
+ }
166
+ return try recursiveDynamicLibraries ( for: description) . map { TraversalNode (
167
+ product: $0. product,
168
+ context: $0. destination
169
+ ) }
197
170
case . test, . executable, . snippet, . macro:
198
171
return [ ]
199
172
}
200
173
}
201
- } )
174
+
175
+ func successors(
176
+ for module: ResolvedModule ,
177
+ destination: BuildParameters . Destination
178
+ ) -> [ TraversalNode ] {
179
+ let isTopLevel = topLevelDependencies. contains ( module. underlying) || product. modules
180
+ . contains ( id: module. id)
181
+ let topLevelIsMacro = isTopLevel && product. type == . macro
182
+ let topLevelIsPlugin = isTopLevel && product. type == . plugin
183
+ let topLevelIsTest = isTopLevel && product. type == . test
184
+
185
+ if !topLevelIsMacro && !topLevelIsTest && module. type == . macro {
186
+ return [ ]
187
+ }
188
+ if shouldExcludePlugins, !topLevelIsPlugin && !topLevelIsTest && module. type == . plugin {
189
+ return [ ]
190
+ }
191
+ return module. dependencies ( satisfying: productDescription. buildParameters. buildEnvironment)
192
+ . map {
193
+ switch $0 {
194
+ case . product( let product, _) :
195
+ . init( product: product, context: destination)
196
+ case . module( let module, _) :
197
+ . init( module: module, context: destination)
198
+ }
199
+ }
200
+ }
201
+
202
+ let directDependencies = product. modules
203
+ . map { TraversalNode ( module: $0, context: productDescription. destination) }
204
+
205
+ var uniqueNodes = Set < TraversalNode > ( directDependencies)
206
+
207
+ try depthFirstSearch ( directDependencies) {
208
+ let result : [ TraversalNode ] = switch $0 {
209
+ case . product( let product, let destination) :
210
+ try successors ( for: product, destination: destination)
211
+ case . module( let module, let destination) :
212
+ successors ( for: module, destination: destination)
213
+ case . package :
214
+ [ ]
215
+ }
216
+
217
+ return result. filter { uniqueNodes. insert ( $0) . inserted }
218
+ } onNext: { node, _, _ in
219
+ switch node {
220
+ case . package : break
221
+ case . product( let product, let destination) :
222
+ allDependencies. append ( . product( product, self . description ( for: product, context: destination) ) )
223
+ case . module( let module, let destination) :
224
+ allDependencies. append ( . module( module, self . description ( for: module, context: destination) ) )
225
+ }
226
+ }
227
+ }
202
228
203
229
// Create empty arrays to collect our results.
204
- var linkLibraries = [ ResolvedProduct ] ( )
205
- var staticTargets = [ ResolvedModule ] ( )
230
+ var linkLibraries = [ ProductBuildDescription ] ( )
231
+ var staticTargets = [ ModuleBuildDescription ] ( )
206
232
var systemModules = [ ResolvedModule] ( )
207
233
var libraryBinaryPaths : Set < AbsolutePath > = [ ]
208
234
var availableTools = [ String: AbsolutePath] ( )
209
235
210
- for dependency in allTargets {
236
+ for dependency in allDependencies {
211
237
switch dependency {
212
- case . module( let target , _ ) :
213
- switch target . type {
238
+ case . module( let module , let description ) :
239
+ switch module . type {
214
240
// Executable target have historically only been included if they are directly in the product's
215
241
// target list. Otherwise they have always been just build-time dependencies.
216
242
// In tool version .v5_5 or greater, we also include executable modules implemented in Swift in
217
243
// any test products... this is to allow testing of executables. Note that they are also still
218
244
// built as separate products that the test can invoke as subprocesses.
219
245
case . executable, . snippet, . macro:
220
- if product. modules. contains ( id: target. id) {
221
- staticTargets. append ( target)
222
- } else if product. type == . test && ( target. underlying as? SwiftModule ) ? . supportsTestableExecutablesFeature == true {
246
+ if product. modules. contains ( id: module. id) {
247
+ guard let description else {
248
+ throw InternalError ( " Could not find a description for module: \( module) " )
249
+ }
250
+ staticTargets. append ( description)
251
+ } else if product. type == . test && ( module. underlying as? SwiftModule ) ?
252
+ . supportsTestableExecutablesFeature == true
253
+ {
223
254
// Only "top-level" targets should really be considered here, not transitive ones.
224
- let isTopLevel = topLevelDependencies. contains ( target. underlying) || product. modules. contains ( id: target. id)
225
- if let toolsVersion = graph. package ( for: product) ? . manifest. toolsVersion, toolsVersion >= . v5_5, isTopLevel {
226
- staticTargets. append ( target)
255
+ let isTopLevel = topLevelDependencies. contains ( module. underlying) || product. modules
256
+ . contains ( id: module. id)
257
+ if let toolsVersion = graph. package ( for: product) ? . manifest. toolsVersion, toolsVersion >= . v5_5,
258
+ isTopLevel
259
+ {
260
+ guard let description else {
261
+ throw InternalError ( " Could not find a description for module: \( module) " )
262
+ }
263
+ staticTargets. append ( description)
227
264
}
228
265
}
229
266
// Test targets should be included only if they are directly in the product's target list.
230
267
case . test:
231
- if product. modules. contains ( id: target. id) {
232
- staticTargets. append ( target)
268
+ if product. modules. contains ( id: module. id) {
269
+ guard let description else {
270
+ throw InternalError ( " Could not find a description for module: \( module) " )
271
+ }
272
+ staticTargets. append ( description)
233
273
}
234
274
// Library targets should always be included for the same build triple.
235
275
case . library:
236
- if target. buildTriple == product. buildTriple {
237
- staticTargets. append ( target)
276
+ guard let description else {
277
+ throw InternalError ( " Could not find a description for module: \( module) " )
278
+ }
279
+ if description. destination == productDescription. destination {
280
+ staticTargets. append ( description)
238
281
}
239
282
// Add system target to system targets array.
240
283
case . systemModule:
241
- systemModules. append ( target )
284
+ systemModules. append ( module )
242
285
// Add binary to binary paths set.
243
286
case . binary:
244
- guard let binaryTarget = target . underlying as? BinaryModule else {
245
- throw InternalError ( " invalid binary target ' \( target . name) ' " )
287
+ guard let binaryTarget = module . underlying as? BinaryModule else {
288
+ throw InternalError ( " invalid binary target ' \( module . name) ' " )
246
289
}
247
290
switch binaryTarget. kind {
248
291
case . xcframework:
249
- let libraries = try self . parseXCFramework ( for: binaryTarget, triple: buildParameters. triple)
292
+ let libraries = try self . parseXCFramework (
293
+ for: binaryTarget,
294
+ triple: productDescription. buildParameters. triple
295
+ )
250
296
for library in libraries {
251
297
libraryBinaryPaths. insert ( library. libraryPath)
252
298
}
253
299
case . artifactsArchive:
254
- let tools = try self . parseArtifactsArchive ( for: binaryTarget, triple: buildParameters. triple)
255
- tools. forEach { availableTools [ $0. name] = $0. executablePath }
256
- case . unknown:
257
- throw InternalError ( " unknown binary target ' \( target. name) ' type " )
300
+ let tools = try self . parseArtifactsArchive (
301
+ for: binaryTarget, triple: productDescription. buildParameters. triple
302
+ )
303
+ tools. forEach { availableTools [ $0. name] = $0. executablePath }
304
+ case . unknown:
305
+ throw InternalError ( " unknown binary target ' \( module. name) ' type " )
258
306
}
259
307
case . plugin:
260
308
continue
261
309
}
262
310
263
- case . product( let product, _ ) :
311
+ case . product( let product, let description ) :
264
312
// Add the dynamic products to array of libraries to link.
265
313
if product. type == . library( . dynamic) {
266
- linkLibraries. append ( product)
314
+ guard let description else {
315
+ throw InternalError ( " Dynamic library product should have description: \( product) " )
316
+ }
317
+ linkLibraries. append ( description)
267
318
}
268
319
}
269
320
}
270
321
271
322
// Add derived test targets, if necessary
272
323
if product. type == . test, let derivedTestTargets = derivedTestTargetsMap [ product. id] {
273
- staticTargets. append ( contentsOf: derivedTestTargets)
324
+ staticTargets. append ( contentsOf: derivedTestTargets. compactMap {
325
+ self . description ( for: $0, context: productDescription. destination)
326
+ } )
274
327
}
275
328
276
329
return ( linkLibraries, staticTargets, systemModules, libraryBinaryPaths, availableTools)
@@ -280,7 +333,7 @@ extension BuildPlan {
280
333
private func parseArtifactsArchive( for binaryTarget: BinaryModule , triple: Triple ) throws -> [ ExecutableInfo ] {
281
334
try self . externalExecutablesCache. memoize ( key: binaryTarget) {
282
335
let execInfos = try binaryTarget. parseArtifactArchives ( for: triple, fileSystem: self . fileSystem)
283
- return execInfos. filter { !$0. supportedTriples. isEmpty}
336
+ return execInfos. filter { !$0. supportedTriples. isEmpty }
284
337
}
285
338
}
286
339
}
0 commit comments