Skip to content

Commit bf42a68

Browse files
authored
Merge pull request swiftlang#67 from DavidGoldman/master
Allow multiple Xcode toolchains in the registry
2 parents ed5575b + 6c93c92 commit bf42a68

File tree

5 files changed

+110
-14
lines changed

5 files changed

+110
-14
lines changed

Sources/SKCore/FileBuildSettings.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
/// of a BuildSystem query.
1818
public struct FileBuildSettings {
1919

20-
/// The identifier of the toolchain that is preferred for compiling this file, if any.
21-
public var preferredToolchain: String? = nil
20+
/// The Toolchain that is preferred for compiling this file, if any.
21+
public var preferredToolchain: Toolchain? = nil
2222

2323
/// The compiler arguments to use for this file.
2424
public var compilerArguments: [String]
@@ -27,7 +27,7 @@ public struct FileBuildSettings {
2727
public var workingDirectory: String? = nil
2828

2929
public init(
30-
preferredToolchain: String? = nil,
30+
preferredToolchain: Toolchain? = nil,
3131
compilerArguments: [String],
3232
workingDirectory: String? = nil)
3333
{

Sources/SKCore/ToolchainRegistry.swift

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ public final class ToolchainRegistry {
2828
var _toolchains: [Toolchain] = []
2929

3030
/// The toolchains indexed by their identifier. **Must be accessed on `queue`**.
31-
var toolchainIdentifiers: [String: Toolchain] = [:]
31+
/// Multiple toolchains may exist for the XcodeDefault toolchain identifier.
32+
var toolchainsByIdentifier: [String: [Toolchain]] = [:]
33+
34+
/// The toolchains indexed by their path. **Must be accessed on `queue`**.
35+
/// Note: Not all toolchains have a path.
36+
var toolchainsByPath: [AbsolutePath: Toolchain] = [:]
3237

3338
/// The default toolchain. **Must be accessed on `queue`**.
3439
var _default: Toolchain? = nil
@@ -92,7 +97,7 @@ extension ToolchainRegistry {
9297
get {
9398
return queue.sync {
9499
if _default == nil {
95-
if let tc = toolchainIdentifiers[darwinToolchainIdentifier] {
100+
if let tc = toolchainsByIdentifier[darwinToolchainIdentifier]?.first {
96101
_default = tc
97102
} else {
98103
_default = _toolchains.first
@@ -108,8 +113,8 @@ extension ToolchainRegistry {
108113
_default = nil
109114
return
110115
}
111-
precondition(toolchainIdentifiers[toolchain.identifier] === toolchain,
112-
"default toolchain must be registered first")
116+
precondition(_toolchains.contains { $0 === toolchain },
117+
"default toolchain must be registered first")
113118
_default = toolchain
114119
}
115120
}
@@ -132,8 +137,16 @@ extension ToolchainRegistry {
132137
return queue.sync { _toolchains }
133138
}
134139

140+
public func toolchains(identifier: String) -> [Toolchain] {
141+
return queue.sync { toolchainsByIdentifier[identifier] ?? [] }
142+
}
143+
135144
public func toolchain(identifier: String) -> Toolchain? {
136-
return queue.sync { toolchainIdentifiers[identifier] }
145+
return toolchains(identifier: identifier).first
146+
}
147+
148+
public func toolchain(path: AbsolutePath) -> Toolchain? {
149+
return queue.sync { toolchainsByPath[path] }
137150
}
138151
}
139152

@@ -144,6 +157,9 @@ extension ToolchainRegistry {
144157
/// There is already a toolchain with the given identifier.
145158
case duplicateToolchainIdentifier
146159

160+
/// There is already a toolchain with the given path.
161+
case duplicateToolchainPath
162+
147163
/// The toolchain does not exist, or has no tools.
148164
case invalidToolchain
149165
}
@@ -157,10 +173,24 @@ extension ToolchainRegistry {
157173
}
158174

159175
func _registerToolchain(_ toolchain: Toolchain) throws {
160-
guard toolchainIdentifiers[toolchain.identifier] == nil else {
161-
throw Error.duplicateToolchainIdentifier
176+
// Non-XcodeDefault toolchain: disallow all duplicates.
177+
if toolchain.identifier != ToolchainRegistry.darwinDefaultToolchainIdentifier {
178+
guard toolchainsByIdentifier[toolchain.identifier] == nil else {
179+
throw Error.duplicateToolchainIdentifier
180+
}
162181
}
163-
toolchainIdentifiers[toolchain.identifier] = toolchain
182+
183+
// Toolchain should always be unique by path if it is present.
184+
if let path = toolchain.path {
185+
guard toolchainsByPath[path] == nil else {
186+
throw Error.duplicateToolchainPath
187+
}
188+
toolchainsByPath[path] = toolchain
189+
}
190+
191+
var toolchains = toolchainsByIdentifier[toolchain.identifier] ?? []
192+
toolchains.append(toolchain)
193+
toolchainsByIdentifier[toolchain.identifier] = toolchains
164194
_toolchains.append(toolchain)
165195
}
166196

Sources/SourceKit/SourceKitServer.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,7 @@ public final class SourceKitServer: LanguageServer {
121121
}
122122

123123
func toolchain(for url: URL, _ language: Language) -> Toolchain? {
124-
if let id = workspace?.buildSettings.settings(for: url, language)?.preferredToolchain,
125-
let toolchain = toolchainRegistry.toolchain(identifier:id)
126-
{
124+
if let toolchain = workspace?.buildSettings.settings(for: url, language)?.preferredToolchain {
127125
return toolchain
128126
}
129127

Tests/SKCoreTests/ToolchainRegistryTests.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,70 @@ final class ToolchainRegistryTests: XCTestCase {
349349
makeToolchain(binPath: AbsolutePath("/t3/bin"), fs, sourcekitd: true)
350350
XCTAssertNotNil(Toolchain(AbsolutePath("/t3"), fs))
351351
}
352+
353+
func testDuplicateError() {
354+
let tr = ToolchainRegistry()
355+
let toolchain = Toolchain(identifier: "a", displayName: "a", path: nil)
356+
XCTAssertNoThrow(try tr.registerToolchain(toolchain), "Error registering toolchain")
357+
XCTAssertThrowsError(try tr.registerToolchain(toolchain),
358+
"Expected error registering toolchain twice") { e in
359+
guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainIdentifier else {
360+
XCTFail("Expected .duplicateToolchainIdentifier not \(e)")
361+
return
362+
}
363+
}
364+
}
365+
366+
func testDuplicatePathError() {
367+
let tr = ToolchainRegistry()
368+
let path = AbsolutePath("/foo/bar")
369+
let first = Toolchain(identifier: "a", displayName: "a", path: path)
370+
let second = Toolchain(identifier: "b", displayName: "b", path: path)
371+
XCTAssertNoThrow(try tr.registerToolchain(first), "Error registering toolchain")
372+
XCTAssertThrowsError(try tr.registerToolchain(second),
373+
"Expected error registering toolchain twice") { e in
374+
guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainPath else {
375+
XCTFail("Error mismatch: expected duplicateToolchainPath not \(e)")
376+
return
377+
}
378+
}
379+
}
380+
381+
func testDuplicateXcodeError() {
382+
let tr = ToolchainRegistry()
383+
let xcodeToolchain = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier,
384+
displayName: "a",
385+
path: AbsolutePath("/versionA"))
386+
XCTAssertNoThrow(try tr.registerToolchain(xcodeToolchain), "Error registering toolchain")
387+
XCTAssertThrowsError(try tr.registerToolchain(xcodeToolchain),
388+
"Expected error registering toolchain twice") { e in
389+
guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainPath else {
390+
XCTFail("Error mismatch: expected duplicateToolchainPath not \(e)")
391+
return
392+
}
393+
}
394+
}
395+
396+
func testMultipleXcodes() {
397+
let tr = ToolchainRegistry()
398+
let pathA = AbsolutePath("/versionA")
399+
let xcodeA = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier,
400+
displayName: "a",
401+
path: pathA)
402+
let pathB = AbsolutePath("/versionB")
403+
let xcodeB = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier,
404+
displayName: "b",
405+
path: pathB)
406+
XCTAssertNoThrow(try tr.registerToolchain(xcodeA))
407+
XCTAssertNoThrow(try tr.registerToolchain(xcodeB))
408+
XCTAssert(tr.toolchain(path: pathA) === xcodeA)
409+
XCTAssert(tr.toolchain(path: pathB) === xcodeB)
410+
411+
let toolchains = tr.toolchains(identifier: xcodeA.identifier)
412+
XCTAssert(toolchains.count == 2)
413+
XCTAssert(toolchains[0] === xcodeA)
414+
XCTAssert(toolchains[1] === xcodeB)
415+
}
352416
}
353417

354418
#if os(macOS)

Tests/SKCoreTests/XCTestManifests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ extension ToolchainRegistryTests {
3737
static let __allTests__ToolchainRegistryTests = [
3838
("testDefaultBasic", testDefaultBasic),
3939
("testDefaultDarwin", testDefaultDarwin),
40+
("testDuplicateError", testDuplicateError),
41+
("testDuplicatePathError", testDuplicatePathError),
42+
("testDuplicateXcodeError", testDuplicateXcodeError),
4043
("testDylibNames", testDylibNames),
4144
("testFromDirectory", testFromDirectory),
45+
("testMultipleXcodes", testMultipleXcodes),
4246
("testSearchDarwin", testSearchDarwin),
4347
("testSearchExplicitEnv", testSearchExplicitEnv),
4448
("testSearchExplicitEnvBuiltin", testSearchExplicitEnvBuiltin),

0 commit comments

Comments
 (0)