Skip to content

Commit 542279e

Browse files
authored
Remove obsolete experimental explicitly-built modules implementation (#8778)
The prototype implementation that this change removes has not been used in a couple of years and is not a viable approach long term, primarily due to lack of support for dynamic tasks required by Swift incremental builds. Instead, 'swift-build' integration will bring along the fully-fledged explicitly-built modules flow.
1 parent 573d8c1 commit 542279e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+58
-566
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift

Lines changed: 1 addition & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -106,26 +106,13 @@ extension LLBuildManifestBuilder {
106106
inputs: [Node],
107107
resolver: ArgsResolver,
108108
isMainModule: (Job) -> Bool,
109-
uniqueExplicitDependencyTracker: UniqueExplicitDependencyJobTracker? = nil
110109
) throws {
111110
// Add build jobs to the manifest
112111
for job in jobs {
113112
let tool = try resolver.resolve(.path(job.tool))
114113
let commandLine = try job.commandLine.map { try resolver.resolve($0) }
115114
let arguments = [tool] + commandLine
116115

117-
// Check if an explicit pre-build dependency job has already been
118-
// added as a part of this build.
119-
if let uniqueExplicitDependencyTracker,
120-
job.isExplicitDependencyPreBuildJob
121-
{
122-
if try !uniqueExplicitDependencyTracker.registerExplicitDependencyBuildJob(job) {
123-
// This is a duplicate of a previously-seen identical job.
124-
// Skip adding it to the manifest
125-
continue
126-
}
127-
}
128-
129116
let jobInputs = try job.inputs.map { try $0.resolveToNode(fileSystem: self.fileSystem) }
130117
let jobOutputs = try job.outputs.map { try $0.resolveToNode(fileSystem: self.fileSystem) }
131118

@@ -137,7 +124,7 @@ extension LLBuildManifestBuilder {
137124
// common intermediate dependency modules, such dependencies can lead
138125
// to cycles in the resulting manifest.
139126
var manifestNodeInputs: [Node] = []
140-
if targetDescription.buildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) {
127+
if !isMainModule(job) {
141128
manifestNodeInputs = jobInputs
142129
} else {
143130
manifestNodeInputs = (inputs + jobInputs).uniqued()
@@ -172,207 +159,6 @@ extension LLBuildManifestBuilder {
172159
}
173160
}
174161

175-
// Building a Swift module in Explicit Module Build mode requires passing all of its module
176-
// dependencies as explicit arguments to the build command. Thus, building a SwiftPM package
177-
// with multiple inter-dependent targets requires that each module’s build job must
178-
// have its module dependencies’ modules passed into it as explicit module dependencies.
179-
// Because none of the targets have been built yet, a given target's dependency scanning
180-
// action will not be able to discover its module dependencies' modules. Instead, it is
181-
// SwiftPM's responsibility to communicate to the driver, when planning a given module's
182-
// build, that this module has dependencies that are other targets, along with a list of
183-
// future artifacts of such dependencies (.swiftmodule and .pcm files).
184-
// The driver will then use those artifacts as explicit inputs to its module’s build jobs.
185-
//
186-
// Consider an example SwiftPM package with two targets: module B, and module A, where A
187-
// depends on B:
188-
// SwiftPM will process targets in a topological order and “bubble-up” each module’s
189-
// inter-module dependency graph to its dependencies. First, SwiftPM will process B, and be
190-
// able to plan its full build because it does not have any module dependencies. Then the
191-
// driver is tasked with planning a build for A. SwiftPM will pass as input to the driver
192-
// the module dependency graph of its module’s dependencies, in this case, just the
193-
// dependency graph of B. The driver is then responsible for the necessary post-processing
194-
// to merge the dependency graphs and plan the build for A, using artifacts of B as explicit
195-
// inputs.
196-
public func addTargetsToExplicitBuildManifest() throws {
197-
// Sort the product targets in topological order in order to collect and "bubble up"
198-
// their respective dependency graphs to the depending targets.
199-
let allPackageDependencies = self.plan.targets.flatMap { $0.recursiveDependencies(using: self.plan) }
200-
201-
// Instantiate the inter-module dependency oracle which will cache commonly-scanned
202-
// modules across targets' Driver instances.
203-
let dependencyOracle = InterModuleDependencyOracle()
204-
205-
// Explicit dependency pre-build jobs may be common to multiple targets.
206-
// We de-duplicate them here to avoid adding identical entries to the
207-
// downstream LLBuild manifest
208-
let explicitDependencyJobTracker = UniqueExplicitDependencyJobTracker()
209-
210-
// Create commands for all module descriptions in the plan.
211-
for dependency in allPackageDependencies.reversed() {
212-
guard case .module(let module, let description) = dependency else {
213-
// Product dependency build jobs are added after the fact.
214-
// Targets that depend on product dependencies will expand the corresponding
215-
// product into its constituent targets.
216-
continue
217-
}
218-
219-
guard module.underlying.type != .systemModule,
220-
module.underlying.type != .binary
221-
else {
222-
// Much like non-Swift targets, system modules will consist of a modulemap
223-
// somewhere in the filesystem, with the path to that module being either
224-
// manually-specified or computed based on the system module type (apt, brew).
225-
// Similarly, binary targets will bring in an .xcframework, the contents of
226-
// which will be exposed via search paths.
227-
//
228-
// In both cases, the dependency scanning action in the driver will be automatically
229-
// be able to detect such targets' modules.
230-
continue
231-
}
232-
233-
guard let description else {
234-
throw InternalError("Expected description for module \(module)")
235-
}
236-
237-
switch description {
238-
case .swift(let desc):
239-
try self.createExplicitSwiftTargetCompileCommand(
240-
description: desc,
241-
dependencyOracle: dependencyOracle,
242-
explicitDependencyJobTracker: explicitDependencyJobTracker
243-
)
244-
case .clang(let desc):
245-
try self.createClangCompileCommand(desc)
246-
}
247-
}
248-
}
249-
250-
private func createExplicitSwiftTargetCompileCommand(
251-
description: SwiftModuleBuildDescription,
252-
dependencyOracle: InterModuleDependencyOracle,
253-
explicitDependencyJobTracker: UniqueExplicitDependencyJobTracker?
254-
) throws {
255-
// Inputs.
256-
let inputs = try self.computeSwiftCompileCmdInputs(description)
257-
258-
// Outputs.
259-
let objectNodes = try description.objects.map(Node.file)
260-
let moduleNode = Node.file(description.moduleOutputPath)
261-
let cmdOutputs = objectNodes + [moduleNode]
262-
263-
// Commands.
264-
try addExplicitBuildSwiftCmds(
265-
description,
266-
inputs: inputs,
267-
dependencyOracle: dependencyOracle,
268-
explicitDependencyJobTracker: explicitDependencyJobTracker
269-
)
270-
271-
self.addTargetCmd(description, cmdOutputs: cmdOutputs)
272-
try self.addModuleWrapCmd(description)
273-
}
274-
275-
private func addExplicitBuildSwiftCmds(
276-
_ targetDescription: SwiftModuleBuildDescription,
277-
inputs: [Node],
278-
dependencyOracle: InterModuleDependencyOracle,
279-
explicitDependencyJobTracker: UniqueExplicitDependencyJobTracker? = nil
280-
) throws {
281-
// Pass the driver its external dependencies (module dependencies)
282-
var dependencyModuleDetailsMap: SwiftDriver.ExternalTargetModuleDetailsMap = [:]
283-
// Collect paths for module dependencies of this module (direct and transitive)
284-
try self.collectTargetDependencyModuleDetails(
285-
for: .swift(targetDescription),
286-
dependencyModuleDetailsMap: &dependencyModuleDetailsMap
287-
)
288-
289-
// Compute the set of frontend
290-
// jobs needed to build this Swift module.
291-
var commandLine = try targetDescription.emitCommandLine()
292-
commandLine.append("-driver-use-frontend-path")
293-
commandLine.append(targetDescription.buildParameters.toolchain.swiftCompilerPath.pathString)
294-
commandLine.append("-experimental-explicit-module-build")
295-
let resolver = try ArgsResolver(fileSystem: self.fileSystem)
296-
let executor = SPMSwiftDriverExecutor(
297-
resolver: resolver,
298-
fileSystem: self.fileSystem,
299-
env: Environment.current
300-
)
301-
var driver = try Driver(
302-
args: commandLine,
303-
fileSystem: self.fileSystem,
304-
executor: executor,
305-
compilerIntegratedTooling: false,
306-
externalTargetModuleDetailsMap: dependencyModuleDetailsMap,
307-
interModuleDependencyOracle: dependencyOracle
308-
)
309-
try driver.checkLDPathOption(commandLine: commandLine)
310-
311-
let jobs = try driver.planBuild()
312-
try self.addSwiftDriverJobs(
313-
for: targetDescription,
314-
jobs: jobs,
315-
inputs: inputs,
316-
resolver: resolver,
317-
isMainModule: { driver.isExplicitMainModuleJob(job: $0) },
318-
uniqueExplicitDependencyTracker: explicitDependencyJobTracker
319-
)
320-
}
321-
322-
/// Collect a map from all module dependencies of the specified module to the build planning artifacts for said
323-
/// dependency,
324-
/// in the form of a path to a .swiftmodule file and the dependency's InterModuleDependencyGraph.
325-
private func collectTargetDependencyModuleDetails(
326-
for targetDescription: ModuleBuildDescription,
327-
dependencyModuleDetailsMap: inout SwiftDriver.ExternalTargetModuleDetailsMap
328-
) throws {
329-
for dependency in targetDescription.dependencies(using: self.plan) {
330-
switch dependency {
331-
case .product(let product, let productDescription):
332-
for productDependency in product.modules {
333-
guard let dependencyModuleDescription = self.plan.description(
334-
for: productDependency,
335-
context: productDescription?.destination ?? targetDescription.destination
336-
) else
337-
{
338-
throw InternalError("unknown dependency target for \(productDependency)")
339-
}
340-
try self.addTargetDependencyInfo(
341-
for: dependencyModuleDescription,
342-
dependencyModuleDetailsMap: &dependencyModuleDetailsMap
343-
)
344-
}
345-
case .module(let dependencyModule, let dependencyDescription):
346-
guard let dependencyDescription else {
347-
throw InternalError("No build description for module: \(dependencyModule)")
348-
}
349-
// Product dependencies are broken down into the targets that make them up.
350-
try self.addTargetDependencyInfo(
351-
for: dependencyDescription,
352-
dependencyModuleDetailsMap: &dependencyModuleDetailsMap
353-
)
354-
}
355-
}
356-
}
357-
358-
private func addTargetDependencyInfo(
359-
for targetDescription: ModuleBuildDescription,
360-
dependencyModuleDetailsMap: inout SwiftDriver.ExternalTargetModuleDetailsMap
361-
) throws {
362-
guard case .swift(let dependencySwiftTargetDescription) = targetDescription else {
363-
return
364-
}
365-
dependencyModuleDetailsMap[ModuleDependencyId.swiftPlaceholder(targetDescription.module.c99name)] =
366-
SwiftDriver.ExternalTargetModuleDetails(
367-
path: TSCAbsolutePath(dependencySwiftTargetDescription.moduleOutputPath),
368-
isFramework: false
369-
)
370-
try self.collectTargetDependencyModuleDetails(
371-
for: targetDescription,
372-
dependencyModuleDetailsMap: &dependencyModuleDetailsMap
373-
)
374-
}
375-
376162
private func addCmdWithBuiltinSwiftTool(
377163
_ target: SwiftModuleBuildDescription,
378164
inputs: [Node],
@@ -581,32 +367,6 @@ extension LLBuildManifestBuilder {
581367
}
582368
}
583369

584-
extension SwiftDriver.Job {
585-
fileprivate var isExplicitDependencyPreBuildJob: Bool {
586-
(kind == .emitModule && inputs.contains { $0.file.extension == "swiftinterface" }) || kind == .generatePCM
587-
}
588-
}
589-
590-
/// A simple mechanism to keep track of already-known explicit module pre-build jobs.
591-
/// It uses the output filename of the job (either a `.swiftmodule` or a `.pcm`) for uniqueness,
592-
/// because the SwiftDriver encodes the module's context hash into this filename. Any two jobs
593-
/// producing an binary module file with an identical name are therefore duplicate
594-
private class UniqueExplicitDependencyJobTracker {
595-
private var uniqueDependencyModuleIDSet: Set<Int> = []
596-
597-
/// Registers the input Job with the tracker. Returns `false` if this job is already known
598-
func registerExplicitDependencyBuildJob(_ job: SwiftDriver.Job) throws -> Bool {
599-
guard job.isExplicitDependencyPreBuildJob,
600-
let soleOutput = job.outputs.spm_only
601-
else {
602-
throw InternalError("Expected explicit module dependency build job")
603-
}
604-
let jobUniqueID = soleOutput.file.basename.hashValue
605-
let (new, _) = self.uniqueDependencyModuleIDSet.insert(jobUniqueID)
606-
return new
607-
}
608-
}
609-
610370
extension TypedVirtualPath {
611371
/// Resolve a typed virtual path provided by the Swift driver to
612372
/// a node in the build graph.

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,26 +93,17 @@ public class LLBuildManifestBuilder {
9393

9494
addPackageStructureCommand()
9595
addBinaryDependencyCommands()
96-
if self.plan.destinationBuildParameters.driverParameters.useExplicitModuleBuild {
97-
// Explicit module builds use the integrated driver directly and
98-
// require that every target's build jobs specify its dependencies explicitly to plan
99-
// its build.
100-
// Currently behind:
101-
// --experimental-explicit-module-build
102-
try addTargetsToExplicitBuildManifest()
103-
} else {
104-
// Create commands for all target descriptions in the plan.
105-
for description in self.plan.targetMap {
106-
switch description {
107-
case .swift(let desc):
108-
try self.createSwiftCompileCommand(desc)
109-
case .clang(let desc):
110-
if desc.buildParameters.prepareForIndexing == .off {
111-
try self.createClangCompileCommand(desc)
112-
} else {
113-
// Hook up the clang module target when preparing
114-
try self.createClangPrepareCommand(desc)
115-
}
96+
// Create commands for all target descriptions in the plan.
97+
for description in self.plan.targetMap {
98+
switch description {
99+
case .swift(let desc):
100+
try self.createSwiftCompileCommand(desc)
101+
case .clang(let desc):
102+
if desc.buildParameters.prepareForIndexing == .off {
103+
try self.createClangCompileCommand(desc)
104+
} else {
105+
// Hook up the clang module target when preparing
106+
try self.createClangPrepareCommand(desc)
116107
}
117108
}
118109
}

Sources/CoreCommands/Options.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,6 @@ public struct BuildOptions: ParsableArguments {
530530
@Option(help: "A flag that indicates this build should check whether targets only import their explicitly-declared dependencies.")
531531
public var explicitTargetDependencyImportCheck: TargetDependencyImportCheckingMode = .none
532532

533-
/// Whether to use the explicit module build flow (with the integrated driver)
534-
@Flag(name: .customLong("experimental-explicit-module-build"))
535-
public var useExplicitModuleBuild: Bool = false
536-
537533
/// The build system to use.
538534
@Option(name: .customLong("build-system"))
539535
var _buildSystem: BuildSystemProvider.Kind = .native

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,6 @@ public final class SwiftCommandState {
422422
observabilityScope.emit(.unsupportedFlag("--multiroot-data-file"))
423423
}
424424

425-
if options.build.useExplicitModuleBuild && !options.build.useIntegratedSwiftDriver {
426-
observabilityScope
427-
.emit(error: "'--experimental-explicit-module-build' option requires '--use-integrated-swift-driver'")
428-
}
429-
430425
if !options.build.architectures.isEmpty && options.build.customCompileTriple != nil {
431426
observabilityScope.emit(.mutuallyExclusiveArgumentsError(arguments: ["--arch", "--triple"]))
432427
}
@@ -879,7 +874,6 @@ public final class SwiftCommandState {
879874
explicitTargetDependencyImportCheckingMode: self.options.build.explicitTargetDependencyImportCheck
880875
.modeParameter,
881876
useIntegratedSwiftDriver: self.options.build.useIntegratedSwiftDriver,
882-
useExplicitModuleBuild: self.options.build.useExplicitModuleBuild,
883877
isPackageAccessModifierSupported: DriverSupport.isPackageNameSupported(
884878
toolchain: toolchain,
885879
fileSystem: self.fileSystem

0 commit comments

Comments
 (0)