Skip to content

Commit 39dce40

Browse files
committed
Improve performance of sourceFilesAndDirectories
`SourceFilesAndDirectoriesKey` contained all source files in the project and computing its hash value was pretty expensive. The key didn’t really provide any value here because the only way it changes is if the build targets change and if that’s the case, we already clear `cachedSourceFilesAndDirectories`, so we can just avoid the hash value computation.
1 parent 29619a6 commit 39dce40

File tree

1 file changed

+26
-32
lines changed

1 file changed

+26
-32
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,21 @@ package struct SourceFileInfo: Sendable {
5454
/// from non-test targets or files that don't actually contain any tests.
5555
package var mayContainTests: Bool
5656

57+
/// Source files returned here fall into two categories:
58+
/// - Buildable source files are files that can be built by the build system and that make sense to background index
59+
/// - Non-buildable source files include eg. the SwiftPM package manifest or header files. We have sufficient
60+
/// compiler arguments for these files to provide semantic editor functionality but we can't build them.
61+
package var isBuildable: Bool
62+
5763
fileprivate func merging(_ other: SourceFileInfo?) -> SourceFileInfo {
5864
guard let other else {
5965
return self
6066
}
6167
return SourceFileInfo(
6268
targets: targets.union(other.targets),
6369
isPartOfRootProject: other.isPartOfRootProject || isPartOfRootProject,
64-
mayContainTests: other.mayContainTests || mayContainTests
70+
mayContainTests: other.mayContainTests || mayContainTests,
71+
isBuildable: other.isBuildable || isBuildable
6572
)
6673
}
6774
}
@@ -327,11 +334,9 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
327334

328335
private var cachedTargetSources = RequestCache<BuildTargetSourcesRequest>()
329336

330-
/// The parameters with which `SourceFilesAndDirectories` can be cached in `cachedSourceFilesAndDirectories`.
331-
private struct SourceFilesAndDirectoriesKey: Hashable {
332-
let includeNonBuildableFiles: Bool
333-
let sourcesItems: [SourcesItem]
334-
}
337+
/// `SourceFilesAndDirectories` is a global property that only gets reset when the build targets change and thus
338+
/// has no real key.
339+
private struct SourceFilesAndDirectoriesKey: Hashable {}
335340

336341
private struct SourceFilesAndDirectories {
337342
/// The source files in the workspace, ie. all `SourceItem`s that have `kind == .file`.
@@ -675,7 +680,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
675680
package func targets(for document: DocumentURI) async -> Set<BuildTargetIdentifier> {
676681
return await orLog("Getting targets for source file") {
677682
var result: Set<BuildTargetIdentifier> = []
678-
let filesAndDirectories = try await sourceFilesAndDirectories(includeNonBuildableFiles: true)
683+
let filesAndDirectories = try await sourceFilesAndDirectories()
679684
if let targets = filesAndDirectories.files[document]?.targets {
680685
result.formUnion(targets)
681686
}
@@ -1032,46 +1037,35 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10321037
///
10331038
/// - SeeAlso: Comment in `sourceFilesAndDirectories` for a definition of what `buildable` means.
10341039
package func buildableSourceFiles() async throws -> [DocumentURI: SourceFileInfo] {
1035-
return try await sourceFilesAndDirectories(includeNonBuildableFiles: false).files
1040+
return try await sourceFilesAndDirectories().files.filter(\.value.isBuildable)
10361041
}
10371042

10381043
/// Get all files and directories that are known to the build system, ie. that are returned by a `buildTarget/sources`
10391044
/// request for any target in the project.
10401045
///
1041-
/// Source files returned here fall into two categories:
1042-
/// - Buildable source files are files that can be built by the build system and that make sense to background index
1043-
/// - Non-buildable source files include eg. the SwiftPM package manifest or header files. We have sufficient
1044-
/// compiler arguments for these files to provide semantic editor functionality but we can't build them.
1045-
///
1046-
/// `includeNonBuildableFiles` determines whether non-buildable files should be included.
1047-
private func sourceFilesAndDirectories(includeNonBuildableFiles: Bool) async throws -> SourceFilesAndDirectories {
1048-
let targets = try await self.buildTargets()
1049-
let sourcesItems = try await self.sourceFiles(in: Set(targets.keys))
1050-
1051-
let key = SourceFilesAndDirectoriesKey(
1052-
includeNonBuildableFiles: includeNonBuildableFiles,
1053-
sourcesItems: sourcesItems
1054-
)
1046+
/// - Important: This method returns both buildable and non-buildable source files. Callers need to check
1047+
/// `SourceFileInfo.isBuildable` if they are only interested in buildable source files.
1048+
private func sourceFilesAndDirectories() async throws -> SourceFilesAndDirectories {
1049+
return try await cachedSourceFilesAndDirectories.get(
1050+
SourceFilesAndDirectoriesKey(),
1051+
isolation: self
1052+
) { key in
1053+
let targets = try await self.buildTargets()
1054+
let sourcesItems = try await self.sourceFiles(in: Set(targets.keys))
10551055

1056-
return try await cachedSourceFilesAndDirectories.get(key, isolation: self) { key in
10571056
var files: [DocumentURI: SourceFileInfo] = [:]
10581057
var directories: [DocumentURI: SourceFileInfo] = [:]
1059-
for sourcesItem in key.sourcesItems {
1058+
for sourcesItem in sourcesItems {
10601059
let target = targets[sourcesItem.target]?.target
10611060
let isPartOfRootProject = !(target?.tags.contains(.dependency) ?? false)
10621061
let mayContainTests = target?.tags.contains(.test) ?? true
1063-
if !key.includeNonBuildableFiles && (target?.tags.contains(.notBuildable) ?? false) {
1064-
continue
1065-
}
1066-
10671062
for sourceItem in sourcesItem.sources {
1068-
if !key.includeNonBuildableFiles && sourceItem.sourceKitData?.isHeader ?? false {
1069-
continue
1070-
}
10711063
let info = SourceFileInfo(
10721064
targets: [sourcesItem.target],
10731065
isPartOfRootProject: isPartOfRootProject,
1074-
mayContainTests: mayContainTests
1066+
mayContainTests: mayContainTests,
1067+
isBuildable: !(target?.tags.contains(.notBuildable) ?? false)
1068+
&& !(sourceItem.sourceKitData?.isHeader ?? false)
10751069
)
10761070
switch sourceItem.kind {
10771071
case .file:

0 commit comments

Comments
 (0)