diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 831689605a4..e67d758c24c 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -624,6 +624,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS sourceFiles: plugin.sources.paths, pluginName: plugin.moduleName, toolsVersion: plugin.toolsVersion, + workers: config.toolsBuildParameters.workers, observabilityScope: self.observabilityScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index bef12b95fa8..572bbb62638 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -817,6 +817,7 @@ extension BuildPlan { pluginGeneratedResources: pluginDerivedResources.map(\.path) ), buildEnvironment: buildParameters.buildEnvironment, + workers: buildParameters.workers, scriptRunner: configuration.scriptRunner, workingDirectory: package.path, outputDirectory: pluginOutputDir, diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 31502178e6c..78654002e18 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -380,6 +380,7 @@ struct PluginCommand: AsyncSwiftCommand { let _ = try await pluginTarget.invoke( action: .performCommand(package: package, arguments: arguments), buildEnvironment: buildEnvironment, + workers: buildParameters.workers, scriptRunner: pluginScriptRunner, workingDirectory: swiftCommandState.originalWorkingDirectory, outputDirectory: outputDir, diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 3b7411d8ce9..d957ab0f487 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -528,7 +528,7 @@ public struct BuildOptions: ParsableArguments { /// The number of jobs for llbuild to start (aka the number of schedulerLanes) @Option(name: .shortAndLong, help: "The number of jobs to spawn in parallel during the build process.") - public var jobs: UInt32? + public var jobs: UInt32 = UInt32(ProcessInfo.processInfo.activeProcessorCount) /// Whether to use the integrated Swift driver rather than shelling out /// to a separate process. diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 970e3e19d2c..2d726d00b43 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -936,7 +936,7 @@ public final class SwiftCommandState { buildSystemKind: options.build.buildSystem, pkgConfigDirectories: options.locations.pkgConfigDirectories, architectures: options.build.architectures, - workers: options.build.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount), + workers: options.build.jobs, shouldCreateDylibForDynamicProducts: !self.options.build.shouldBuildDylibsAsFrameworks, sanitizers: options.build.enabledSanitizers, indexStoreMode: options.build.indexStoreMode.buildParameter, diff --git a/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift b/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift index a5b450a6f5a..24830344791 100644 --- a/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift +++ b/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift @@ -66,6 +66,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { writableDirectories: [Basics.AbsolutePath], readOnlyDirectories: [Basics.AbsolutePath], allowNetworkConnections: [SandboxNetworkPermission], + workers: UInt32, fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -77,6 +78,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { sourceFiles: sourceFiles, pluginName: pluginName, toolsVersion: toolsVersion, + workers: workers, observabilityScope: observabilityScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate, @@ -119,6 +121,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { sourceFiles: [Basics.AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, + workers: UInt32, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginScriptCompilerDelegate, @@ -221,6 +224,9 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { // Parse the plugin as a library so that `@main` is supported even though there might be only a single source file. commandLine += ["-parse-as-library"] + // Enable concurrent compilation. + commandLine += ["-j\(workers)"] + // Ask the compiler to create a diagnostics file (we'll put it next to the executable). commandLine += ["-Xfrontend", "-serialize-diagnostics-path", "-Xfrontend", diagFilePath.pathString] diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 200727c6990..30b73883e84 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -63,6 +63,7 @@ extension PluginModule { public func invoke( action: PluginAction, buildEnvironment: BuildEnvironment, + workers: UInt32, scriptRunner: PluginScriptRunner, workingDirectory: AbsolutePath, outputDirectory: AbsolutePath, @@ -83,6 +84,7 @@ extension PluginModule { self.invoke( action: action, buildEnvironment: buildEnvironment, + workers: workers, scriptRunner: scriptRunner, workingDirectory: workingDirectory, outputDirectory: outputDirectory, @@ -132,6 +134,7 @@ extension PluginModule { public func invoke( action: PluginAction, buildEnvironment: BuildEnvironment, + workers: UInt32, scriptRunner: PluginScriptRunner, workingDirectory: AbsolutePath, outputDirectory: AbsolutePath, @@ -419,6 +422,7 @@ extension PluginModule { writableDirectories: writableDirectories, readOnlyDirectories: readOnlyDirectories, allowNetworkConnections: allowNetworkConnections, + workers: workers, fileSystem: fileSystem, observabilityScope: observabilityScope, callbackQueue: callbackQueue, @@ -442,6 +446,7 @@ extension PluginModule { module: ResolvedModule, action: PluginAction, buildEnvironment: BuildEnvironment, + workers: UInt32, scriptRunner: PluginScriptRunner, workingDirectory: AbsolutePath, outputDirectory: AbsolutePath, @@ -461,6 +466,7 @@ extension PluginModule { module: module, action: action, buildEnvironment: buildEnvironment, + workers: workers, scriptRunner: scriptRunner, workingDirectory: workingDirectory, outputDirectory: outputDirectory, @@ -488,6 +494,7 @@ extension PluginModule { module: ResolvedModule, action: PluginAction, buildEnvironment: BuildEnvironment, + workers: UInt32, scriptRunner: PluginScriptRunner, workingDirectory: AbsolutePath, outputDirectory: AbsolutePath, @@ -531,6 +538,7 @@ extension PluginModule { self.invoke( action: action, buildEnvironment: buildEnvironment, + workers: workers, scriptRunner: scriptRunner, workingDirectory: workingDirectory, outputDirectory: outputDirectory, diff --git a/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift b/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift index fda28b919a0..d4249a9591a 100644 --- a/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift +++ b/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift @@ -28,6 +28,7 @@ public protocol PluginScriptRunner { sourceFiles: [Basics.AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, + workers: UInt32, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginScriptCompilerDelegate, @@ -53,6 +54,7 @@ public protocol PluginScriptRunner { writableDirectories: [Basics.AbsolutePath], readOnlyDirectories: [Basics.AbsolutePath], allowNetworkConnections: [SandboxNetworkPermission], + workers: UInt32, fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -70,6 +72,7 @@ public extension PluginScriptRunner { sourceFiles: [Basics.AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, + workers: UInt32, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginScriptCompilerDelegate @@ -79,6 +82,7 @@ public extension PluginScriptRunner { sourceFiles: sourceFiles, pluginName: pluginName, toolsVersion: toolsVersion, + workers: workers, observabilityScope: observabilityScope, callbackQueue: callbackQueue, delegate: delegate, diff --git a/Sources/SwiftBuildSupport/PIFBuilder.swift b/Sources/SwiftBuildSupport/PIFBuilder.swift index 6e304910499..72fece44764 100644 --- a/Sources/SwiftBuildSupport/PIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PIFBuilder.swift @@ -302,6 +302,7 @@ public final class PIFBuilder { pluginGeneratedResources: pluginDerivedResources.map(\.path) ), buildEnvironment: buildParameters.buildEnvironment, + workers: buildParameters.workers, scriptRunner: pluginScriptRunner, workingDirectory: package.path, outputDirectory: pluginOutputDir, diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index b2a9ee05ffb..f69e9b59f5f 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -515,6 +515,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { sourceFiles: plugin.sources.paths, pluginName: plugin.moduleName, toolsVersion: plugin.toolsVersion, + workers: self.buildParameters.workers, observabilityScope: observabilityScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate diff --git a/Tests/BuildTests/PluginInvocationTests.swift b/Tests/BuildTests/PluginInvocationTests.swift index 7569ed86851..4d97913bc41 100644 --- a/Tests/BuildTests/PluginInvocationTests.swift +++ b/Tests/BuildTests/PluginInvocationTests.swift @@ -140,6 +140,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: [AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, + workers: UInt32, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginScriptCompilerDelegate, @@ -159,6 +160,7 @@ final class PluginInvocationTests: XCTestCase { writableDirectories: [AbsolutePath], readOnlyDirectories: [AbsolutePath], allowNetworkConnections: [SandboxNetworkPermission], + workers: UInt32, fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -384,6 +386,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: buildToolPlugin.sources.paths, pluginName: buildToolPlugin.name, toolsVersion: buildToolPlugin.apiVersion, + workers: 1, observabilityScope: observability.topScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate @@ -393,6 +396,7 @@ final class PluginInvocationTests: XCTestCase { XCTAssert(result.succeeded == false) XCTAssert(result.cached == false) XCTAssert(result.commandLine.contains(result.executableFile.pathString), "\(result.commandLine)") + XCTAssert(result.commandLine.contains("-j1"), "expected workers flag in \(result.commandLine)") XCTAssert(result.executableFile.components.contains("plugin-cache"), "\(result.executableFile.pathString)") XCTAssert(result.compilerOutput.contains("error: missing return"), "\(result.compilerOutput)") XCTAssert(result.diagnosticsFile.suffix == ".dia", "\(result.diagnosticsFile.pathString)") @@ -436,6 +440,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: buildToolPlugin.sources.paths, pluginName: buildToolPlugin.name, toolsVersion: buildToolPlugin.apiVersion, + workers: 1, observabilityScope: observability.topScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate @@ -486,6 +491,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: buildToolPlugin.sources.paths, pluginName: buildToolPlugin.name, toolsVersion: buildToolPlugin.apiVersion, + workers: 1, observabilityScope: observability.topScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate @@ -548,6 +554,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: buildToolPlugin.sources.paths, pluginName: buildToolPlugin.name, toolsVersion: buildToolPlugin.apiVersion, + workers: 1, observabilityScope: observability.topScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate @@ -601,6 +608,7 @@ final class PluginInvocationTests: XCTestCase { sourceFiles: buildToolPlugin.sources.paths, pluginName: buildToolPlugin.name, toolsVersion: buildToolPlugin.apiVersion, + workers: 1, observabilityScope: observability.topScope, callbackQueue: DispatchQueue.sharedConcurrent, delegate: delegate diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index a6354a3e2eb..c051afa5887 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -813,6 +813,7 @@ struct PluginTests { plugin.invoke( action: .performCommand(package: package, arguments: arguments), buildEnvironment: BuildEnvironment(platform: .macOS, configuration: .debug), + workers: 1, scriptRunner: scriptRunner, workingDirectory: package.path, outputDirectory: pluginDir.appending("output"), @@ -1112,6 +1113,7 @@ struct PluginTests { _ = try await plugin.invoke( action: .performCommand(package: package, arguments: []), buildEnvironment: BuildEnvironment(platform: .macOS, configuration: .debug), + workers: 1, scriptRunner: scriptRunner, workingDirectory: package.path, outputDirectory: pluginDir.appending("output"),