Skip to content

Commit 430fdef

Browse files
committed
Use BuildDescription.traverseModules to get the target targets of a SwiftPM package
1 parent e43740a commit 430fdef

File tree

2 files changed

+51
-118
lines changed

2 files changed

+51
-118
lines changed

Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift

Lines changed: 49 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,8 +656,8 @@ final class SwiftPMBuildSystemTests: XCTestCase {
656656
.compilerArguments
657657
XCTAssertNotNil(argsManifest)
658658

659-
assertArgumentsDoNotContain(manifest.pathString, arguments: argsManifest ?? [])
660-
assertArgumentsContain(try resolveSymlinks(manifest).pathString, arguments: argsManifest ?? [])
659+
assertArgumentsContain(manifest.pathString, arguments: argsManifest ?? [])
660+
assertArgumentsDoNotContain(try resolveSymlinks(manifest).pathString, arguments: argsManifest ?? [])
661661
}
662662
}
663663

0 commit comments

Comments
 (0)