@@ -182,7 +182,6 @@ fileprivate final class ProjectGenerator {
182
182
// group there.
183
183
if ref. kind == . folder {
184
184
guard groups [ path] == nil else {
185
- log. warning ( " Skipping blue folder ' \( path) '; already added " )
186
185
return nil
187
186
}
188
187
}
@@ -211,24 +210,35 @@ fileprivate final class ProjectGenerator {
211
210
}
212
211
213
212
func generateBaseTarget(
214
- _ name: String , at parentPath: RelativePath ? ,
213
+ _ name: String , at parentPath: RelativePath ? , canUseBuildableFolder : Bool ,
215
214
productType: Xcode . Target . ProductType ? , includeInAllTarget: Bool
216
215
) -> Xcode . Target ? {
217
216
guard targets [ name] == nil else {
218
217
log. warning ( " Duplicate target ' \( name) ', skipping " )
219
218
return nil
220
219
}
221
- // Make sure we can create a group for the parent path, otherwise
222
- // this is nested in a folder reference and there's nothing we can do.
223
- if let parentPath, !parentPath. components. isEmpty,
224
- group ( for: repoRelativePath. appending ( parentPath) ) == nil {
225
- return nil
220
+ var buildableFolder : Xcode . FileReference ?
221
+ if let parentPath, !parentPath. components. isEmpty {
222
+ // If we've been asked to use buildable folders, see if we can create
223
+ // a folder reference at the parent path. Otherwise, create a group at
224
+ // the parent path. If we can't create either a folder or group, this is
225
+ // nested in a folder reference and there's nothing we can do.
226
+ if spec. useBuildableFolders && canUseBuildableFolder {
227
+ buildableFolder = getOrCreateRepoRef ( . folder( parentPath) )
228
+ }
229
+ guard buildableFolder != nil ||
230
+ group ( for: repoRelativePath. appending ( parentPath) ) != nil else {
231
+ return nil
232
+ }
226
233
}
227
234
let target = project. addTarget ( productType: productType, name: name)
228
235
targets [ name] = target
229
236
if includeInAllTarget {
230
237
allTarget. addDependency ( on: target)
231
238
}
239
+ if let buildableFolder {
240
+ target. addBuildableFolder ( buildableFolder)
241
+ }
232
242
target. buildSettings. common. ONLY_ACTIVE_ARCH = " YES "
233
243
target. buildSettings. common. USE_HEADERMAP = " NO "
234
244
// The product name needs to be unique across every project we generate
@@ -274,8 +284,12 @@ fileprivate final class ProjectGenerator {
274
284
}
275
285
unbuildableSources += targetInfo. unbuildableSources
276
286
277
- for header in targetInfo. headers {
278
- getOrCreateRepoRef ( . file( header) )
287
+ // Need to defer the addition of headers since the target may want to use
288
+ // a buildable folder.
289
+ defer {
290
+ for header in targetInfo. headers {
291
+ getOrCreateRepoRef ( . file( header) )
292
+ }
279
293
}
280
294
281
295
// If we have no sources, we're done.
@@ -289,8 +303,20 @@ fileprivate final class ProjectGenerator {
289
303
}
290
304
return
291
305
}
306
+ // Can only use buildable folders if there are no unique arguments and no
307
+ // unbuildable sources.
308
+ // TODO: To improve the coverage of buildable folders, we ought to start
309
+ // automatically splitting umbrella Clang targets like 'stdlib', since
310
+ // they always have files with unique args.
311
+ let canUseBuildableFolders =
312
+ try spec. useBuildableFolders && targetInfo. unbuildableSources. isEmpty &&
313
+ targetInfo. sources. allSatisfy {
314
+ try ! buildDir. clangArgs. hasUniqueArgs ( for: $0. path, parent: targetPath)
315
+ }
316
+
292
317
let target = generateBaseTarget (
293
- targetInfo. name, at: targetInfo. parentPath, productType: . staticArchive,
318
+ targetInfo. name, at: targetPath,
319
+ canUseBuildableFolder: canUseBuildableFolders, productType: . staticArchive,
294
320
includeInAllTarget: includeInAllTarget
295
321
)
296
322
guard let target else { return }
@@ -464,7 +490,7 @@ fileprivate final class ProjectGenerator {
464
490
)
465
491
}
466
492
let target = generateBaseTarget (
467
- targetInfo. name, at: nil , productType: nil ,
493
+ targetInfo. name, at: nil , canUseBuildableFolder : false , productType: nil ,
468
494
includeInAllTarget: includeInAllTarget
469
495
)
470
496
guard let target else { return nil }
@@ -505,9 +531,11 @@ fileprivate final class ProjectGenerator {
505
531
guard checkNotExcluded ( buildRule. parentPath, for: " Swift target " ) else {
506
532
return nil
507
533
}
534
+ // Create the target. Swift targets can always use buildable folders
535
+ // since they have a consistent set of arguments.
508
536
let target = generateBaseTarget (
509
- targetInfo. name, at: buildRule. parentPath, productType : . staticArchive ,
510
- includeInAllTarget: includeInAllTarget
537
+ targetInfo. name, at: buildRule. parentPath, canUseBuildableFolder : true ,
538
+ productType : . staticArchive , includeInAllTarget: includeInAllTarget
511
539
)
512
540
guard let target else { return nil }
513
541
0 commit comments