@@ -182,15 +182,13 @@ package actor SwiftPMBuildSystem {
182
182
/// The entry point via with we can access the `SourceKitLSPAPI` provided by SwiftPM.
183
183
private var buildDescription : SourceKitLSPAPI . BuildDescription ?
184
184
185
- private var modulesGraph : ModulesGraph
185
+ /// Maps source and header files to the target that include them.
186
+ private var fileToTargets : [ DocumentURI : Set < ConfiguredTarget > ] = [ : ]
186
187
187
- private var fileToTargets : [ DocumentURI : [ SwiftBuildTarget ] ] = [ : ]
188
- private var sourceDirToTargets : [ DocumentURI : [ SwiftBuildTarget ] ] = [ : ]
189
-
190
- /// Maps configured targets ids to their SwiftPM build target as well as an index in their topological sorting.
191
- ///
192
- /// Targets with lower index are more low level, ie. targets with higher indices depend on targets with lower indices.
193
- private var targets : [ ConfiguredTarget : ( index: Int , buildTarget: SwiftBuildTarget ) ] = [ : ]
188
+ /// Maps configured targets ids to their SwiftPM build target as well as the depth at which they occur in the build
189
+ /// graph. Top level targets on which no other target depends have a depth of `1`. Targets with dependencies have a
190
+ /// greater depth.
191
+ private var targets : [ ConfiguredTarget : ( buildTarget: SwiftBuildTarget , depth: Int ) ] = [ : ]
194
192
195
193
/// Creates a build system using the Swift Package Manager, if this workspace is a package.
196
194
///
@@ -316,12 +314,6 @@ package actor SwiftPMBuildSystem {
316
314
flags: buildFlags
317
315
)
318
316
319
- self . modulesGraph = try ModulesGraph (
320
- rootPackages: [ ] ,
321
- packages: IdentifiableSet ( ) ,
322
- dependencies: [ ] ,
323
- binaryArtifacts: [ : ]
324
- )
325
317
self . reloadPackageStatusCallback = reloadPackageStatusCallback
326
318
327
319
// The debounce duration of 500ms was chosen arbitrarily without scientific research.
@@ -404,45 +396,26 @@ extension SwiftPMBuildSystem {
404
396
/// Make sure to execute any throwing statements before setting any
405
397
/// properties because otherwise we might end up in an inconsistent state
406
398
/// with only some properties modified.
407
- self . modulesGraph = modulesGraph
408
-
409
- self . targets = Dictionary (
410
- try buildDescription. allTargetsInTopologicalOrder ( in: modulesGraph) . enumerated ( ) . map { ( index, target) in
411
- return ( key: ConfiguredTarget ( target) , value: ( index, target) )
412
- } ,
413
- uniquingKeysWith: { first, second in
414
- logger. fault ( " Found two targets with the same name \( first. buildTarget. name) " )
415
- return second
416
- }
417
- )
418
399
419
- self . fileToTargets = [ DocumentURI: [ SwiftBuildTarget] ] (
420
- modulesGraph. allModules. flatMap { target in
421
- return target. sources. paths. compactMap { ( filePath) -> ( key: DocumentURI , value: [ SwiftBuildTarget ] ) ? in
422
- guard let buildTarget = buildDescription. getBuildTarget ( for: target, in: modulesGraph) else {
423
- return nil
424
- }
425
- return ( key: DocumentURI ( filePath. asURL) , value: [ buildTarget] )
400
+ self . targets = [ : ]
401
+ self . fileToTargets = [ : ]
402
+ buildDescription. traverseModules { buildTarget, parent, depth in
403
+ let configuredTarget = ConfiguredTarget ( buildTarget)
404
+ var depth = depth
405
+ if let existingDepth = targets [ configuredTarget] ? . depth {
406
+ depth = max ( existingDepth, depth)
407
+ } else {
408
+ for source in buildTarget. sources + buildTarget. headers {
409
+ fileToTargets [ DocumentURI ( source) , default: [ ] ] . insert ( configuredTarget)
426
410
}
427
- } ,
428
- uniquingKeysWith: { $0 + $1 }
429
- )
430
-
431
- self . sourceDirToTargets = [ DocumentURI: [ SwiftBuildTarget] ] (
432
- modulesGraph. allModules. compactMap { ( target) -> ( DocumentURI , [ SwiftBuildTarget ] ) ? in
433
- guard let buildTarget = buildDescription. getBuildTarget ( for: target, in: modulesGraph) else {
434
- return nil
435
- }
436
- return ( key: DocumentURI ( target. sources. root. asURL) , value: [ buildTarget] )
437
- } ,
438
- uniquingKeysWith: { $0 + $1 }
439
- )
411
+ }
412
+ targets [ configuredTarget] = ( buildTarget, depth)
413
+ }
440
414
441
- guard let delegate = self . delegate else {
442
- return
415
+ if let delegate = self . delegate {
416
+ await delegate. fileBuildSettingsChanged ( self . watchedFiles)
417
+ await delegate. fileHandlingCapabilityChanged ( )
443
418
}
444
- await delegate. fileBuildSettingsChanged ( self . watchedFiles)
445
- await delegate. fileHandlingCapabilityChanged ( )
446
419
for testFilesDidChangeCallback in testFilesDidChangeCallbacks {
447
420
await testFilesDidChangeCallback ( )
448
421
}
@@ -556,7 +529,7 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
556
529
557
530
let targets = buildTargets ( for: uri)
558
531
if !targets. isEmpty {
559
- return targets. map ( ConfiguredTarget . init )
532
+ return targets. sorted { ( $0 . targetID , $0 . runDestinationID ) < ( $1 . targetID , $1 . runDestinationID ) }
560
533
}
561
534
562
535
if path. basename == " Package.swift "
@@ -567,10 +540,6 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
567
540
return [ ConfiguredTarget . forPackageManifest]
568
541
}
569
542
570
- if let targets = try ? inferredTargets ( for: path) {
571
- return targets
572
- }
573
-
574
543
return [ ]
575
544
}
576
545
@@ -580,27 +549,27 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
580
549
581
550
package func topologicalSort( of targets: [ ConfiguredTarget ] ) -> [ ConfiguredTarget ] ? {
582
551
return targets. sorted { ( lhs: ConfiguredTarget , rhs: ConfiguredTarget ) -> Bool in
583
- let lhsIndex = self . targets [ lhs] ? . index ?? self . targets . count
584
- let rhsIndex = self . targets [ rhs] ? . index ?? self . targets . count
585
- return lhsIndex < rhsIndex
552
+ let lhsDepth = self . targets [ lhs] ? . depth ?? 0
553
+ let rhsDepth = self . targets [ rhs] ? . depth ?? 0
554
+ return lhsDepth > rhsDepth
586
555
}
587
556
}
588
557
589
558
package func targets( dependingOn targets: [ ConfiguredTarget ] ) -> [ ConfiguredTarget ] ? {
590
- let targetIndices = targets. compactMap { self . targets [ $0] ? . index }
591
- let minimumTargetIndex : Int ?
592
- if targetIndices . count == targets. count {
593
- minimumTargetIndex = targetIndices . min ( )
559
+ let targetDepths = targets. compactMap { self . targets [ $0] ? . depth }
560
+ let minimumTargetDepth : Int ?
561
+ if targetDepths . count == targets. count {
562
+ minimumTargetDepth = targetDepths . max ( )
594
563
} else {
595
564
// One of the targets didn't have an entry in self.targets. We don't know what might depend on it.
596
- minimumTargetIndex = nil
565
+ minimumTargetDepth = nil
597
566
}
598
567
599
568
// Files that occur before the target in the topological sorting don't depend on it.
600
569
// Ideally, we should consult the dependency graph here for more accurate dependency analysis instead of relying on
601
570
// a flattened list (https://github.com/swiftlang/sourcekit-lsp/issues/1312).
602
571
return self . targets. compactMap { ( configuredTarget, value) -> ConfiguredTarget ? in
603
- if let minimumTargetIndex , value. index <= minimumTargetIndex {
572
+ if let minimumTargetDepth , value. depth >= minimumTargetDepth {
604
573
return nil
605
574
}
606
575
return configuredTarget
@@ -725,7 +694,7 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
725
694
}
726
695
727
696
/// Returns the resolved target descriptions for the given file, if one is known.
728
- private func buildTargets( for file: DocumentURI ) -> [ SwiftBuildTarget ] {
697
+ private func buildTargets( for file: DocumentURI ) -> Set < ConfiguredTarget > {
729
698
if let targets = fileToTargets [ file] {
730
699
return targets
731
700
}
@@ -775,7 +744,9 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
775
744
guard event. uri. fileURL? . pathExtension == " swift " , let targets = fileToTargets [ event. uri] else {
776
745
continue
777
746
}
778
- filesWithUpdatedDependencies. formUnion ( targets. flatMap ( \. sources) . map ( DocumentURI . init) )
747
+ for target in targets {
748
+ filesWithUpdatedDependencies. formUnion ( self . targets [ target] ? . buildTarget. sources. map ( DocumentURI . init) ?? [ ] )
749
+ }
779
750
}
780
751
781
752
// If a `.swiftmodule` file is updated, this means that we have performed a build / are
@@ -801,66 +772,28 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
801
772
}
802
773
803
774
package func sourceFiles( ) -> [ SourceFileInfo ] {
804
- return fileToTargets. compactMap { ( uri, targets) -> SourceFileInfo ? in
805
- // We should only set mayContainTests to `true` for files from test targets
806
- // (https://github.com/swiftlang/sourcekit-lsp/issues/1174).
807
- return SourceFileInfo (
808
- uri: uri,
809
- isPartOfRootProject: targets. contains ( where: \. isPartOfRootPackage) ,
810
- mayContainTests: true
811
- )
775
+ var sourceFiles : [ DocumentURI : SourceFileInfo ] = [ : ]
776
+ for (buildTarget, depth) in self . targets. values {
777
+ for sourceFile in buildTarget. sources {
778
+ let uri = DocumentURI ( sourceFile)
779
+ sourceFiles [ uri] = SourceFileInfo (
780
+ uri: uri,
781
+ isPartOfRootProject: depth == 1 || ( sourceFiles [ uri] ? . isPartOfRootProject ?? false ) ,
782
+ mayContainTests: true
783
+ )
784
+ }
812
785
}
786
+ return sourceFiles. values. sorted { $0. uri. pseudoPath < $1. uri. pseudoPath }
813
787
}
814
788
815
789
package func addSourceFilesDidChangeCallback( _ callback: @Sendable @escaping ( ) async -> Void ) async {
816
790
testFilesDidChangeCallbacks. append ( callback)
817
791
}
818
- }
819
-
820
- extension SwiftPMBuildSystem {
821
-
822
- // MARK: Implementation details
823
792
824
793
/// Retrieve settings for a package manifest (Package.swift).
825
794
private func settings( forPackageManifest path: AbsolutePath ) throws -> FileBuildSettings ? {
826
- func impl( _ path: AbsolutePath ) -> FileBuildSettings ? {
827
- for package in modulesGraph. packages where path == package . manifest. path {
828
- let compilerArgs = workspace. interpreterFlags ( for: package . path) + [ path. pathString]
829
- return FileBuildSettings ( compilerArguments: compilerArgs)
830
- }
831
- return nil
832
- }
833
-
834
- if let result = impl ( path) {
835
- return result
836
- }
837
-
838
- let canonicalPath = try resolveSymlinks ( path)
839
- return canonicalPath == path ? nil : impl ( canonicalPath)
840
- }
841
-
842
- /// This finds the target a file belongs to based on its location in the file system.
843
- ///
844
- /// This is primarily intended to find the target a header belongs to.
845
- private func inferredTargets( for path: AbsolutePath ) throws -> [ ConfiguredTarget ] {
846
- func impl( _ path: AbsolutePath ) throws -> [ ConfiguredTarget ] {
847
- var dir = path. parentDirectory
848
- while !dir. isRoot {
849
- if let buildTargets = sourceDirToTargets [ DocumentURI ( dir. asURL) ] {
850
- return buildTargets. map ( ConfiguredTarget . init)
851
- }
852
- dir = dir. parentDirectory
853
- }
854
- return [ ]
855
- }
856
-
857
- let result = try impl ( path)
858
- if !result. isEmpty {
859
- return result
860
- }
861
-
862
- let canonicalPath = try resolveSymlinks ( path)
863
- return try canonicalPath == path ? [ ] : impl ( canonicalPath)
795
+ let compilerArgs = workspace. interpreterFlags ( for: path. parentDirectory) + [ path. pathString]
796
+ return FileBuildSettings ( compilerArguments: compilerArgs)
864
797
}
865
798
}
866
799
0 commit comments