@@ -60,13 +60,13 @@ package final actor ToolchainRegistry {
60
60
private let toolchainsByIdentifier : [ String : [ Toolchain ] ]
61
61
62
62
/// The toolchains indexed by their path.
63
- private let toolchainsByPath : [ URL : Toolchain ]
63
+ private var toolchainsByPath : [ URL : Toolchain ]
64
64
65
65
/// Map from compiler paths (`clang`, `swift`, `swiftc`) mapping to the toolchain that contained them.
66
66
///
67
67
/// This allows us to find the toolchain that should be used for semantic functionality based on which compiler it is
68
68
/// built with in the `compile_commands.json`.
69
- private let toolchainsByCompiler : [ URL : Toolchain ]
69
+ private var toolchainsByCompiler : [ URL : Toolchain ]
70
70
71
71
/// The currently selected toolchain identifier on Darwin.
72
72
package let darwinToolchainOverride : String ?
@@ -96,6 +96,13 @@ package final actor ToolchainRegistry {
96
96
var toolchainsByPath : [ URL : Toolchain ] = [ : ]
97
97
var toolchainsByCompiler : [ URL : Toolchain ] = [ : ]
98
98
for (toolchain, reason) in toolchainsAndReasonsParam {
99
+ // Toolchain should always be unique by path. It isn't particularly useful to log if we already have a toolchain
100
+ // though, as we could have just found toolchains through symlinks (this is actually quite normal - eg. OSS
101
+ // toolchains add a `swift-latest.xctoolchain` symlink on macOS).
102
+ if toolchainsByPath [ toolchain. path] != nil {
103
+ continue
104
+ }
105
+
99
106
// Non-XcodeDefault toolchain: disallow all duplicates.
100
107
if toolchainsByIdentifier [ toolchain. identifier] != nil ,
101
108
toolchain. identifier != ToolchainRegistry . darwinDefaultToolchainIdentifier
@@ -104,12 +111,6 @@ package final actor ToolchainRegistry {
104
111
continue
105
112
}
106
113
107
- // Toolchain should always be unique by path.
108
- if toolchainsByPath [ toolchain. path] != nil {
109
- logger. fault ( " Found two toolchains with the same path: \( toolchain. path) " )
110
- continue
111
- }
112
-
113
114
toolchainsByPath [ toolchain. path] = toolchain
114
115
toolchainsByIdentifier [ toolchain. identifier, default: [ ] ] . append ( toolchain)
115
116
@@ -218,9 +219,14 @@ package final actor ToolchainRegistry {
218
219
}
219
220
}
220
221
221
- let toolchainsAndReasons = toolchainPaths. compactMap {
222
- if let toolchain = Toolchain ( $0. path) {
223
- return ( toolchain, $0. reason)
222
+ let toolchainsAndReasons = toolchainPaths. compactMap { toolchainAndReason in
223
+ let resolvedPath = orLog ( " Toolchain realpath " ) {
224
+ try toolchainAndReason. path. realpath
225
+ }
226
+ if let resolvedPath,
227
+ let toolchain = Toolchain ( resolvedPath)
228
+ {
229
+ return ( toolchain, toolchainAndReason. reason)
224
230
}
225
231
return nil
226
232
}
@@ -285,7 +291,43 @@ package final actor ToolchainRegistry {
285
291
/// If we have a toolchain in the toolchain registry that contains the compiler with the given URL, return it.
286
292
/// Otherwise, return `nil`.
287
293
package func toolchain( withCompiler compiler: URL ) -> Toolchain ? {
288
- return toolchainsByCompiler [ compiler]
294
+ if let toolchain = toolchainsByCompiler [ compiler] {
295
+ return toolchain
296
+ }
297
+
298
+ // Only canonicalize the folder path, as we don't want to resolve symlinks to eg. `swift-driver`.
299
+ let resolvedPath = orLog ( " Compiler realpath " ) {
300
+ try compiler. deletingLastPathComponent ( ) . realpath
301
+ } ? . appending ( component: compiler. lastPathComponent)
302
+ guard let resolvedPath,
303
+ let toolchain = toolchainsByCompiler [ resolvedPath]
304
+ else {
305
+ return nil
306
+ }
307
+
308
+ // Cache mapping of non-realpath to the realpath toolchain for faster subsequent lookups
309
+ toolchainsByCompiler [ compiler] = toolchain
310
+ return toolchain
311
+ }
312
+
313
+ /// If we have a toolchain in the toolchain registry with the given URL, return it. Otherwise, return `nil`.
314
+ package func toolchain( withPath path: URL ) -> Toolchain ? {
315
+ if let toolchain = toolchainsByPath [ path] {
316
+ return toolchain
317
+ }
318
+
319
+ let resolvedPath = orLog ( " Toolchain realpath " ) {
320
+ try path. realpath
321
+ }
322
+ guard let resolvedPath,
323
+ let toolchain = toolchainsByPath [ resolvedPath]
324
+ else {
325
+ return nil
326
+ }
327
+
328
+ // Cache mapping of non-realpath to the realpath toolchain for faster subsequent lookups
329
+ toolchainsByPath [ path] = toolchain
330
+ return toolchain
289
331
}
290
332
}
291
333
@@ -294,10 +336,6 @@ extension ToolchainRegistry {
294
336
package func toolchains( withIdentifier identifier: String ) -> [ Toolchain ] {
295
337
return toolchainsByIdentifier [ identifier] ?? [ ]
296
338
}
297
-
298
- package func toolchain( withPath path: URL ) -> Toolchain ? {
299
- return toolchainsByPath [ path]
300
- }
301
339
}
302
340
303
341
extension ToolchainRegistry {
0 commit comments