Skip to content

Commit 76954e0

Browse files
authored
Merge pull request #1925 from ahoppen/plugin-tests-using-xcode-toolchain
Fix test failures when running SourceKit-LSP tests with an Xcode toolchain
2 parents f1251c0 + 99019bb commit 76954e0

File tree

4 files changed

+75
-13
lines changed

4 files changed

+75
-13
lines changed

Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift

+31-6
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ fileprivate extension ThreadSafeBox {
4141
}
4242
}
4343

44+
#if canImport(Darwin)
45+
fileprivate func setenv(name: String, value: String, override: Bool) throws {
46+
struct FailedToSetEnvError: Error {
47+
let errorCode: Int32
48+
}
49+
try name.withCString { name in
50+
try value.withCString { value in
51+
let result = setenv(name, value, override ? 0 : 1)
52+
if result != 0 {
53+
throw FailedToSetEnvError(errorCode: result)
54+
}
55+
}
56+
}
57+
}
58+
#endif
59+
4460
/// Wrapper for sourcekitd, taking care of initialization, shutdown, and notification handler
4561
/// multiplexing.
4662
///
@@ -103,12 +119,21 @@ package actor DynamicallyLoadedSourceKitD: SourceKitD {
103119
dylibPath: URL,
104120
pluginPaths: PluginPaths?
105121
) async throws -> SourceKitD {
106-
try await SourceKitDRegistry.shared
107-
.getOrAdd(
108-
dylibPath,
109-
pluginPaths: pluginPaths,
110-
create: { try DynamicallyLoadedSourceKitD(dylib: dylibPath, pluginPaths: pluginPaths) }
111-
)
122+
try await SourceKitDRegistry.shared.getOrAdd(dylibPath, pluginPaths: pluginPaths) {
123+
#if canImport(Darwin)
124+
#if compiler(>=6.3)
125+
#warning("Remove this when we no longer need to support sourcekitd_plugin_initialize")
126+
#endif
127+
if let pluginPaths {
128+
try setenv(
129+
name: "SOURCEKIT_LSP_PLUGIN_SOURCEKITD_PATH_\(pluginPaths.clientPlugin.filePath)",
130+
value: dylibPath.filePath,
131+
override: false
132+
)
133+
}
134+
#endif
135+
return try DynamicallyLoadedSourceKitD(dylib: dylibPath, pluginPaths: pluginPaths)
136+
}
112137
}
113138

