Skip to content

Commit 1d61ceb

Browse files
authored
[Explicit Module Builds] Deduplicate dependency build jobs before adding them to LLBuild manifest (#3769)
This avoids adding duplicate entries to the LLBuild manifest in explicit module builds by de-duplicating explicit dependency pre-build jobs based on their unique output filename.
1 parent 277cd76 commit 1d61ceb

File tree

1 file changed

+56
-7
lines changed

1 file changed

+56
-7
lines changed

Sources/Build/LLBuildManifestBuilder.swift

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,25 @@ extension LLBuildManifestBuilder {
237237
private func addSwiftDriverJobs(for targetDescription: SwiftTargetBuildDescription,
238238
jobs: [Job], inputs: [Node],
239239
resolver: ArgsResolver,
240-
isMainModule: (Job) -> Bool) throws {
240+
isMainModule: (Job) -> Bool,
241+
uniqueExplicitDependencyTracker: UniqueExplicitDependencyJobTracker? = nil) throws {
241242
// Add build jobs to the manifest
242243
for job in jobs {
243244
let tool = try resolver.resolve(.path(job.tool))
244245
let commandLine = try job.commandLine.map{ try resolver.resolve($0) }
245246
let arguments = [tool] + commandLine
246247

248+
// Check if an explicit pre-build dependency job has already been
249+
// added as a part of this build.
250+
if let uniqueDependencyTracker = uniqueExplicitDependencyTracker,
251+
job.isExplicitDependencyPreBuildJob {
252+
if try !uniqueDependencyTracker.registerExplicitDependencyBuildJob(job) {
253+
// This is a duplicate of a previously-seen identical job.
254+
// Skip adding it to the manifest
255+
continue
256+
}
257+
}
258+
247259
let jobInputs = try job.inputs.map { try $0.resolveToNode() }
248260
let jobOutputs = try job.outputs.map { try $0.resolveToNode() }
249261

@@ -320,6 +332,11 @@ extension LLBuildManifestBuilder {
320332
// modules across targets' Driver instances.
321333
let dependencyOracle = InterModuleDependencyOracle()
322334

335+
// Explicit dependency pre-build jobs may be common to multiple targets.
336+
// We de-duplicate them here to avoid adding identical entries to the
337+
// downstream LLBuild manifest
338+
let explicitDependencyJobTracker = UniqueExplicitDependencyJobTracker()
339+
323340
// Create commands for all target descriptions in the plan.
324341
for dependency in allPackageDependencies.reversed() {
325342
guard case .target(let target, _) = dependency else {
@@ -346,7 +363,8 @@ extension LLBuildManifestBuilder {
346363
switch description {
347364
case .swift(let desc):
348365
try self.createExplicitSwiftTargetCompileCommand(description: desc,
349-
dependencyOracle: dependencyOracle)
366+
dependencyOracle: dependencyOracle,
367+
explicitDependencyJobTracker: explicitDependencyJobTracker)
350368
case .clang(let desc):
351369
try self.createClangCompileCommand(desc)
352370
}
@@ -355,7 +373,8 @@ extension LLBuildManifestBuilder {
355373

356374
private func createExplicitSwiftTargetCompileCommand(
357375
description: SwiftTargetBuildDescription,
358-
dependencyOracle: InterModuleDependencyOracle
376+
dependencyOracle: InterModuleDependencyOracle,
377+
explicitDependencyJobTracker: UniqueExplicitDependencyJobTracker?
359378
) throws {
360379
// Inputs.
361380
let inputs = try self.computeSwiftCompileCmdInputs(description)
@@ -367,7 +386,8 @@ extension LLBuildManifestBuilder {
367386

368387
// Commands.
369388
try addExplicitBuildSwiftCmds(description, inputs: inputs,
370-
dependencyOracle: dependencyOracle)
389+
dependencyOracle: dependencyOracle,
390+
explicitDependencyJobTracker: explicitDependencyJobTracker)
371391

372392
self.addTargetCmd(description, cmdOutputs: cmdOutputs)
373393
self.addModuleWrapCmd(description)
@@ -376,7 +396,8 @@ extension LLBuildManifestBuilder {
376396
private func addExplicitBuildSwiftCmds(
377397
_ targetDescription: SwiftTargetBuildDescription,
378398
inputs: [Node],
379-
dependencyOracle: InterModuleDependencyOracle
399+
dependencyOracle: InterModuleDependencyOracle,
400+
explicitDependencyJobTracker: UniqueExplicitDependencyJobTracker? = nil
380401
) throws {
381402
// Pass the driver its external dependencies (target dependencies)
382403
var dependencyModulePathMap: SwiftDriver.ExternalTargetModulePathMap = [:]
@@ -399,7 +420,8 @@ extension LLBuildManifestBuilder {
399420
interModuleDependencyOracle: dependencyOracle)
400421
let jobs = try driver.planBuild()
401422
try addSwiftDriverJobs(for: targetDescription, jobs: jobs, inputs: inputs, resolver: resolver,
402-
isMainModule: { driver.isExplicitMainModuleJob(job: $0)})
423+
isMainModule: { driver.isExplicitMainModuleJob(job: $0)},
424+
uniqueExplicitDependencyTracker: explicitDependencyJobTracker)
403425
}
404426

405427
/// Collect a map from all target dependencies of the specified target to the build planning artifacts for said dependency,
@@ -631,7 +653,34 @@ extension LLBuildManifestBuilder {
631653
}
632654
}
633655

634-
// MARK: - Compile C-family
656+
fileprivate extension SwiftDriver.Job {
657+
var isExplicitDependencyPreBuildJob: Bool {
658+
return (kind == .emitModule &&
659+
inputs.contains { $0.file.extension == "swiftinterface" } ) ||
660+
kind == .generatePCM
661+
}
662+
}
663+
664+
/// A simple mechanism to keep track of already-known explicit module pre-build jobs.
665+
/// It uses the output filename of the job (either a `.swiftmodule` or a `.pcm`) for uniqueness,
666+
/// because the SwiftDriver encodes the module's context hash into this filename. Any two jobs
667+
/// producing an binary module file with an identical name are therefore duplicate
668+
fileprivate class UniqueExplicitDependencyJobTracker {
669+
private var uniqueDependencyModuleIDSet: Set<Int> = []
670+
671+
/// Registers the input Job with the tracker. Returns `false` if this job is already known
672+
func registerExplicitDependencyBuildJob(_ job: SwiftDriver.Job) throws -> Bool {
673+
guard job.isExplicitDependencyPreBuildJob,
674+
let soleOutput = job.outputs.spm_only else {
675+
throw InternalError("Expected explicit module dependency build job")
676+
}
677+
let jobUniqueID = soleOutput.file.basename.hashValue
678+
let (new, _) = uniqueDependencyModuleIDSet.insert(jobUniqueID)
679+
return new
680+
}
681+
}
682+
683+
// MARK:- Compile C-family
635684

636685
extension LLBuildManifestBuilder {
637686
/// Create a llbuild target for a Clang target description.

0 commit comments

Comments
 (0)