Skip to content

Commit 4666d3f

Browse files
committed
Fix an issue that caused the wrong default toolchain to get selected
A previous commit in this PR changed the way the default toolchain was computed. That commit had a bug that it didn’t prefer the toolchain discovered from `SOURCEKIT_TOOLCHAIN_PATH` and relative to the `sourcekit-lsp` executable over the Xcode default toolchain. To fix that, record the reason why we found a toolchain and use that to prefer those toolchains over the Xcode default toolchain.
1 parent 6505ec8 commit 4666d3f

File tree

3 files changed

+70
-46
lines changed

3 files changed

+70
-46
lines changed

Sources/SKCore/ToolchainRegistry.swift

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,32 @@ import var TSCBasic.localFileSystem
2828
/// ToolchainRegistry is usually initialized by performing a search of predetermined paths,
2929
/// e.g. `ToolchainRegistry(searchPaths: ToolchainRegistry.defaultSearchPaths)`.
3030
public final actor ToolchainRegistry {
31+
/// The reason why a toolchain got added to the registry.
32+
///
33+
/// Used to determine the default toolchain. For example, a toolchain discoverd by the `SOURCEKIT_TOOLCHAIN_PATH`
34+
/// environment variable always takes precedence.
35+
private enum ToolchainRegisterReason: Comparable {
36+
/// The toolchain was found because of the `SOURCEKIT_TOOLCHAIN_PATH` environment variable (or equivalent if
37+
/// overridden in `ToolchainRegistry.init`).
38+
case sourcekitToolchainEnvironmentVariable
39+
40+
/// The toolchain was found relative to the location where sourcekit-lsp is installed.
41+
case relativeToInstallPath
42+
43+
/// The toolchain was found in an Xcode installation
44+
case xcode
45+
46+
/// The toolchain was found relative to the `SOURCEKIT_PATH` or `PATH` environment variables.
47+
case pathEnvironmentVariable
48+
}
49+
50+
/// The toolchains and the reasons why they were added to the registry.s
51+
private let toolchainsAndReasons: [(toolchain: Toolchain, reason: ToolchainRegisterReason)]
3152

3253
/// The toolchains, in the order they were registered.
33-
public let toolchains: [Toolchain]
54+
public var toolchains: [Toolchain] {
55+
return toolchainsAndReasons.map(\.toolchain)
56+
}
3457

3558
/// The toolchains indexed by their identifier.
3659
///
@@ -45,21 +68,31 @@ public final actor ToolchainRegistry {
4568
/// The currently selected toolchain identifier on Darwin.
4669
public let darwinToolchainOverride: String?
4770

71+
/// Create a toolchain registry with a pre-defined list of toolchains.
72+
///
73+
/// For testing purposes.
74+
@_spi(Testing)
75+
public init(toolchains: [Toolchain]) {
76+
self.init(
77+
toolchainsAndReasons: toolchains.map { ($0, .xcode) },
78+
darwinToolchainOverride: nil
79+
)
80+
}
81+
4882
/// Creates a toolchain registry from a list of toolchains.
4983
///
5084
/// - Parameters:
51-
/// - toolchains: The toolchains that should be stored in the registry.
85+
/// - toolchainsAndReasons: The toolchains that should be stored in the registry and why they should be added.
5286
/// - darwinToolchainOverride: The contents of the `TOOLCHAINS` environment
5387
/// variable, which picks the default toolchain.
54-
@_spi(Testing)
55-
public init(
56-
toolchains toolchainsParam: [Toolchain],
88+
private init(
89+
toolchainsAndReasons toolchainsAndReasonsParam: [(toolchain: Toolchain, reason: ToolchainRegisterReason)],
5790
darwinToolchainOverride: String?
5891
) {
59-
var toolchains: [Toolchain] = []
92+
var toolchainsAndReasons: [(toolchain: Toolchain, reason: ToolchainRegisterReason)] = []
6093
var toolchainsByIdentifier: [String: [Toolchain]] = [:]
6194
var toolchainsByPath: [AbsolutePath: Toolchain] = [:]
62-
for toolchain in toolchainsParam {
95+
for (toolchain, reason) in toolchainsAndReasonsParam {
6396
// Non-XcodeDefault toolchain: disallow all duplicates.
6497
if toolchain.identifier != ToolchainRegistry.darwinDefaultToolchainIdentifier {
6598
guard toolchainsByIdentifier[toolchain.identifier] == nil else {
@@ -76,10 +109,10 @@ public final actor ToolchainRegistry {
76109
}
77110

78111
toolchainsByIdentifier[toolchain.identifier, default: []].append(toolchain)
79-
toolchains.append(toolchain)
112+
toolchainsAndReasons.append((toolchain, reason))
80113
}
81114

82-
self.toolchains = toolchains
115+
self.toolchainsAndReasons = toolchainsAndReasons
83116
self.toolchainsByIdentifier = toolchainsByIdentifier
84117
self.toolchainsByPath = toolchainsByPath
85118

@@ -115,18 +148,18 @@ public final actor ToolchainRegistry {
115148
_ fileSystem: FileSystem = localFileSystem
116149
) {
117150
// The paths at which we have found toolchains
118-
var toolchainPaths: [AbsolutePath] = []
151+
var toolchainPaths: [(path: AbsolutePath, reason: ToolchainRegisterReason)] = []
119152

120153
// Scan for toolchains in the paths given by `environmentVariables`.
121154
for envVar in environmentVariables {
122155
if let pathStr = ProcessEnv.block[envVar], let path = try? AbsolutePath(validating: pathStr) {
123-
toolchainPaths.append(path)
156+
toolchainPaths.append((path, .sourcekitToolchainEnvironmentVariable))
124157
}
125158
}
126159

127160
// Search for toolchains relative to the path at which sourcekit-lsp is installed.
128161
if let installPath = installPath {
129-
toolchainPaths.append(installPath)
162+
toolchainPaths.append((installPath, .relativeToInstallPath))
130163
}
131164

132165
// Search for toolchains in the Xcode developer directories and global toolchain install paths
@@ -149,22 +182,25 @@ public final actor ToolchainRegistry {
149182
for name in direntries {
150183
let path = xctoolchainSearchPath.appending(component: name)
151184
if path.extension == "xctoolchain" {
152-
toolchainPaths.append(path)
185+
toolchainPaths.append((path, .xcode))
153186
}
154187
}
155188
}
156189

157190
// Scan for toolchains by the given PATH-like environment variables.
158191
for envVar: ProcessEnvironmentKey in ["SOURCEKIT_PATH", "PATH", "Path"] {
159192
for path in getEnvSearchPaths(pathString: ProcessEnv.block[envVar], currentWorkingDirectory: nil) {
160-
toolchainPaths.append(path)
193+
toolchainPaths.append((path, .pathEnvironmentVariable))
161194
}
162195
}
163196

164-
self.init(
165-
toolchains: toolchainPaths.compactMap { Toolchain($0, fileSystem) },
166-
darwinToolchainOverride: darwinToolchainOverride
167-
)
197+
let toolchainsAndReasons = toolchainPaths.compactMap {
198+
if let toolchain = Toolchain($0.path, fileSystem) {
199+
return (toolchain, $0.reason)
200+
}
201+
return nil
202+
}
203+
self.init(toolchainsAndReasons: toolchainsAndReasons, darwinToolchainOverride: darwinToolchainOverride)
168204
}
169205

170206
/// The default toolchain.
@@ -176,11 +212,17 @@ public final actor ToolchainRegistry {
176212
/// The default toolchain must be only of the registered toolchains.
177213
public var `default`: Toolchain? {
178214
get {
215+
// Toolchains discovered from the `SOURCEKIT_TOOLCHAIN_PATH` environment variable or relative to sourcekit-lsp's
216+
// install path always take precedence over Xcode toolchains.
217+
if let (toolchain, reason) = toolchainsAndReasons.first, reason < .xcode {
218+
return toolchain
219+
}
220+
// Try finding the Xcode default toolchain.
179221
if let tc = toolchainsByIdentifier[darwinToolchainIdentifier]?.first {
180222
return tc
181-
} else {
182-
return toolchains.first
183223
}
224+
// If neither of that worked, pick the first toolchain.
225+
return toolchains.first
184226
}
185227
}
186228

Tests/SKCoreTests/ToolchainRegistryTests.swift

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ import enum PackageLoading.Platform
2121

2222
final class ToolchainRegistryTests: XCTestCase {
2323
func testDefaultSingleToolchain() async throws {
24-
let tr = ToolchainRegistry(
25-
toolchains: [Toolchain(identifier: "a", displayName: "a", path: nil)],
26-
darwinToolchainOverride: nil
27-
)
24+
let tr = ToolchainRegistry(toolchains: [Toolchain(identifier: "a", displayName: "a", path: nil)])
2825
await assertEqual(tr.default?.identifier, "a")
2926
}
3027

@@ -33,8 +30,7 @@ final class ToolchainRegistryTests: XCTestCase {
3330
toolchains: [
3431
Toolchain(identifier: "a", displayName: "a", path: nil),
3532
Toolchain(identifier: "b", displayName: "b", path: nil),
36-
],
37-
darwinToolchainOverride: nil
33+
]
3834
)
3935
await assertEqual(tr.default?.identifier, "a")
4036
await assertTrue(tr.default === tr.toolchain(identifier: "a"))
@@ -49,8 +45,7 @@ final class ToolchainRegistryTests: XCTestCase {
4945
toolchains: [
5046
Toolchain(identifier: "a", displayName: "a", path: nil),
5147
Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, displayName: "a", path: nil),
52-
],
53-
darwinToolchainOverride: nil
48+
]
5449
)
5550
await assertEqual(tr.default?.identifier, ToolchainRegistry.darwinDefaultToolchainIdentifier)
5651
}
@@ -450,12 +445,7 @@ final class ToolchainRegistryTests: XCTestCase {
450445
XCTAssertNotNil(t2.clangd)
451446
XCTAssertNotNil(t2.swiftc)
452447

453-
let tr = ToolchainRegistry(
454-
toolchains: [
455-
Toolchain(path.parentDirectory, fs)!
456-
],
457-
darwinToolchainOverride: nil
458-
)
448+
let tr = ToolchainRegistry(toolchains: [Toolchain(path.parentDirectory, fs)!])
459449
let t3 = try await unwrap(tr.toolchain(identifier: t2.identifier))
460450
XCTAssertEqual(t3.sourcekitd, t2.sourcekitd)
461451
XCTAssertEqual(t3.clang, t2.clang)
@@ -495,12 +485,7 @@ final class ToolchainRegistryTests: XCTestCase {
495485

496486
func testDuplicateToolchainOnlyRegisteredOnce() async throws {
497487
let toolchain = Toolchain(identifier: "a", displayName: "a", path: nil)
498-
let tr = ToolchainRegistry(
499-
toolchains: [
500-
toolchain, toolchain,
501-
],
502-
darwinToolchainOverride: nil
503-
)
488+
let tr = ToolchainRegistry(toolchains: [toolchain, toolchain])
504489
assertEqual(await tr.toolchains.count, 1)
505490
}
506491

@@ -509,10 +494,7 @@ final class ToolchainRegistryTests: XCTestCase {
509494
let first = Toolchain(identifier: "a", displayName: "a", path: path)
510495
let second = Toolchain(identifier: "b", displayName: "b", path: path)
511496

512-
let tr = ToolchainRegistry(
513-
toolchains: [first, second],
514-
darwinToolchainOverride: nil
515-
)
497+
let tr = ToolchainRegistry(toolchains: [first, second])
516498
assertEqual(await tr.toolchains.count, 1)
517499
}
518500

@@ -529,7 +511,7 @@ final class ToolchainRegistryTests: XCTestCase {
529511
displayName: "b",
530512
path: pathB
531513
)
532-
let tr = ToolchainRegistry(toolchains: [xcodeA, xcodeB], darwinToolchainOverride: nil)
514+
let tr = ToolchainRegistry(toolchains: [xcodeA, xcodeB])
533515
await assertTrue(tr.toolchain(path: pathA) === xcodeA)
534516
await assertTrue(tr.toolchain(path: pathB) === xcodeB)
535517

Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
9797
await assertThrowsError(
9898
try await SwiftPMWorkspace(
9999
workspacePath: packageRoot,
100-
toolchainRegistry: ToolchainRegistry(toolchains: [], darwinToolchainOverride: nil),
100+
toolchainRegistry: ToolchainRegistry(toolchains: []),
101101
fileSystem: fs,
102102
buildSetup: SourceKitServer.Options.testDefault.buildSetup
103103
)

0 commit comments

Comments
 (0)