Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Sources/Build/BuildOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
// `BuildPlan`.
if let pluginConfiguration: PluginConfiguration, !self.config.shouldSkipBuilding(for: .target) {
let pluginsPerModule = graph.pluginsPerModule(
satisfying: self.config.buildEnvironment(for: .host)
satisfyingHost: self.config.buildEnvironment(for: .host),
targetEnvironment: self.config.buildEnvironment(for: .target)
)

pluginTools = try await buildPluginTools(
Expand Down
26 changes: 17 additions & 9 deletions Sources/Build/BuildPlan/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
for: module,
destination: destination,
configuration: pluginConfiguration,
buildParameters: toolsBuildParameters,
hostBuildParameters: toolsBuildParameters,
targetBuildEnvironment: buildParameters.buildEnvironment,
modulesGraph: graph,
tools: pluginTools,
additionalFileRules: additionalFileRules,
Expand Down Expand Up @@ -741,7 +742,8 @@ extension BuildPlan {
for module: ResolvedModule,
destination: BuildParameters.Destination,
configuration: PluginConfiguration,
buildParameters: BuildParameters,
hostBuildParameters: BuildParameters,
targetBuildEnvironment: BuildEnvironment,
modulesGraph: ModulesGraph,
tools: [ResolvedModule.ID: [String: PluginTool]],
additionalFileRules: [FileRuleDescription],
Expand All @@ -757,10 +759,16 @@ extension BuildPlan {
throw InternalError("could not determine package for module \(self)")
}

let enabledTraits = modulesGraph.enabledTraitsMap[package.identity]

// Apply each build tool plugin used by the target in order,
// creating a list of results (one for each plugin usage).
var buildToolPluginResults: [BuildToolPluginInvocationResult] = []
for plugin in module.pluginDependencies(satisfying: buildParameters.buildEnvironment) {
for plugin in module.pluginDependencies(
satisfying: hostBuildParameters.buildEnvironment,
targetEnvironment: targetBuildEnvironment,
enabledTraits: enabledTraits
) {
let pluginModule = plugin.underlying as! PluginModule

// Determine the tools to which this plugin has access, and create a name-to-path mapping from tool
Expand Down Expand Up @@ -801,14 +809,14 @@ extension BuildPlan {
target: module,
toolsVersion: package.manifest.toolsVersion,
additionalFileRules: additionalFileRules,
buildParameters: buildParameters,
buildParameters: hostBuildParameters,
buildToolPluginInvocationResults: buildToolPluginResults,
prebuildCommandResults: [],
observabilityScope: observability.topScope
)
pluginDerivedSources = Sources(
paths: pluginGeneratedFiles.sources.map(\.self),
root: buildParameters.dataPath
root: hostBuildParameters.dataPath
)
pluginDerivedResources = pluginGeneratedFiles.resources.values.map(\.self)
} else {
Expand All @@ -824,18 +832,18 @@ extension BuildPlan {
pluginGeneratedSources: pluginDerivedSources.paths,
pluginGeneratedResources: pluginDerivedResources.map(\.path)
),
buildEnvironment: buildParameters.buildEnvironment,
workers: buildParameters.workers,
buildEnvironment: hostBuildParameters.buildEnvironment,
workers: hostBuildParameters.workers,
scriptRunner: configuration.scriptRunner,
workingDirectory: package.path,
outputDirectory: pluginOutputDir,
toolSearchDirectories: [buildParameters.toolchain.swiftCompilerPath.parentDirectory],
toolSearchDirectories: [hostBuildParameters.toolchain.swiftCompilerPath.parentDirectory],
accessibleTools: accessibleTools,
writableDirectories: writableDirectories,
readOnlyDirectories: readOnlyDirectories,
allowNetworkConnections: [],
pkgConfigDirectories: pkgConfigDirectories,
sdkRootPath: buildParameters.toolchain.sdkRootPath,
sdkRootPath: hostBuildParameters.toolchain.sdkRootPath,
fileSystem: fileSystem,
modulesGraph: modulesGraph,
observabilityScope: observabilityScope
Expand Down
8 changes: 7 additions & 1 deletion Sources/Commands/PackageCommands/BuildServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ struct BuildServer: AsyncSwiftCommand {
sendMirrorFile: nil
)

let buildSystem = try await swiftCommandState.createBuildSystem(explicitBuildSystem: .swiftbuild)
// BSP / SourceKit-LSP feeds workspaces that can declare multiple ConfiguredTargets
// across platforms — this is the only context where one PIF generation can drive a
// multi-CT build.
let buildSystem = try await swiftCommandState.createBuildSystem(
explicitBuildSystem: .swiftbuild,
configuredTargetMode: .multiple
)
guard let swiftBuildSystem = buildSystem as? SwiftBuildSystem else {
throw ArgumentParser.ValidationError("Failed to initialize the '--build-system swiftbuild' backend; expected a 'SwiftBuildSystem' but got '\(buildSystem)'")
}
Expand Down
10 changes: 7 additions & 3 deletions Sources/CoreCommands/BuildSystemSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ private struct NativeBuildSystemFactory: BuildSystemFactory {
outputStream: OutputByteStream?,
logLevel: Diagnostic.Severity?,
observabilityScope: ObservabilityScope?,
delegate: BuildSystemDelegate?
delegate: BuildSystemDelegate?,
configuredTargetMode: PIFConfiguredTargetMode
) async throws -> any BuildSystem {
_ = try await swiftCommandState.getRootPackageInformation(enableAllTraits)
let testEntryPointPath = productsBuildParameters?.testProductStyle.explicitlySpecifiedEntryPointPath
Expand Down Expand Up @@ -89,7 +90,8 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory {
outputStream: OutputByteStream?,
logLevel: Diagnostic.Severity?,
observabilityScope: ObservabilityScope?,
delegate: BuildSystemDelegate?
delegate: BuildSystemDelegate?,
configuredTargetMode: PIFConfiguredTargetMode
) throws -> any BuildSystem {
return try XcodeBuildSystem(
buildParameters: productsBuildParameters ?? self.swiftCommandState.productsBuildParameters,
Expand Down Expand Up @@ -121,7 +123,8 @@ private struct SwiftBuildSystemFactory: BuildSystemFactory {
outputStream: OutputByteStream?,
logLevel: Diagnostic.Severity?,
observabilityScope: ObservabilityScope?,
delegate: BuildSystemDelegate?
delegate: BuildSystemDelegate?,
configuredTargetMode: PIFConfiguredTargetMode
) throws -> any BuildSystem {
return try SwiftBuildSystem(
buildParameters: productsBuildParameters ?? self.swiftCommandState.productsBuildParameters,
Expand All @@ -145,6 +148,7 @@ private struct SwiftBuildSystemFactory: BuildSystemFactory {
),
delegate: delegate,
scratchDirectory: self.swiftCommandState.scratchDirectory,
configuredTargetMode: configuredTargetMode,
)
}
}
Expand Down
6 changes: 4 additions & 2 deletions Sources/CoreCommands/SwiftCommandState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,8 @@ public final class SwiftCommandState {
outputStream: OutputByteStream? = .none,
logLevel: Basics.Diagnostic.Severity? = nil,
observabilityScope: ObservabilityScope? = .none,
delegate: BuildSystemDelegate? = nil
delegate: BuildSystemDelegate? = nil,
configuredTargetMode: PIFConfiguredTargetMode = .single
) async throws -> BuildSystem {

if self.options.build.useIntegratedSwiftDriver && self.options.build.buildSystem == .native {
Expand All @@ -995,7 +996,8 @@ public final class SwiftCommandState {
outputStream: outputStream,
logLevel: logLevel ?? self.logLevel,
observabilityScope: observabilityScope,
delegate: delegate
delegate: delegate,
configuredTargetMode: configuredTargetMode
)

// register the build system with the cancellation handler
Expand Down
82 changes: 77 additions & 5 deletions Sources/PackageGraph/ModulesGraph+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ extension ModulesGraph {
rootDependencies: resolvedPackages.filter { rootDependencies.contains($0.manifest) },
packages: resolvedPackages,
dependencies: requiredDependencies,
binaryArtifacts: binaryArtifacts
binaryArtifacts: binaryArtifacts,
enabledTraitsMap: enabledTraitsMap
)
}
}
Expand All @@ -237,19 +238,28 @@ private func checkAllDependenciesAreUsed(
for package in rootPackages {
// List all dependency products dependent on by the package modules.
let productDependencies = IdentifiableSet(package.modules.flatMap { module in
module.dependencies.compactMap { moduleDependency in
let dependencyProducts = module.dependencies.compactMap { moduleDependency in
switch moduleDependency {
case .product(let product, _):
product
case .module:
nil
}
}
let pluginProducts = module.pluginUsages.compactMap { pluginUsage in
switch pluginUsage {
case .product(let product, _):
product
case .module:
nil
}
}
return dependencyProducts + pluginProducts
})

// List all dependencies of modules that are guarded by a trait.
let traitGuardedProductDependencies = Set(package.underlying.modules.flatMap { module in
module.dependencies.compactMap { moduleDependency in
let dependencyProducts = module.dependencies.compactMap { moduleDependency in
switch moduleDependency {
case .product(let product, let conditions):
if conditions.contains(where: { $0.traitCondition != nil }) {
Expand All @@ -261,6 +271,18 @@ private func checkAllDependenciesAreUsed(
return nil
}
}
let pluginProducts = module.pluginUsages.compactMap { pluginUsage in
switch pluginUsage {
case .product(let product, let condition):
if let condition, !condition.traits.isEmpty {
return product.name
}
return nil
case .module:
return nil
}
}
return dependencyProducts + pluginProducts
})

for dependencyId in package.dependencies {
Expand Down Expand Up @@ -587,8 +609,8 @@ private func createResolvedPackages(
}
packageBuilder.modules = moduleBuilders

// Establish dependencies between the modules. A module can only depend on another module present in the same
// package.
// Establish dependencies and local plugin usages between the modules.
// A module can only depend on another module present in the same package.
let modulesMap = moduleBuilders.spm_createDictionary { ($0.module, $0) }
for moduleBuilder in moduleBuilders {
moduleBuilder.dependencies += try moduleBuilder.module.dependencies.compactMap { dependency in
Expand All @@ -607,6 +629,17 @@ private func createResolvedPackages(
return nil
}
}
moduleBuilder.pluginUsages += try moduleBuilder.module.pluginUsages.compactMap { usage in
switch usage {
case .module(let moduleDependency, let condition):
guard let moduleBuilder = modulesMap[moduleDependency] else {
throw InternalError("unknown plugin target \(moduleDependency.name)")
}
return .module(moduleBuilder, condition: condition)
case .product:
return nil
}
}
moduleBuilder.defaultLocalization = packageBuilder.defaultLocalization
moduleBuilder.supportedPlatforms = packageBuilder.supportedPlatforms
}
Expand Down Expand Up @@ -783,6 +816,26 @@ private func createResolvedPackages(

moduleBuilder.dependencies.append(.product(product, conditions: conditions))
}

// Establish product plugin usages.
for case .product(let productRef, let condition) in moduleBuilder.module.pluginUsages {
let product = lookupByProductIDs ? productDependencyMap[productRef.identity] :
productDependencyMap[productRef.name]
guard let product else {
if !observabilityScope.errorsReportedInAnyScope {
let error = prepareProductDependencyNotFoundError(
packageBuilder: packageBuilder,
moduleBuilder: moduleBuilder,
dependency: productRef,
lookupByProductIDs: lookupByProductIDs
)
packageObservabilityScope.emit(error)
}
continue
}

moduleBuilder.pluginUsages.append(.product(product, condition: condition))
}
}
}

Expand Down Expand Up @@ -1491,6 +1544,11 @@ private final class ResolvedModuleBuilder: ResolvedBuilder<ResolvedModule> {
case product(_ product: ResolvedProductBuilder, conditions: [PackageCondition])
}

enum PluginUsage {
case module(_ module: ResolvedModuleBuilder, condition: Module.PluginUsageCondition?)
case product(_ product: ResolvedProductBuilder, condition: Module.PluginUsageCondition?)
}

/// The reference to its package.
let packageIdentity: PackageIdentity

Expand All @@ -1500,6 +1558,9 @@ private final class ResolvedModuleBuilder: ResolvedBuilder<ResolvedModule> {
/// The module dependencies of this module.
var dependencies: [Dependency] = []

/// The plugin usages of this module.
var pluginUsages: [PluginUsage] = []

/// All the modules this module depends on ignoring the conditions
var allModuleDependencies: [ResolvedModuleBuilder] {
dependencies.flatMap {
Expand Down Expand Up @@ -1575,10 +1636,21 @@ private final class ResolvedModuleBuilder: ResolvedBuilder<ResolvedModule> {
}
}

let pluginUsages = try self.pluginUsages.map { usage -> ResolvedModule.PluginUsage in
switch usage {
case .module(let moduleBuilder, let condition):
return try .module(moduleBuilder.construct(), condition: condition)
case .product(let productBuilder, let condition):
let product = try productBuilder.construct()
return .product(product, condition: condition)
}
}

return ResolvedModule(
packageIdentity: self.packageIdentity,
underlying: self.module,
dependencies: dependencies,
pluginUsages: pluginUsages,
defaultLocalization: self.defaultLocalization,
supportedPlatforms: self.supportedPlatforms,
// maintain existing functionality and default to .all
Expand Down
7 changes: 6 additions & 1 deletion Sources/PackageGraph/ModulesGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,23 @@ public struct ModulesGraph {
/// Any binary artifacts referenced by the graph.
public let binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]]

/// The enabled traits map for all packages in the graph.
public let enabledTraitsMap: EnabledTraitsMap

/// Construct a package graph directly.
public init(
rootPackages: [ResolvedPackage],
rootDependencies: [ResolvedPackage] = [],
packages: IdentifiableSet<ResolvedPackage>,
dependencies requiredDependencies: [PackageReference],
binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]]
binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]],
enabledTraitsMap: EnabledTraitsMap = .init()
) throws {
let rootPackages = IdentifiableSet(rootPackages)
self.requiredDependencies = requiredDependencies
self.inputPackages = rootPackages + rootDependencies
self.binaryArtifacts = binaryArtifacts
self.enabledTraitsMap = enabledTraitsMap
self.packages = packages

var allModules = IdentifiableSet<ResolvedModule>()
Expand Down
Loading
Loading