Skip to content

Commit 0a2cc46

Browse files
committed
Reworked build service plug-in initialization slightly.
Assume plug-ins are relative to the SWBBuildService framework instead of the SWBBuildService bundle. In support of this, removed the `pluginsDirectory` parameter from the build service entry point. Also reworked the responsibility for plug-in registration in tests so it's part of CoreTestSupport.swift directly. This is for <rdar://problem/172848427>.
1 parent 420ea70 commit 0a2cc46

5 files changed

Lines changed: 33 additions & 99 deletions

File tree

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ let package = Package(
290290
swiftSettings: swiftSettings(languageMode: .v6)),
291291
.target(
292292
name: "SWBTestSupport",
293-
dependencies: ["SwiftBuild", "SWBBuildSystem", "SWBCore", "SWBTaskConstruction", "SWBTaskExecution", "SWBUtil", "SWBLLBuild", "SWBMacro"],
293+
dependencies: ["SwiftBuild", "SWBBuildService", "SWBBuildSystem", "SWBCore", "SWBTaskConstruction", "SWBTaskExecution", "SWBUtil", "SWBLLBuild", "SWBMacro"],
294294
swiftSettings: swiftSettings(languageMode: .v5)),
295295

296296
// Tests

Sources/SWBBuildService/BuildServiceEntryPoint.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension BuildService {
7676
do {
7777
try await Service.main { inputFD, outputFD in
7878
// Launch the Swift Build service.
79-
try await BuildService.run(inputFD: inputFD, outputFD: outputFD, connectionMode: .outOfProcess, pluginsDirectory: Bundle.main.builtInPlugInsURL, arguments: arguments, pluginLoadingFinished: {
79+
try await BuildService.run(inputFD: inputFD, outputFD: outputFD, connectionMode: .outOfProcess, arguments: arguments, pluginLoadingFinished: {
8080
// Already using DYLD_IMAGE_SUFFIX, clear it to avoid propagating ASan to children.
8181
// This must happen after plugin loading.
8282
if let suffix = getEnvironmentVariable("DYLD_IMAGE_SUFFIX"), suffix == "_asan" {
@@ -94,7 +94,7 @@ extension BuildService {
9494
/// Common entry point to the build service for in-process and out-of-process connections.
9595
///
9696
/// Called directly from the exported C entry point `swiftbuildServiceEntryPoint` for in-process connections, or from `BuildService.main()` (after some basic file descriptor setup) for out-of-process connections.
97-
fileprivate static func run(inputFD: FileDescriptor, outputFD: FileDescriptor, connectionMode: ServiceHostConnectionMode, pluginsDirectory: URL?, arguments: [String], pluginLoadingFinished: () throws -> Void) async throws {
97+
fileprivate static func run(inputFD: FileDescriptor, outputFD: FileDescriptor, connectionMode: ServiceHostConnectionMode, arguments: [String], pluginLoadingFinished: () throws -> Void) async throws {
9898
let pluginManager = try await { @PluginExtensionSystemActor () async throws in
9999
// Create the plugin manager and load plugins.
100100
let pluginManager = MutablePluginManager(pluginLoadingFilter: { _ in true })
@@ -150,7 +150,7 @@ extension BuildService {
150150
staticPluginInitializers.forEach { $0(pluginManager) }
151151
} else {
152152
// Otherwise, load the normal plugins.
153-
if let pluginsDirectory {
153+
if let pluginsDirectory = Bundle(for: BuildService.self).builtInPlugInsURL {
154154
let pluginsPath = try pluginsDirectory.filePath
155155
pluginManager.load(at: pluginsPath)
156156
for subpath in (try? localFS.listdir(pluginsPath).sorted().map({ pluginsPath.join($0) })) ?? [] {
@@ -190,11 +190,11 @@ extension BuildService {
190190
///
191191
/// This is exported as a C function for clients who wish to spawn the build service in-process, and which is used by the SwiftBuild client framework.
192192
@_cdecl("swiftbuildServiceEntryPoint")
193-
public func swiftbuildServiceEntryPoint(inputFD: Int32, outputFD: Int32, pluginsDirectory: URL?, completion: @Sendable @escaping ((any Error)?) -> Void) {
193+
public func swiftbuildServiceEntryPoint(inputFD: Int32, outputFD: Int32, completion: @Sendable @escaping ((any Error)?) -> Void) {
194194
_Concurrency.Task<Void, Never>.detached {
195195
let error: (any Error)?
196196
do {
197-
try await BuildService.run(inputFD: FileDescriptor(rawValue: inputFD), outputFD: FileDescriptor(rawValue: outputFD), connectionMode: .inProcess, pluginsDirectory: pluginsDirectory, arguments: [buildServiceExecutableName()], pluginLoadingFinished: {})
197+
try await BuildService.run(inputFD: FileDescriptor(rawValue: inputFD), outputFD: FileDescriptor(rawValue: outputFD), connectionMode: .inProcess, arguments: [buildServiceExecutableName()], pluginLoadingFinished: {})
198198
error = nil
199199
} catch let e {
200200
error = e

Sources/SWBCore/Core.swift

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public final class Core: Sendable {
4040
/// Get a configured instance of the core.
4141
///
4242
/// - returns: An initialized Core instance on which all discovery and loading will have been completed. If there are errors during that process, they will be logged to `stderr` and no instance will be returned. Otherwise, the initialized object is returned.
43-
public static func getInitializedCore(_ delegate: any CoreDelegate, pluginManager: MutablePluginManager, developerPath: DeveloperPath? = nil, resourceSearchPaths: [Path] = [], inferiorProductsPath: Path? = nil, extraPluginRegistration: @PluginExtensionSystemActor (_ pluginManager: MutablePluginManager, _ pluginPaths: [Path]) -> Void = { _, _ in }, additionalContentPaths: [Path] = [], environment: [String:String] = [:], buildServiceModTime: Date, connectionMode: ServiceHostConnectionMode) async -> Core? {
43+
public static func getInitializedCore(_ delegate: any CoreDelegate, pluginManager: MutablePluginManager, developerPath: DeveloperPath? = nil, resourceSearchPaths: [Path] = [], inferiorProductsPath: Path? = nil, additionalContentPaths: [Path] = [], environment: [String:String] = [:], buildServiceModTime: Date, connectionMode: ServiceHostConnectionMode) async -> Core? {
4444
// Enable macro expression interning during loading.
4545
return await MacroNamespace.withExpressionInterningEnabled { () -> Core? in
4646
let hostOperatingSystem: OperatingSystem
@@ -51,14 +51,6 @@ public final class Core: Sendable {
5151
return nil
5252
}
5353

54-
if useStaticPluginInitialization {
55-
// In a package context, plugins are statically linked into the build system.
56-
// Load specs from service plugins if requested since we don't have a Service in certain tests
57-
// Here we don't have access to `core.pluginPaths` like we do in the call below,
58-
// but it doesn't matter because `core.pluginPaths` will return an empty array when USE_STATIC_PLUGIN_INITIALIZATION is defined.
59-
await extraPluginRegistration(pluginManager, [])
60-
}
61-
6254
let resolvedDeveloperPath: DeveloperPath
6355
do {
6456
if let resolved = developerPath {
@@ -81,12 +73,6 @@ public final class Core: Sendable {
8173
return nil
8274
}
8375

84-
if !useStaticPluginInitialization {
85-
// In a package context, plugins are statically linked into the build system.
86-
// Load specs from service plugins if requested since we don't have a Service in certain tests.
87-
await extraPluginRegistration(pluginManager, Self.pluginPaths(inferiorProductsPath: inferiorProductsPath, developerPath: resolvedDeveloperPath))
88-
}
89-
9076
let core: Core
9177
do {
9278
core = try await Core(delegate: delegate, hostOperatingSystem: hostOperatingSystem, pluginManager: pluginManager.finalize(), developerPath: resolvedDeveloperPath, resourceSearchPaths: resourceSearchPaths, inferiorProductsPath: inferiorProductsPath, additionalContentPaths: additionalContentPaths, environment: environment, buildServiceModTime: buildServiceModTime, connectionMode: connectionMode)
@@ -316,55 +302,6 @@ public final class Core: Sendable {
316302
_coreSettings.value
317303
}
318304

319-
/// The list of plugin search paths.
320-
private static func pluginPaths(inferiorProductsPath: Path?, developerPath: DeveloperPath) -> [Path] {
321-
if useStaticPluginInitialization {
322-
// In a package context, plugins are statically linked into the build system.
323-
return []
324-
}
325-
326-
var result = [Path]()
327-
328-
// If we are inferior, then search the built products directory first.
329-
//
330-
// FIXME: This is error prone, as it won't validate that any of these are installed in the expected location.
331-
// FIXME: If we remove, move or rename something in the built Xcode, then this will still find the old item in the installed Xcode.
332-
if let inferiorProductsPath {
333-
result.append(inferiorProductsPath)
334-
}
335-
336-
guard let coreFrameworkPath = try? Bundle(for: Self.self).bundleURL.filePath.dirname else {
337-
return result
338-
}
339-
340-
// Search paths relative to SWBCore itself.
341-
do {
342-
func appendInferior(_ path: Path) {
343-
if !developerPath.path.dirname.isAncestor(of: path) {
344-
result.append(path)
345-
}
346-
}
347-
348-
// flat layout, when SWBCore is directly nested under BUILT_PRODUCTS_DIR
349-
appendInferior(coreFrameworkPath)
350-
351-
// flat layout, when SWBCore is nested in SWBBuildServiceBundle in SwiftBuild.framework in BUILT_PRODUCTS_DIR
352-
appendInferior(coreFrameworkPath.join("../../../../../../.."))
353-
}
354-
355-
// In the superior or a hierarchical build, look for plugins in the build service bundle relative to SWBCore.framework.
356-
let pluginPath = coreFrameworkPath.join("../PlugIns")
357-
result.append(pluginPath)
358-
for subdirectory in (try? localFS.listdir(pluginPath)) ?? [] {
359-
let subdirectoryPath = pluginPath.join(subdirectory)
360-
if localFS.isDirectory(subdirectoryPath) {
361-
result.append(subdirectoryPath)
362-
}
363-
}
364-
365-
return result.map { $0.normalize() }
366-
}
367-
368305
/// The list of toolchain search paths.
369306
@_spi(Testing) public let toolchainPaths: [ToolchainRegistry.SearchPath]
370307

Sources/SWBTestSupport/CoreTestSupport.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package import SWBUtil
1616
import SWBTaskConstruction
1717
import SWBTaskExecution
1818
import SWBServiceCore
19+
private import SWBBuildService
1920
import Testing
2021

2122
#if USE_STATIC_PLUGIN_INITIALIZATION
@@ -170,8 +171,25 @@ extension Core {
170171
registerExtraPlugins(pluginManager)
171172
}
172173

174+
var pluginPaths: [Path] = []
175+
if let buildServicePlugInsDirectory = Bundle(for: BuildService.self).builtInPlugInsURL {
176+
let path = Path(buildServicePlugInsDirectory.path(percentEncoded: false))
177+
pluginPaths.append(path)
178+
179+
pluginPaths += ((try? localFS.listdir(path)) ?? []).compactMap {
180+
let subdirectoryPath = path.join($0)
181+
if localFS.isDirectory(subdirectoryPath) {
182+
return subdirectoryPath
183+
} else {
184+
return nil
185+
}
186+
}
187+
}
188+
189+
await extraPluginRegistration(pluginManager: pluginManager, pluginPaths: pluginPaths)
190+
173191
let delegate = inputDelegate ?? TestingCoreDelegate()
174-
guard let core = await Core.getInitializedCore(delegate, pluginManager: pluginManager, developerPath: developerPath, inferiorProductsPath: inferiorProductsPath, extraPluginRegistration: extraPluginRegistration, additionalContentPaths: additionalContentPaths, environment: environment, buildServiceModTime: Date(), connectionMode: .inProcess) else {
192+
guard let core = await Core.getInitializedCore(delegate, pluginManager: pluginManager, developerPath: developerPath, inferiorProductsPath: inferiorProductsPath, additionalContentPaths: additionalContentPaths, environment: environment, buildServiceModTime: Date(), connectionMode: .inProcess) else {
175193
// If we weren't passed a delgate, the caller couldn't possibly have emitted the diagnostics themselves (and CoreInitializationError deliberately doesn't include them in its error output).
176194
if inputDelegate == nil {
177195
// Emit one failure per error, which will be significantly easier to read.

Sources/SwiftBuild/SWBBuildServiceConnection.swift

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ extension SWBBuildServiceConnection {
571571
/// Method by which to launch the build service.
572572
public enum SWBBuildServiceConnectionMode: Sendable {
573573
/// Launch the build service in-process, statically with the provided swiftBuildServiceEntryPoint function.
574-
case inProcessStatic(@Sendable (Int32, Int32, URL?, @Sendable @escaping ((any Error)?) -> Void) -> Void)
574+
case inProcessStatic(@Sendable (Int32, Int32, @Sendable @escaping ((any Error)?) -> Void) -> Void)
575575

576576
/// Launch the build service in-process.
577577
case inProcess
@@ -669,9 +669,9 @@ fileprivate final class InProcessStaticConnection: ConnectionTransport {
669669
private let serviceBundleURL: URL?
670670
private let done = WaitCondition()
671671
private let _state: LockedValue<SWBBuildServiceConnection.State> = .init(.running)
672-
private let startFunc: @Sendable (Int32, Int32, URL?, @Sendable @escaping ((any Error)?) -> Void) -> Void
672+
private let startFunc: @Sendable (Int32, Int32, @Sendable @escaping ((any Error)?) -> Void) -> Void
673673

674-
init(serviceBundleURL: URL?, stdinPipe: IOPipe, stdoutPipe: IOPipe, startFunc: @Sendable @escaping (Int32, Int32, URL?, @Sendable @escaping ((any Error)?) -> Void) -> Void) throws {
674+
init(serviceBundleURL: URL?, stdinPipe: IOPipe, stdoutPipe: IOPipe, startFunc: @Sendable @escaping (Int32, Int32, @Sendable @escaping ((any Error)?) -> Void) -> Void) throws {
675675
self.serviceBundleURL = serviceBundleURL
676676
self.stdinPipe = stdinPipe
677677
self.stdoutPipe = stdoutPipe
@@ -697,22 +697,8 @@ fileprivate final class InProcessStaticConnection: ConnectionTransport {
697697

698698
let inputFD = stdinPipe.readEnd.rawValue
699699
let outputFD = stdoutPipe.writeEnd.rawValue
700-
701-
let buildServiceLocation = SWBBuildServiceConnection.buildServiceLocation(for: .normal, overridingServiceBundleURL: serviceBundleURL)
702-
let buildServicePlugInsDirectory: URL?
703-
if let buildServiceLocation, let buildServiceBundle = buildServiceLocation.bundle {
704-
buildServicePlugInsDirectory = buildServiceBundle.builtInPlugInsURL
705-
} else if let buildServiceLocation: SWBBuildServiceConnection.BuildServiceLocation {
706-
let path = SWBUtil.Path(buildServiceLocation.executable.path)
707-
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
708-
} else {
709-
// If the build service executable is unbundled, then try to find the build service entry point in this executable.
710-
let path = try Library.locate(SWBBuildServiceConnection.self)
711-
// If the build service executable is unbundled, assume that any plugins that may exist are next to our executable.
712-
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
713-
}
714700
launched = true
715-
self.startFunc(Int32(inputFD), Int32(outputFD), buildServicePlugInsDirectory, { [done, terminationHandler] error in
701+
self.startFunc(Int32(inputFD), Int32(outputFD), { [done, terminationHandler] error in
716702
defer { done.signal() }
717703

718704
#if !canImport(Darwin)
@@ -770,7 +756,6 @@ fileprivate final class InProcessConnection: ConnectionTransport {
770756
let buildServiceLocation = SWBBuildServiceConnection.buildServiceLocation(for: variant, overridingServiceBundleURL: serviceBundleURL)
771757

772758
let handle: LibraryHandle
773-
let buildServicePlugInsDirectory: URL?
774759
if let buildServiceLocation, let buildServiceBundle = buildServiceLocation.bundle {
775760
guard let buildServiceFrameworkURL = buildServiceBundle.privateFrameworksURL?.appendingPathComponent("SWBBuildService.framework", isDirectory: true) else {
776761
throw StubError.error("cannot determine build service framework URL in build service bundle")
@@ -796,27 +781,21 @@ fileprivate final class InProcessConnection: ConnectionTransport {
796781
} catch {
797782
throw StubError.error("unable to open build service framework executable at '\(buildServiceFrameworkExecutablePath.str)': \(error)")
798783
}
799-
800-
buildServicePlugInsDirectory = buildServiceBundle.builtInPlugInsURL
801784
} else if let buildServiceLocation {
802785
let path = SWBUtil.Path(buildServiceLocation.executable.path)
803786
handle = try Library.open(path)
804-
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
805787
} else {
806788
// If the build service executable is unbundled, then try to find the build service entry point in this executable.
807789
let path = try Library.locate(SWBBuildServiceConnection.self)
808790
handle = try Library.open(path)
809-
810-
// If the build service executable is unbundled, assume that any plugins that may exist are next to our executable.
811-
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
812791
}
813792

814793
let entryPointName = "swiftbuildServiceEntryPoint"
815794
#if !canImport(Darwin)
816795
// Workaround for a compiler crash presumably related to Objective-C bridging on non-Darwin platforms (rdar://130826719&136043295)
817-
typealias swiftbuildServiceEntryPoint_t = @convention(c) (Int32, Int32, URL?, @Sendable @escaping (Any) -> Void) -> Void
796+
typealias swiftbuildServiceEntryPoint_t = @convention(c) (Int32, Int32, @Sendable @escaping (Any) -> Void) -> Void
818797
#else
819-
typealias swiftbuildServiceEntryPoint_t = @convention(c) (Int32, Int32, URL?, @Sendable @escaping ((any Error)?) -> Void) -> Void
798+
typealias swiftbuildServiceEntryPoint_t = @convention(c) (Int32, Int32, @Sendable @escaping ((any Error)?) -> Void) -> Void
820799
#endif
821800
guard let entryPointFunc: swiftbuildServiceEntryPoint_t = Library.lookup(handle, entryPointName) else {
822801
throw StubError.error("unable to find \(entryPointName) function in service executable")
@@ -827,7 +806,7 @@ fileprivate final class InProcessConnection: ConnectionTransport {
827806

828807
// Launch the service, in process (on background queues).
829808
launched = true
830-
entryPointFunc(Int32(inputFD), Int32(outputFD), buildServicePlugInsDirectory, { [done, terminationHandler] error in
809+
entryPointFunc(Int32(inputFD), Int32(outputFD), { [done, terminationHandler] error in
831810
defer { done.signal() }
832811

833812
#if !canImport(Darwin)

0 commit comments

Comments
 (0)