Skip to content

Commit 07dd943

Browse files
authored
Merge pull request #2362 from bnbarham/xcrun-usr-bin-swift
Resolve `/usr/bin/*` shims on macOS
2 parents d1b9c27 + 8ac9cd6 commit 07dd943

File tree

11 files changed

+103
-42
lines changed

11 files changed

+103
-42
lines changed

Package.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,13 +526,14 @@ var targets: [Target] = [
526526
"SourceKitDForPlugin",
527527
"SwiftExtensionsForPlugin",
528528
"SwiftSourceKitPluginCommon",
529+
.product(name: "_SKLoggingForPlugin", package: "swift-tools-protocols"),
529530
],
530531
exclude: ["CMakeLists.txt"],
531532
swiftSettings: [
532533
.unsafeFlags([
534+
"-module-alias", "SKLogging=_SKLoggingForPlugin",
533535
"-module-alias", "SourceKitD=SourceKitDForPlugin",
534536
"-module-alias", "SwiftExtensions=SwiftExtensionsForPlugin",
535-
"-module-alias", "ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin",
536537
])
537538
],
538539
linkerSettings: sourcekitLSPLinkSettings
@@ -546,15 +547,12 @@ var targets: [Target] = [
546547
"Csourcekitd",
547548
"SourceKitDForPlugin",
548549
"SwiftExtensionsForPlugin",
549-
.product(name: "_SKLoggingForPlugin", package: "swift-tools-protocols"),
550550
],
551551
exclude: ["CMakeLists.txt"],
552552
swiftSettings: [
553553
.unsafeFlags([
554554
"-module-alias", "SourceKitD=SourceKitDForPlugin",
555555
"-module-alias", "SwiftExtensions=SwiftExtensionsForPlugin",
556-
"-module-alias", "ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin",
557-
"-module-alias", "SKLogging=_SKLoggingForPlugin",
558556
])
559557
]
560558
),
@@ -577,9 +575,9 @@ var targets: [Target] = [
577575
swiftSettings: [
578576
.unsafeFlags([
579577
"-module-alias", "CompletionScoring=CompletionScoringForPlugin",
578+
"-module-alias", "SKLogging=_SKLoggingForPlugin",
580579
"-module-alias", "SKUtilities=SKUtilitiesForPlugin",
581580
"-module-alias", "SourceKitD=SourceKitDForPlugin",
582-
"-module-alias", "SKLogging=_SKLoggingForPlugin",
583581
"-module-alias", "SwiftExtensions=SwiftExtensionsForPlugin",
584582
"-module-alias", "ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin",
585583
])

Sources/BuildServerIntegration/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ add_library(BuildServerIntegration STATIC
2020
LegacyBuildServer.swift
2121
MainFilesProvider.swift
2222
SplitShellCommand.swift
23-
SwiftlyResolver.swift
24-
SwiftPMBuildServer.swift)
23+
SwiftPMBuildServer.swift
24+
SwiftToolchainResolver.swift)
2525
set_target_properties(BuildServerIntegration PROPERTIES
2626
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
2727
target_link_libraries(BuildServerIntegration PUBLIC

Sources/BuildServerIntegration/JSONCompilationDatabaseBuildServer.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ fileprivate extension CompilationDatabaseCompileCommand {
2626
///
2727
/// The absence of a compiler means we have an empty command line, which should never happen.
2828
///
29-
/// If the compiler is a symlink to `swiftly`, it uses `swiftlyResolver` to find the corresponding executable in a
30-
/// real toolchain and returns that executable.
31-
func compiler(swiftlyResolver: SwiftlyResolver, compileCommandsDirectory: URL) async -> String? {
29+
/// If the compiler is a symlink to `swiftly` or in `/usr/bin` on macOS, it uses `toolchainResolver` to find the
30+
/// corresponding executable in a real toolchain and returns that executable.
31+
func compiler(toolchainResolver: SwiftToolchainResolver, compileCommandsDirectory: URL) async -> String? {
3232
guard let compiler = commandLine.first else {
3333
return nil
3434
}
35-
let swiftlyResolved = await orLog("Resolving swiftly") {
36-
try await swiftlyResolver.resolve(
35+
let resolved = await orLog("Resolving compiler") {
36+
try await toolchainResolver.resolve(
3737
compiler: URL(fileURLWithPath: compiler),
3838
workingDirectory: directoryURL(compileCommandsDirectory: compileCommandsDirectory)
3939
)?.filePath
4040
}
41-
if let swiftlyResolved {
42-
return swiftlyResolved
41+
if let resolved {
42+
return resolved
4343
}
4444
return compiler
4545
}
@@ -74,7 +74,7 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
7474
/// finds the compilation database in a build directory.
7575
private var configDirectory: URL
7676

77-
private let swiftlyResolver = SwiftlyResolver()
77+
private let toolchainResolver = SwiftToolchainResolver()
7878

7979
// Watch for all all changes to `compile_commands.json` and `compile_flags.txt` instead of just the one at
8080
// `configPath` so that we cover the following semi-common scenario:
@@ -124,7 +124,7 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
124124
package func buildTargets(request: WorkspaceBuildTargetsRequest) async throws -> WorkspaceBuildTargetsResponse {
125125
let compilers = Set(
126126
await compdb.commands.asyncCompactMap { (command) -> String? in
127-
await command.compiler(swiftlyResolver: swiftlyResolver, compileCommandsDirectory: configDirectory)
127+
await command.compiler(toolchainResolver: toolchainResolver, compileCommandsDirectory: configDirectory)
128128
}
129129
).sorted { $0 < $1 }
130130
let targets = try await compilers.asyncMap { compiler in
@@ -155,7 +155,7 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
155155
}
156156
let commandsWithRequestedCompilers = await compdb.commands.lazy.asyncFilter { command in
157157
return await targetCompiler
158-
== command.compiler(swiftlyResolver: swiftlyResolver, compileCommandsDirectory: configDirectory)
158+
== command.compiler(toolchainResolver: toolchainResolver, compileCommandsDirectory: configDirectory)
159159
}
160160
let sources = commandsWithRequestedCompilers.map {
161161
SourceItem(uri: $0.uri(compileCommandsDirectory: configDirectory), kind: .file, generated: false)
@@ -171,7 +171,7 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
171171
self.reloadCompilationDatabase()
172172
}
173173
if notification.changes.contains(where: { $0.uri.fileURL?.lastPathComponent == ".swift-version" }) {
174-
await swiftlyResolver.clearCache()
174+
await toolchainResolver.clearCache()
175175
connectionToSourceKitLSP.send(OnBuildTargetDidChangeNotification(changes: nil))
176176
}
177177
}
@@ -185,7 +185,7 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
185185
) async throws -> TextDocumentSourceKitOptionsResponse? {
186186
let targetCompiler = try request.target.compileCommandsCompiler
187187
let command = await compdb[request.textDocument.uri].asyncFilter {
188-
return await $0.compiler(swiftlyResolver: swiftlyResolver, compileCommandsDirectory: configDirectory)
188+
return await $0.compiler(toolchainResolver: toolchainResolver, compileCommandsDirectory: configDirectory)
189189
== targetCompiler
190190
}.first
191191
guard let command else {

Sources/BuildServerIntegration/SwiftlyResolver.swift renamed to Sources/BuildServerIntegration/SwiftToolchainResolver.swift

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,56 +18,82 @@ import TSCExtensions
1818
import struct TSCBasic.AbsolutePath
1919
import 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?, any 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?, any 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
}

Sources/SwiftSourceKitClientPlugin/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ set_target_properties(SwiftSourceKitClientPlugin PROPERTIES
55
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
66
target_compile_options(SwiftSourceKitClientPlugin PRIVATE
77
$<$<COMPILE_LANGUAGE:Swift>:
8+
"SHELL:-module-alias SKLogging=_SKLoggingForPlugin"
89
"SHELL:-module-alias SourceKitD=SourceKitDForPlugin"
910
"SHELL:-module-alias SwiftExtensions=SwiftExtensionsForPlugin"
10-
"SHELL:-module-alias ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin"
1111
>)
1212
target_link_libraries(SwiftSourceKitClientPlugin PRIVATE
1313
Csourcekitd
1414
SourceKitD
1515
SwiftExtensions
16-
SwiftToolsProtocols::ToolsProtocolsSwiftExtensions
16+
SwiftToolsProtocols::_SKLoggingForPlugin
1717
SwiftSourceKitPluginCommon
1818
$<$<NOT:$<PLATFORM_ID:Darwin>>:FoundationXML>)
1919

Sources/SwiftSourceKitClientPlugin/ClientPlugin.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
public import Csourcekitd
1414
import Foundation
15+
@_spi(SourceKitLSP) import SKLogging
1516
import SourceKitD
1617
import SwiftExtensions
1718
import SwiftSourceKitPluginCommon
@@ -20,7 +21,8 @@ import SwiftSourceKitPluginCommon
2021
/// loaded from.
2122
@_cdecl("sourcekitd_plugin_initialize")
2223
public func sourcekitd_plugin_initialize(_ params: sourcekitd_api_plugin_initialize_params_t) {
23-
fatalError("sourcekitd_plugin_initialize has been removed in favor of sourcekitd_plugin_initialize_2")
24+
LoggingScope.configureDefaultLoggingSubsystem("org.swift.sourcekit-lsp.client-plugin")
25+
logger.fault("sourcekitd_plugin_initialize has been removed in favor of sourcekitd_plugin_initialize_2")
2426
}
2527

2628
@_cdecl("sourcekitd_plugin_initialize_2")

Sources/SwiftSourceKitPlugin/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ set_target_properties(SwiftSourceKitPlugin PROPERTIES
3333
target_compile_options(SwiftSourceKitPlugin PRIVATE
3434
$<$<COMPILE_LANGUAGE:Swift>:
3535
"SHELL:-module-alias CompletionScoring=CompletionScoringForPlugin"
36+
"SHELL:-module-alias SKLogging=_SKLoggingForPlugin"
3637
"SHELL:-module-alias SKUtilities=SKUtilitiesForPlugin"
3738
"SHELL:-module-alias SourceKitD=SourceKitDForPlugin"
38-
"SHELL:-module-alias SKLogging=_SKLoggingForPlugin"
3939
"SHELL:-module-alias SwiftExtensions=SwiftExtensionsForPlugin"
4040
"SHELL:-module-alias ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin"
4141
>)

Sources/SwiftSourceKitPlugin/Plugin.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ final class RequestHandler: Sendable {
162162
/// loaded from.
163163
@_cdecl("sourcekitd_plugin_initialize")
164164
public func sourcekitd_plugin_initialize(_ params: sourcekitd_api_plugin_initialize_params_t) {
165-
fatalError("sourcekitd_plugin_initialize has been removed in favor of sourcekitd_plugin_initialize_2")
165+
LoggingScope.configureDefaultLoggingSubsystem("org.swift.sourcekit-lsp.service-plugin")
166+
logger.fault("sourcekitd_plugin_initialize has been removed in favor of sourcekitd_plugin_initialize_2")
166167
}
167168

168169
#if canImport(Darwin)
@@ -210,7 +211,8 @@ public func sourcekitd_plugin_initialize_2(
210211
_ params: sourcekitd_api_plugin_initialize_params_t,
211212
_ parentLibraryPath: UnsafePointer<CChar>
212213
) {
213-
LoggingScope.configureDefaultLoggingSubsystem("org.swift.sourcekit-lsp.plugin")
214+
LoggingScope.configureDefaultLoggingSubsystem("org.swift.sourcekit-lsp.service-plugin")
215+
214216
let parentLibraryPath = String(cString: parentLibraryPath)
215217
#if canImport(Darwin)
216218
if parentLibraryPath == "SOURCEKIT_LSP_PLUGIN_PARENT_LIBRARY_RTLD_DEFAULT" {

Sources/SwiftSourceKitPluginCommon/CMakeLists.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ add_library(SwiftSourceKitPluginCommon STATIC
55
target_compile_options(SwiftSourceKitPluginCommon PRIVATE
66
$<$<COMPILE_LANGUAGE:Swift>:
77
"SHELL:-module-alias SourceKitD=SourceKitDForPlugin"
8-
"SHELL:-module-alias SKLogging=_SKLoggingForPlugin"
98
"SHELL:-module-alias SwiftExtensions=SwiftExtensionsForPlugin"
10-
"SHELL:-module-alias ToolsProtocolsSwiftExtensions=_ToolsProtocolsSwiftExtensionsForPlugin"
119
>)
1210
set_target_properties(SwiftSourceKitPluginCommon PROPERTIES
1311
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
1412
target_link_libraries(SwiftSourceKitPluginCommon PRIVATE
1513
Csourcekitd
1614
SourceKitDForPlugin
17-
SwiftToolsProtocols::_SKLoggingForPlugin
1815
SwiftExtensionsForPlugin
1916
SwiftToolsProtocols::_ToolsProtocolsSwiftExtensionsForPlugin
2017
$<$<NOT:$<PLATFORM_ID:Darwin>>:FoundationXML>)

Sources/SwiftSourceKitPluginCommon/DynamicallyLoadedSourceKitdD+forPlugin.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14-
@_spi(SourceKitLSP) import SKLogging
1514
package import SourceKitD
1615
import SwiftExtensions
1716

0 commit comments

Comments
 (0)