114139
package init(dylib path: URL, pluginPaths: PluginPaths?, initialize: Bool = true) throws {

Sources/SourceKitD/dlopen.swift

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ package final class DLHandle: Sendable {
3434
#endif
3535
}
3636

37+
#if canImport(Darwin)
38+
package static let rtldDefault = DLHandle(rawValue: Handle(handle: UnsafeMutableRawPointer(bitPattern: -2)!))
39+
#endif
40+
3741
fileprivate let rawValue: ThreadSafeBox<Handle?>
3842

3943
fileprivate init(rawValue: Handle) {

Sources/SwiftSourceKitClientPlugin/ClientPlugin.swift

+12
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public import Csourcekitd
2121
import Csourcekitd
2222
#endif
2323

24+
#if compiler(>=6.3)
25+
#warning("Remove sourcekitd_plugin_initialize when we no longer support toolchains that call it")
26+
#endif
27+
2428
/// Legacy plugin initialization logic in which sourcekitd does not inform the plugin about the sourcekitd path it was
2529
/// loaded from.
2630
@_cdecl("sourcekitd_plugin_initialize")
@@ -38,6 +42,14 @@ public func sourcekitd_plugin_initialize(_ params: sourcekitd_api_plugin_initial
3842
.deletingLastPathComponent()
3943
.appendingPathComponent("sourcekitd.framework")
4044
.appendingPathComponent("sourcekitd")
45+
if !FileManager.default.fileExists(at: url),
46+
let sourcekitdPath = ProcessInfo.processInfo.environment["SOURCEKIT_LSP_PLUGIN_SOURCEKITD_PATH_\(path)"]
47+
{
48+
// When using a SourceKit plugin from the build directory, we can't find sourcekitd relative to the plugin.
49+
// Respect the sourcekitd path that was passed to us via an environment variable from
50+
// `DynamicallyLoadedSourceKitD.getOrCreate`.
51+
url = URL(fileURLWithPath: sourcekitdPath)
52+
}
4153
try! url.filePath.withCString { sourcekitdPath in
4254
sourcekitd_plugin_initialize_2(params, sourcekitdPath)
4355
}

Sources/SwiftSourceKitPlugin/Plugin.swift

+28-7
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ final class RequestHandler: Sendable {
143143
}
144144
}
145145

146+
#if compiler(>=6.3)
147+
#warning("Remove sourcekitd_plugin_initialize when we no longer support toolchains that call it")
148+
#endif
149+
146150
/// Legacy plugin initialization logic in which sourcekitd does not inform the plugin about the sourcekitd path it was
147151
/// loaded from.
148152
@_cdecl("sourcekitd_plugin_initialize")
@@ -160,8 +164,16 @@ public func sourcekitd_plugin_initialize(_ params: sourcekitd_api_plugin_initial
160164
.deletingLastPathComponent()
161165
.appendingPathComponent("sourcekitd.framework")
162166
.appendingPathComponent("sourcekitd")
163-
try! url.filePath.withCString { sourcekitdPath in
164-
sourcekitd_plugin_initialize_2(params, sourcekitdPath)
167+
if FileManager.default.fileExists(at: url) {
168+
try! url.filePath.withCString { sourcekitdPath in
169+
sourcekitd_plugin_initialize_2(params, sourcekitdPath)
170+
}
171+
} else {
172+
// When using a SourceKit plugin from the build directory, we can't find sourcekitd relative to the plugin.
173+
// Since sourcekitd_plugin_initialize is only called on Darwin from Xcode toolchains, we know that we are getting
174+
// called from an XPC sourcekitd. Thus, all sourcekitd symbols that we need should be loaded in the current process
175+
// already and we can use `RTLD_DEFAULT` for the sourcekitd library.
176+
sourcekitd_plugin_initialize_2(params, "SOURCEKIT_LSP_PLUGIN_PARENT_LIBRARY_RTLD_DEFAULT")
165177
}
166178
#else
167179
fatalError("sourcekitd_plugin_initialize is not supported on non-Darwin platforms")
@@ -223,15 +235,24 @@ public func sourcekitd_plugin_initialize_2(
223235
_ params: sourcekitd_api_plugin_initialize_params_t,
224236
_ parentLibraryPath: UnsafePointer<CChar>
225237
) {
238+
let parentLibraryPath = String(cString: parentLibraryPath)
226239
#if canImport(Darwin)
227-
// On macOS, we need to find sourcekitdInProc relative to the library the plugin was loaded from.
228-
DynamicallyLoadedSourceKitD.forPlugin = try! DynamicallyLoadedSourceKitD.inProcLibrary(
229-
relativeTo: URL(fileURLWithPath: String(cString: parentLibraryPath))
230-
)
240+
if parentLibraryPath == "SOURCEKIT_LSP_PLUGIN_PARENT_LIBRARY_RTLD_DEFAULT" {
241+
DynamicallyLoadedSourceKitD.forPlugin = try! DynamicallyLoadedSourceKitD(
242+
dlhandle: .rtldDefault,
243+
path: URL(string: "rtld-default://")!,
244+
pluginPaths: nil,
245+
initialize: false
246+
)
247+
} else {
248+
DynamicallyLoadedSourceKitD.forPlugin = try! DynamicallyLoadedSourceKitD.inProcLibrary(
249+
relativeTo: URL(fileURLWithPath: String(cString: parentLibraryPath))
250+
)
251+
}
231252
#else
232253
// On other platforms, sourcekitd is always in process, so we can load it straight away.
233254
DynamicallyLoadedSourceKitD.forPlugin = try! DynamicallyLoadedSourceKitD(
234-
dylib: URL(fileURLWithPath: String(cString: parentLibraryPath)),
255+
dylib: URL(fileURLWithPath: parentLibraryPath),
235256
pluginPaths: nil,
236257
initialize: false
237258
)

0 commit comments

Comments
 (0)