Skip to content

Commit ae101d2

Browse files
committed
Prefer a toolchain if it is a strict superset of another toolchain
This fixes the issue that you need to set the `PATH` environment variable to have `/usr/bin` as the first entry to test sourcekit-lsp on Linux. rdar://78525669
1 parent a9242f8 commit ae101d2

File tree

4 files changed

+51
-5
lines changed

4 files changed

+51
-5
lines changed

Documentation/Development.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,10 @@ Install the following dependencies of SourceKit-LSP:
4343
* libsqlite3-dev libncurses5-dev python3
4444

4545
```sh
46-
$ export PATH="<path_to_swift_toolchain>/usr/bin:${PATH}"
4746
$ swift package update
4847
$ swift build -Xcxx -I<path_to_swift_toolchain>/usr/lib/swift -Xcxx -I<path_to_swift_toolchain>/usr/lib/swift/Block
4948
```
5049

51-
Setting `PATH` as described above is important even if `<path_to_swift_toolchain>/usr/bin` is already in your `PATH` because `/usr/bin` must be the **first** path to search.
52-
5350
After building, the server will be located at `.build/debug/sourcekit-lsp`, or a similar path, if you passed any custom options to `swift build`. Editors will generally need to be provided with this path in order to run the newly built server - see [Editors](../Editors) for more information about configuration.
5451

5552
SourceKit-LSP is designed to build against the latest SwiftPM, so if you run into any issue make sure you have the most up-to-date dependencies by running `swift package update`.

Sources/SKCore/Toolchain.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,31 @@ public final class Toolchain {
8686
self.sourcekitd = sourcekitd
8787
self.libIndexStore = libIndexStore
8888
}
89+
90+
/// Returns `true` if this toolchain has strictly more tools than `other`.
91+
///
92+
/// ### Examples
93+
/// - A toolchain that contains both `swiftc` and `clangd` is a superset of one that only contains `swiftc`.
94+
/// - A toolchain that contains only `swiftc`, `clangd` is not a superset of a toolchain that contains `swiftc` and
95+
/// `libIndexStore`. These toolchains are not comparable.
96+
/// - Two toolchains that both contain `swiftc` and `clangd` are supersets of each other.
97+
func isSuperset(of other: Toolchain) -> Bool {
98+
func isSuperset(for tool: KeyPath<Toolchain, AbsolutePath?>) -> Bool {
99+
if self[keyPath: tool] == nil && other[keyPath: tool] != nil {
100+
// This toolchain doesn't contain the tool but the other toolchain does. It is not a superset.
101+
return false
102+
} else {
103+
return true
104+
}
105+
}
106+
return isSuperset(for: \.clang) && isSuperset(for: \.swift) && isSuperset(for: \.swiftc)
107+
&& isSuperset(for: \.clangd) && isSuperset(for: \.sourcekitd) && isSuperset(for: \.libIndexStore)
108+
}
109+
110+
/// Same as `isSuperset` but returns `false` if both toolchains have the same set of tools.
111+
func isProperSuperset(of other: Toolchain) -> Bool {
112+
return self.isSuperset(of: other) && !other.isSuperset(of: self)
113+
}
89114
}
90115

91116
extension Toolchain {

Sources/SKCore/ToolchainRegistry.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,13 @@ public final actor ToolchainRegistry {
221221
if let tc = toolchainsByIdentifier[darwinToolchainIdentifier]?.first {
222222
return tc
223223
}
224-
// If neither of that worked, pick the first toolchain.
225-
return toolchains.first
224+
var result: Toolchain? = nil
225+
for toolchain in toolchains {
226+
if result == nil || toolchain.isProperSuperset(of: result!) {
227+
result = toolchain
228+
}
229+
}
230+
return result
226231
}
227232
}
228233

Tests/SKCoreTests/ToolchainRegistryTests.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,25 @@ final class ToolchainRegistryTests: XCTestCase {
556556
// Env variable wins.
557557
await assertEqual(tr.default?.path, try AbsolutePath(validating: "/t2/bin"))
558558
}
559+
560+
func testSupersetToolchains() async throws {
561+
let onlySwiftcToolchain = Toolchain(
562+
identifier: "onlySwiftc",
563+
displayName: "onlySwiftc",
564+
path: try AbsolutePath(validating: "/usr/local"),
565+
swiftc: try AbsolutePath(validating: "/usr/local/bin/swiftc")
566+
)
567+
let swiftcAndSourcekitdToolchain = Toolchain(
568+
identifier: "swiftcAndSourcekitd",
569+
displayName: "swiftcAndSourcekitd",
570+
path: try AbsolutePath(validating: "/usr"),
571+
swiftc: try AbsolutePath(validating: "/usr/bin/swiftc"),
572+
sourcekitd: try AbsolutePath(validating: "/usr/lib/sourcekitd.framework/sourcekitd")
573+
)
574+
575+
let tr = ToolchainRegistry(toolchains: [onlySwiftcToolchain, swiftcAndSourcekitdToolchain])
576+
await assertEqual(tr.default?.identifier, "swiftcAndSourcekitd")
577+
}
559578
}
560579

561580
private func makeXCToolchain(

0 commit comments

Comments
 (0)