@@ -60,13 +60,13 @@ package final actor ToolchainRegistry {
6060 private let toolchainsByIdentifier : [ String : [ Toolchain ] ]
6161
6262 /// The toolchains indexed by their path.
63- private let toolchainsByPath : [ URL : Toolchain ]
63+ private var toolchainsByPath : [ URL : Toolchain ]
6464
6565 /// Map from compiler paths (`clang`, `swift`, `swiftc`) mapping to the toolchain that contained them.
6666 ///
6767 /// This allows us to find the toolchain that should be used for semantic functionality based on which compiler it is
6868 /// built with in the `compile_commands.json`.
69- private let toolchainsByCompiler : [ URL : Toolchain ]
69+ private var toolchainsByCompiler : [ URL : Toolchain ]
7070
7171 /// The currently selected toolchain identifier on Darwin.
7272 package let darwinToolchainOverride : String ?
@@ -96,6 +96,13 @@ package final actor ToolchainRegistry {
9696 var toolchainsByPath : [ URL : Toolchain ] = [ : ]
9797 var toolchainsByCompiler : [ URL : Toolchain ] = [ : ]
9898 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+
99106 // Non-XcodeDefault toolchain: disallow all duplicates.
100107 if toolchainsByIdentifier [ toolchain. identifier] != nil ,
101108 toolchain. identifier != ToolchainRegistry . darwinDefaultToolchainIdentifier
@@ -104,12 +111,6 @@ package final actor ToolchainRegistry {
104111 continue
105112 }
106113
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-
113114 toolchainsByPath [ toolchain. path] = toolchain
114115 toolchainsByIdentifier [ toolchain. identifier, default: [ ] ] . append ( toolchain)
115116
@@ -218,9 +219,14 @@ package final actor ToolchainRegistry {
218219 }
219220 }
220221
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)
224230 }
225231 return nil
226232 }
@@ -283,7 +289,43 @@ package final actor ToolchainRegistry {
283289 /// If we have a toolchain in the toolchain registry that contains the compiler with the given URL, return it.
284290 /// Otherwise, return `nil`.
285291 package func toolchain( withCompiler compiler: URL ) -> Toolchain ? {
286- return toolchainsByCompiler [ compiler]
292+ if let toolchain = toolchainsByCompiler [ compiler] {
293+ return toolchain
294+ }
295+
296+ // Only canonicalize the folder path, as we don't want to resolve symlinks to eg. `swift-driver`.
297+ let resolvedPath = orLog ( " Compiler realpath " ) {
298+ try compiler. deletingLastPathComponent ( ) . realpath
299+ } ? . appending ( component: compiler. lastPathComponent)
300+ guard let resolvedPath,
301+ let toolchain = toolchainsByCompiler [ resolvedPath]
302+ else {
303+ return nil
304+ }
305+
306+ // Cache mapping of non-realpath to the realpath toolchain for faster subsequent lookups
307+ toolchainsByCompiler [ compiler] = toolchain
308+ return toolchain
309+ }
310+
311+ /// If we have a toolchain in the toolchain registry with the given URL, return it. Otherwise, return `nil`.
312+ package func toolchain( withPath path: URL ) -> Toolchain ? {
313+ if let toolchain = toolchainsByPath [ path] {
314+ return toolchain
315+ }
316+
317+ let resolvedPath = orLog ( " Toolchain realpath " ) {
318+ try path. realpath
319+ }
320+ guard let resolvedPath,
321+ let toolchain = toolchainsByPath [ resolvedPath]
322+ else {
323+ return nil
324+ }
325+
326+ // Cache mapping of non-realpath to the realpath toolchain for faster subsequent lookups
327+ toolchainsByPath [ path] = toolchain
328+ return toolchain
287329 }
288330}
289331
@@ -292,10 +334,6 @@ extension ToolchainRegistry {
292334 package func toolchains( withIdentifier identifier: String ) -> [ Toolchain ] {
293335 return toolchainsByIdentifier [ identifier] ?? [ ]
294336 }
295-
296- package func toolchain( withPath path: URL ) -> Toolchain ? {
297- return toolchainsByPath [ path]
298- }
299337}
300338
301339extension ToolchainRegistry {
0 commit comments