@@ -182,15 +182,13 @@ package actor SwiftPMBuildSystem {
182182 /// The entry point via with we can access the `SourceKitLSPAPI` provided by SwiftPM.
183183 private var buildDescription : SourceKitLSPAPI . BuildDescription ?
184184
185- private var modulesGraph : ModulesGraph
185+ /// Maps source and header files to the target that include them.
186+ private var fileToTargets : [ DocumentURI : Set < ConfiguredTarget > ] = [ : ]
186187
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 ) ] = [ : ]
194192
195193 /// Creates a build system using the Swift Package Manager, if this workspace is a package.
196194 ///
@@ -316,12 +314,6 @@ package actor SwiftPMBuildSystem {
316314 flags: buildFlags
317315 )
318316
319- self . modulesGraph = try ModulesGraph (
320- rootPackages: [ ] ,
321- packages: IdentifiableSet ( ) ,
322- dependencies: [ ] ,
323- binaryArtifacts: [ : ]
324- )
325317 self . reloadPackageStatusCallback = reloadPackageStatusCallback
326318
327319 // The debounce duration of 500ms was chosen arbitrarily without scientific research.
@@ -404,45 +396,26 @@ extension SwiftPMBuildSystem {
404396 /// Make sure to execute any throwing statements before setting any
405397 /// properties because otherwise we might end up in an inconsistent state
406398 /// 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- )
418399
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)
426410 }
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+ }
440414
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 ( )
443418 }
444- await delegate. fileBuildSettingsChanged ( self . watchedFiles)
445- await delegate. fileHandlingCapabilityChanged ( )
446419 for testFilesDidChangeCallback in testFilesDidChangeCallbacks {
447420 await testFilesDidChangeCallback ( )
448421 }
@@ -556,7 +529,7 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
556529
557530 let targets = buildTargets ( for: uri)
558531 if !targets. isEmpty {
559- return targets. map ( ConfiguredTarget . init )
532+ return targets. sorted { ( $0 . targetID , $0 . runDestinationID ) < ( $1 . targetID , $1 . runDestinationID ) }
560533 }
561534
562535 if path. basename == " Package.swift "
@@ -567,10 +540,6 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
567540 return [ ConfiguredTarget . forPackageManifest]
568541 }
569542
570- if let targets = try ? inferredTargets ( for: path) {
571- return targets
572- }
573-
574543 return [ ]
575544 }
576545
@@ -580,27 +549,27 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
580549
581550 package func topologicalSort( of targets: [ ConfiguredTarget ] ) -> [ ConfiguredTarget ] ? {
582551 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
586555 }
587556 }
588557
589558 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 ( )
594563 } else {
595564 // 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
597566 }
598567
599568 // Files that occur before the target in the topological sorting don't depend on it.
600569 // Ideally, we should consult the dependency graph here for more accurate dependency analysis instead of relying on
601570 // a flattened list (https://github.com/swiftlang/sourcekit-lsp/issues/1312).
602571 return self . targets. compactMap { ( configuredTarget, value) -> ConfiguredTarget ? in
603- if let minimumTargetIndex , value. index <= minimumTargetIndex {
572+ if let minimumTargetDepth , value. depth >= minimumTargetDepth {
604573 return nil
605574 }
606575 return configuredTarget
@@ -725,7 +694,7 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
725694 }
726695
727696 /// 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 > {
729698 if let targets = fileToTargets [ file] {
730699 return targets
731700 }
@@ -775,7 +744,9 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
775744 guard event. uri. fileURL? . pathExtension == " swift " , let targets = fileToTargets [ event. uri] else {
776745 continue
777746 }
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+ }
779750 }
780751
781752 // If a `.swiftmodule` file is updated, this means that we have performed a build / are
@@ -801,66 +772,28 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuildSystem {
801772 }
802773
803774 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+ }
812785 }
786+ return sourceFiles. values. sorted { $0. uri. pseudoPath < $1. uri. pseudoPath }
813787 }
814788
815789 package func addSourceFilesDidChangeCallback( _ callback: @Sendable @escaping ( ) async -> Void ) async {
816790 testFilesDidChangeCallbacks. append ( callback)
817791 }
818- }
819-
820- extension SwiftPMBuildSystem {
821-
822- // MARK: Implementation details
823792
824793 /// Retrieve settings for a package manifest (Package.swift).
825794 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)
864797 }
865798}
866799
0 commit comments