@@ -18,56 +18,86 @@ 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 ? {
36- let cacheKey = CacheKey ( compiler: compiler, workingDirectory: workingDirectory)
36+ let cacheKey : 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+ #if !os(macOS)
80+ return nil
81+ #endif
82+
83+ guard compiler. deletingLastPathComponent ( ) == URL ( fileURLWithPath: " /usr/bin " ) else {
84+ return nil
85+ }
86+
87+ let xcrunResult = try await Process . run (
88+ arguments: [ " xcrun " , " -f " , compiler. lastPathComponent] ,
89+ workingDirectory: try AbsolutePath ( validatingOrNil: workingDirectory? . filePath)
90+ )
91+
92+ let resolvedCompiler = URL (
93+ fileURLWithPath: try xcrunResult. utf8Output ( ) . trimmingCharacters ( in: . whitespacesAndNewlines)
94+ )
95+ if FileManager . default. fileExists ( at: resolvedCompiler) {
96+ return resolvedCompiler
97+ }
98+ return nil
99+ }
100+
71101 func clearCache( ) {
72102 cache. removeAll ( )
73103 }
0 commit comments