@@ -63,6 +63,26 @@ private func getDefaultToolchain(_ toolchainRegistry: ToolchainRegistry) async -
63
63
return await toolchainRegistry. default
64
64
}
65
65
66
+ fileprivate extension BuildTriple {
67
+ /// A string that can be used to identify the build triple in `ConfiguredTarget.runDestinationID`.
68
+ var id : String {
69
+ switch self {
70
+ case . tools:
71
+ return " tools "
72
+ case . destination:
73
+ return " destination "
74
+ }
75
+ }
76
+ }
77
+
78
+ fileprivate extension ConfiguredTarget {
79
+ init ( _ buildTarget: any SwiftBuildTarget ) {
80
+ self . init ( targetID: buildTarget. name, runDestinationID: buildTarget. buildTriple. id)
81
+ }
82
+
83
+ static let forPackageManifest = ConfiguredTarget ( targetID: " " , runDestinationID: " " )
84
+ }
85
+
66
86
/// Swift Package Manager build system and workspace support.
67
87
///
68
88
/// This class implements the `BuildSystem` interface to provide the build settings for a Swift
@@ -98,13 +118,13 @@ public actor SwiftPMBuildSystem {
98
118
let fileSystem : FileSystem
99
119
private let toolchainRegistry : ToolchainRegistry
100
120
101
- var fileToTarget : [ AbsolutePath : SwiftBuildTarget ] = [ : ]
102
- var sourceDirToTarget : [ AbsolutePath : SwiftBuildTarget ] = [ : ]
121
+ var fileToTargets : [ AbsolutePath : [ SwiftBuildTarget ] ] = [ : ]
122
+ var sourceDirToTargets : [ AbsolutePath : [ SwiftBuildTarget ] ] = [ : ]
103
123
104
- /// Maps target ids (aka. `ConfiguredTarget.targetID`) to their SwiftPM build target as well as an index in their
105
- /// topological sorting. Targets with lower index are more low level, ie. targets with higher indices depend on
106
- /// targets with lower indices.
107
- var targets : [ String : ( index: Int , buildTarget: SwiftBuildTarget ) ] = [ : ]
124
+ /// Maps configured targets ids to their SwiftPM build target as well as an index in their topological sorting.
125
+ ///
126
+ /// Targets with lower index are more low level, ie. targets with higher indices depend on targets with lower indices.
127
+ var targets : [ ConfiguredTarget : ( index: Int , buildTarget: SwiftBuildTarget ) ] = [ : ]
108
128
109
129
/// The URIs for which the delegate has registered for change notifications,
110
130
/// mapped to the language the delegate specified when registering for change notifications.
@@ -292,40 +312,34 @@ extension SwiftPMBuildSystem {
292
312
293
313
self . targets = Dictionary (
294
314
try buildDescription. allTargetsInTopologicalOrder ( in: modulesGraph) . enumerated ( ) . map { ( index, target) in
295
- return ( key: target. name , ( index, target) )
315
+ return ( key: ConfiguredTarget ( target) , value : ( index, target) )
296
316
} ,
297
317
uniquingKeysWith: { first, second in
298
318
logger. fault ( " Found two targets with the same name \( first. buildTarget. name) " )
299
319
return second
300
320
}
301
321
)
302
322
303
- self . fileToTarget = [ AbsolutePath: SwiftBuildTarget] (
323
+ self . fileToTargets = [ AbsolutePath: [ SwiftBuildTarget] ] (
304
324
modulesGraph. allTargets. flatMap { target in
305
- return target. sources. paths. compactMap {
325
+ return target. sources. paths. compactMap { ( filePath ) -> ( key : AbsolutePath , value : [ SwiftBuildTarget ] ) ? in
306
326
guard let buildTarget = buildDescription. getBuildTarget ( for: target, in: modulesGraph) else {
307
327
return nil
308
328
}
309
- return ( key: $0 , value: buildTarget)
329
+ return ( key: filePath , value: [ buildTarget] )
310
330
}
311
331
} ,
312
- uniquingKeysWith: { td, _ in
313
- // FIXME: is there a preferred target?
314
- return td
315
- }
332
+ uniquingKeysWith: { $0 + $1 }
316
333
)
317
334
318
- self . sourceDirToTarget = [ AbsolutePath: SwiftBuildTarget] (
319
- modulesGraph. allTargets. compactMap { ( target) -> ( AbsolutePath , SwiftBuildTarget ) ? in
335
+ self . sourceDirToTargets = [ AbsolutePath: [ SwiftBuildTarget] ] (
336
+ modulesGraph. allTargets. compactMap { ( target) -> ( AbsolutePath , [ SwiftBuildTarget ] ) ? in
320
337
guard let buildTarget = buildDescription. getBuildTarget ( for: target, in: modulesGraph) else {
321
338
return nil
322
339
}
323
- return ( key: target. sources. root, value: buildTarget)
340
+ return ( key: target. sources. root, value: [ buildTarget] )
324
341
} ,
325
- uniquingKeysWith: { td, _ in
326
- // FIXME: is there a preferred target?
327
- return td
328
- }
342
+ uniquingKeysWith: { $0 + $1 }
329
343
)
330
344
331
345
guard let delegate = self . delegate else {
@@ -365,11 +379,11 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
365
379
return nil
366
380
}
367
381
368
- if configuredTarget. targetID == " " {
382
+ if configuredTarget == . forPackageManifest {
369
383
return try settings ( forPackageManifest: path)
370
384
}
371
385
372
- guard let buildTarget = self . targets [ configuredTarget. targetID ] ? . buildTarget else {
386
+ guard let buildTarget = self . targets [ configuredTarget] ? . buildTarget else {
373
387
logger. error ( " Did not find target with name \( configuredTarget. targetID) " )
374
388
return nil
375
389
}
@@ -399,27 +413,29 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
399
413
return [ ]
400
414
}
401
415
402
- if let target = try ? buildTarget ( for: path) {
403
- return [ ConfiguredTarget ( targetID : target . name , runDestinationID : " dummy " ) ]
416
+ if let targets = orLog ( " Getting build targets for file " , { try buildTargets ( for: path) } ) , !targets . isEmpty {
417
+ return targets . map ( ConfiguredTarget . init )
404
418
}
405
419
406
420
if path. basename == " Package.swift " {
407
421
// We use an empty target name to represent the package manifest since an empty target name is not valid for any
408
422
// user-defined target.
409
- return [ ConfiguredTarget ( targetID : " " , runDestinationID : " dummy " ) ]
423
+ return [ ConfiguredTarget . forPackageManifest ]
410
424
}
411
425
412
- if url. pathExtension == " h " , let target = try ? target ( forHeader: path) {
413
- return [ target]
426
+ if url. pathExtension == " h " , let targets = orLog ( " Getting targets for header " , { try targets ( forHeader: path) } ) ,
427
+ !targets. isEmpty
428
+ {
429
+ return targets
414
430
}
415
431
416
432
return [ ]
417
433
}
418
434
419
435
public func topologicalSort( of targets: [ ConfiguredTarget ] ) -> [ ConfiguredTarget ] ? {
420
436
return targets. sorted { ( lhs: ConfiguredTarget , rhs: ConfiguredTarget ) -> Bool in
421
- let lhsIndex = self . targets [ lhs. targetID ] ? . index ?? self . targets. count
422
- let rhsIndex = self . targets [ lhs. targetID ] ? . index ?? self . targets. count
437
+ let lhsIndex = self . targets [ lhs] ? . index ?? self . targets. count
438
+ let rhsIndex = self . targets [ lhs] ? . index ?? self . targets. count
423
439
return lhsIndex < rhsIndex
424
440
}
425
441
}
@@ -494,19 +510,19 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
494
510
self . watchedFiles. remove ( uri)
495
511
}
496
512
497
- /// Returns the resolved target description for the given file, if one is known .
498
- private func buildTarget ( for file: AbsolutePath ) throws -> SwiftBuildTarget ? {
499
- if let td = fileToTarget [ file] {
500
- return td
513
+ /// Returns the resolved target descriptions for the given file.
514
+ private func buildTargets ( for file: AbsolutePath ) throws -> [ SwiftBuildTarget ] {
515
+ if let targets = fileToTargets [ file] {
516
+ return targets
501
517
}
502
518
503
519
let realpath = try resolveSymlinks ( file)
504
- if realpath != file, let td = fileToTarget [ realpath] {
505
- fileToTarget [ file] = td
506
- return td
520
+ if realpath != file, let targets = fileToTargets [ realpath] {
521
+ fileToTargets [ file] = targets
522
+ return targets
507
523
}
508
524
509
- return nil
525
+ return [ ]
510
526
}
511
527
512
528
/// An event is relevant if it modifies a file that matches one of the file rules used by the SwiftPM workspace.
@@ -547,11 +563,11 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
547
563
guard let url = event. uri. fileURL,
548
564
url. pathExtension == " swift " ,
549
565
let absolutePath = try ? AbsolutePath ( validating: url. path) ,
550
- let target = fileToTarget [ absolutePath]
566
+ let targets = fileToTargets [ absolutePath]
551
567
else {
552
568
continue
553
569
}
554
- filesWithUpdatedDependencies. formUnion ( target . sources. map { DocumentURI ( $0 ) } )
570
+ filesWithUpdatedDependencies. formUnion ( targets . flatMap ( \ . sources) . map ( DocumentURI . init ) )
555
571
}
556
572
557
573
// If a `.swiftmodule` file is updated, this means that we have performed a build / are
@@ -564,7 +580,7 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
564
580
// All of this shouldn't be necessary once we have background preparation, in which case we know when preparation of
565
581
// a target has finished.
566
582
if events. contains ( where: { $0. uri. fileURL? . pathExtension == " swiftmodule " } ) {
567
- filesWithUpdatedDependencies. formUnion ( self . fileToTarget . keys. map { DocumentURI ( $0. asURL) } )
583
+ filesWithUpdatedDependencies. formUnion ( self . fileToTargets . keys. map { DocumentURI ( $0. asURL) } )
568
584
}
569
585
await self . fileDependenciesUpdatedDebouncer. scheduleCall ( filesWithUpdatedDependencies)
570
586
}
@@ -577,12 +593,12 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
577
593
}
578
594
579
595
public func sourceFiles( ) -> [ SourceFileInfo ] {
580
- return fileToTarget . compactMap { ( path, target ) -> SourceFileInfo ? in
596
+ return fileToTargets . compactMap { ( path, targets ) -> SourceFileInfo ? in
581
597
// We should only set mayContainTests to `true` for files from test targets
582
598
// (https://github.com/apple/sourcekit-lsp/issues/1174).
583
599
return SourceFileInfo (
584
600
uri: DocumentURI ( path. asURL) ,
585
- isPartOfRootProject: target . isPartOfRootPackage,
601
+ isPartOfRootProject: targets . contains ( where : \ . isPartOfRootPackage) ,
586
602
mayContainTests: true
587
603
)
588
604
}
@@ -615,25 +631,26 @@ extension SwiftPMBuildSystem {
615
631
return canonicalPath == path ? nil : impl ( canonicalPath)
616
632
}
617
633
618
- /// This finds the target the header belongs to based on its location in the file system.
619
- private func target ( forHeader path: AbsolutePath ) throws -> ConfiguredTarget ? {
620
- func impl( _ path: AbsolutePath ) throws -> ConfiguredTarget ? {
634
+ /// This finds the targets the header belongs to based on its location in the file system.
635
+ private func targets ( forHeader path: AbsolutePath ) throws -> [ ConfiguredTarget ] {
636
+ func impl( _ path: AbsolutePath ) throws -> [ ConfiguredTarget ] {
621
637
var dir = path. parentDirectory
622
638
while !dir. isRoot {
623
- if let buildTarget = sourceDirToTarget [ dir] {
624
- return ConfiguredTarget ( targetID : buildTarget . name , runDestinationID : " dummy " )
639
+ if let buildTargets = sourceDirToTargets [ dir] , !buildTargets . isEmpty {
640
+ return buildTargets . map ( ConfiguredTarget . init )
625
641
}
626
642
dir = dir. parentDirectory
627
643
}
628
- return nil
644
+ return [ ]
629
645
}
630
646
631
- if let result = try impl ( path) {
647
+ let result = try impl ( path)
648
+ if !result. isEmpty {
632
649
return result
633
650
}
634
651
635
652
let canonicalPath = try resolveSymlinks ( path)
636
- return try canonicalPath == path ? nil : impl ( canonicalPath)
653
+ return try canonicalPath == path ? [ ] : impl ( canonicalPath)
637
654
}
638
655
}
639
656
0 commit comments