@@ -18,56 +18,82 @@ import TSCExtensions
1818import struct TSCBasic. AbsolutePath
1919import class TSCBasic. Process
2020
21- /// Given a path to a compiler, which might be a symlink to `swiftly`, this type determines the compiler executable in
22- /// an actual toolchain. It also caches the results . The client needs to invalidate the cache if the path that swiftly
23- /// might resolve to has changed, eg. because `.swift-version` has been updated.
24- actor SwiftlyResolver {
21+ /// Given a path to a compiler, which might be a symlink to `swiftly` or `/usr/bin` on macOS , this type determines the
22+ /// compiler executable in an actual toolchain and caches the result . The client needs to invalidate the cache if the
23+ /// path that this may resolve to has changed, eg. because `.swift-version` or `SDKROOT ` has been updated.
24+ actor SwiftToolchainResolver {
2525 private struct CacheKey : Hashable {
2626 let compiler : URL
2727 let workingDirectory : URL ?
2828 }
2929
3030 private var cache : LRUCache < CacheKey , Result < URL ? , Error > > = LRUCache ( capacity: 100 )
3131
32- /// Check if `compiler` is a symlink to `swiftly`. If so, find the executable in the toolchain that swiftly resolves
33- /// to within the given working directory and return the URL of the corresponding compiler in that toolchain.
34- /// If `compiler` does not resolve to `swiftly`, return `nil`.
32+ /// Check if `compiler` is a symlink to `swiftly` or in `/usr/bin` on macOS . If so, find the executable in the
33+ /// toolchain that would be resolved to within the given working directory and return the URL of the corresponding
34+ /// compiler in that toolchain. If `compiler` does not resolve to `swiftly` or `/usr/bin` on macOS , return `nil`.
3535 func resolve( compiler: URL , workingDirectory: URL ? ) async throws -> URL ? {
3636 let cacheKey = CacheKey ( compiler: compiler, workingDirectory: workingDirectory)
3737 if let cached = cache [ cacheKey] {
3838 return try cached. get ( )
3939 }
40+
4041 let computed : Result < URL ? , Error >
4142 do {
42- computed = . success(
43- try await resolveSwiftlyTrampolineImpl ( compiler: compiler, workingDirectory: workingDirectory)
44- )
43+ var resolved = try await resolveSwiftlyTrampoline ( compiler: compiler, workingDirectory: workingDirectory)
44+ if resolved == nil {
45+ resolved = try await resolveXcrunTrampoline ( compiler: compiler, workingDirectory: workingDirectory)
46+ }
47+
48+ computed = . success( resolved)
4549 } catch {
4650 computed = . failure( error)
4751 }
52+
4853 cache [ cacheKey] = computed
4954 return try computed. get ( )
5055 }
5156
52- private func resolveSwiftlyTrampolineImpl ( compiler: URL , workingDirectory: URL ? ) async throws -> URL ? {
57+ private func resolveSwiftlyTrampoline ( compiler: URL , workingDirectory: URL ? ) async throws -> URL ? {
5358 let realpath = try compiler. realpath
5459 guard realpath. lastPathComponent == " swiftly " else {
5560 return nil
5661 }
62+
5763 let swiftlyResult = try await Process . run (
5864 arguments: [ realpath. filePath, " use " , " -p " ] ,
5965 workingDirectory: try AbsolutePath ( validatingOrNil: workingDirectory? . filePath)
6066 )
6167 let swiftlyToolchain = URL (
6268 fileURLWithPath: try swiftlyResult. utf8Output ( ) . trimmingCharacters ( in: . whitespacesAndNewlines)
6369 )
70+
6471 let resolvedCompiler = swiftlyToolchain. appending ( components: " usr " , " bin " , compiler. lastPathComponent)
6572 if FileManager . default. fileExists ( at: resolvedCompiler) {
6673 return resolvedCompiler
6774 }
6875 return nil
6976 }
7077
78+ private func resolveXcrunTrampoline( compiler: URL , workingDirectory: URL ? ) async throws -> URL ? {
79+ guard Platform . current == . darwin, compiler. deletingLastPathComponent ( ) == URL ( filePath: " /usr/bin/ " ) else {
80+ return nil
81+ }
82+
83+ let xcrunResult = try await Process . run (
84+ arguments: [ " xcrun " , " -f " , compiler. lastPathComponent] ,
85+ workingDirectory: try AbsolutePath ( validatingOrNil: workingDirectory? . filePath)
86+ )
87+
88+ let resolvedCompiler = URL (
89+ fileURLWithPath: try xcrunResult. utf8Output ( ) . trimmingCharacters ( in: . whitespacesAndNewlines)
90+ )
91+ if FileManager . default. fileExists ( at: resolvedCompiler) {
92+ return resolvedCompiler
93+ }
94+ return nil
95+ }
96+
7197 func clearCache( ) {
7298 cache. removeAll ( )
7399 }
0 commit comments