Skip to content

Commit 4c7a044

Browse files
Merge pull request #1498 from cachemeifyoucan/eng/PR-swift-caching-incremental-build-bridging-header
[Caching] Fix incremental cache build with bridging header
2 parents 3a73847 + dd7d4c6 commit 4c7a044

File tree

6 files changed

+87
-13
lines changed

6 files changed

+87
-13
lines changed

Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
8383
Dictionary(uniqueKeysWithValues:
8484
jobsInPhases.compileGroups.map { ($0.primaryInput, $0) })
8585
let buildRecord = self.moduleDependencyGraph.buildRecord
86+
let jobCreatingPch = jobsInPhases.beforeCompiles.first(where: {$0.kind == .generatePCH})
8687
guard !buildRecord.inputInfos.isEmpty else {
8788
func everythingIsMandatory()
8889
throws -> (initiallySkippedCompileGroups: [TypedVirtualPath: CompileJobGroup],
@@ -97,7 +98,8 @@ extension IncrementalCompilationState.FirstWaveComputer {
9798
jobsInPhases.beforeCompiles +
9899
batchJobFormer.formBatchedJobs(
99100
mandatoryCompileGroupsInOrder.flatMap {$0.allJobs()},
100-
showJobLifecycle: showJobLifecycle)
101+
showJobLifecycle: showJobLifecycle,
102+
jobCreatingPch: jobCreatingPch)
101103

102104
moduleDependencyGraph.setPhase(to: .buildingAfterEachCompilation)
103105
return (initiallySkippedCompileGroups: [:],
@@ -124,7 +126,8 @@ extension IncrementalCompilationState.FirstWaveComputer {
124126
let mandatoryBeforeCompilesJobs = try computeMandatoryBeforeCompilesJobs()
125127
let batchedCompilationJobs = try batchJobFormer.formBatchedJobs(
126128
mandatoryCompileGroupsInOrder.flatMap {$0.allJobs()},
127-
showJobLifecycle: showJobLifecycle)
129+
showJobLifecycle: showJobLifecycle,
130+
jobCreatingPch: jobCreatingPch)
128131

129132
// In the case where there are no compilation jobs to run on this build (no source-files were changed),
130133
// we can skip running `beforeCompiles` jobs if we also ensure that none of the `afterCompiles` jobs

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationProtectedState.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@ extension IncrementalCompilationState {
3434
/// fileprivate in order to control concurrency.
3535
fileprivate let moduleDependencyGraph: ModuleDependencyGraph
3636

37+
fileprivate let jobCreatingPch: Job?
3738
fileprivate let reporter: Reporter?
3839

3940
init(skippedCompileGroups: [TypedVirtualPath: CompileJobGroup],
4041
_ moduleDependencyGraph: ModuleDependencyGraph,
42+
_ jobCreatingPch: Job?,
4143
_ driver: inout Driver) {
4244
self.skippedCompileGroups = skippedCompileGroups
4345
self.moduleDependencyGraph = moduleDependencyGraph
4446
self.reporter = moduleDependencyGraph.info.reporter
47+
self.jobCreatingPch = jobCreatingPch
4548
self.driver = driver
4649
}
4750
}
@@ -61,7 +64,7 @@ extension IncrementalCompilationState.ProtectedState {
6164
mutationSafetyPrecondition()
6265
// batch in here to protect the Driver from concurrent access
6366
return try collectUnbatchedJobsDiscoveredToBeNeededAfterFinishing(job: finishedJob)
64-
.map {try driver.formBatchedJobs($0, showJobLifecycle: driver.showJobLifecycle)}
67+
.map {try driver.formBatchedJobs($0, showJobLifecycle: driver.showJobLifecycle, jobCreatingPch: jobCreatingPch)}
6568
}
6669

6770
/// Remember a job (group) that is before a compile or a compile itself.

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public final class IncrementalCompilationState {
7777
self.protectedState = ProtectedState(
7878
skippedCompileGroups: firstWave.initiallySkippedCompileGroups,
7979
initialState.graph,
80+
jobsInPhases.allJobs.first(where: {$0.kind == .generatePCH}),
8081
&driver)
8182
self.mandatoryJobsInOrder = firstWave.mandatoryJobsInOrder
8283
self.jobsAfterCompiles = jobsInPhases.afterCompiles

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -453,14 +453,12 @@ extension Driver {
453453
pchCompileJob: Job?) throws {
454454
guard let pchJob = pchCompileJob, isCachingEnabled else { return }
455455

456-
// The pch input file (the bridging header) is added as last inputs to the job.
457-
guard let inputFile = pchJob.inputs.last else { assertionFailure("no input files from pch job"); return }
458-
assert(inputFile.type == .objcHeader, "Expect objc header input type")
459-
let bridgingHeaderCacheKey = try computeOutputCacheKey(commandLine: pchJob.commandLine,
460-
input: inputFile)
461-
guard let key = bridgingHeaderCacheKey else { return }
456+
assert(pchJob.outputCacheKeys.count == 1, "Expect one and only one cache key from pch job")
457+
guard let bridgingHeaderCacheKey = pchJob.outputCacheKeys.first?.value else {
458+
throw Error.unsupportedConfigurationForCaching("pch job doesn't have an associated cache key")
459+
}
462460
commandLine.appendFlag("-bridging-header-pch-key")
463-
commandLine.appendFlag(key)
461+
commandLine.appendFlag(bridgingHeaderCacheKey)
464462
}
465463

466464
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate],

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ extension Driver {
127127
// can be returned from `planBuild`.
128128
// But in that case, don't emit lifecycle messages.
129129
formBatchedJobs(jobsInPhases.allJobs,
130-
showJobLifecycle: showJobLifecycle && incrementalCompilationState == nil),
130+
showJobLifecycle: showJobLifecycle && incrementalCompilationState == nil,
131+
jobCreatingPch: jobsInPhases.allJobs.first(where: {$0.kind == .generatePCH})),
131132
incrementalCompilationState
132133
)
133134
}
@@ -838,7 +839,7 @@ extension Driver {
838839
///
839840
/// So, in order to avoid making jobs and rebatching, the code would have to just get outputs for each
840841
/// compilation. But `compileJob` intermixes the output computation with other stuff.
841-
mutating func formBatchedJobs(_ jobs: [Job], showJobLifecycle: Bool) throws -> [Job] {
842+
mutating func formBatchedJobs(_ jobs: [Job], showJobLifecycle: Bool, jobCreatingPch: Job?) throws -> [Job] {
842843
guard compilerMode.isBatchCompile else {
843844
// Don't even go through the logic so as to not print out confusing
844845
// "batched foobar" messages.
@@ -864,7 +865,6 @@ extension Driver {
864865
compileJobs.filter { $0.outputs.contains {$0.type == .moduleTrace} }
865866
.flatMap {$0.primaryInputs}
866867
)
867-
let jobCreatingPch: Job? = jobs.first(where: {$0.kind == .generatePCH})
868868

869869
let batchedCompileJobs = try inputsInOrder.compactMap { anInput -> Job? in
870870
let idx = partitions.assignment[anInput]!

Tests/SwiftDriverTests/CachingBuildTests.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,4 +761,73 @@ final class CachingBuildTests: XCTestCase {
761761
}
762762
}
763763
}
764+
765+
func testCacheIncrementalBuildPlan() throws {
766+
try withTemporaryDirectory { path in
767+
try localFileSystem.changeCurrentWorkingDirectory(to: path)
768+
let moduleCachePath = path.appending(component: "ModuleCache")
769+
let casPath = path.appending(component: "cas")
770+
try localFileSystem.createDirectory(moduleCachePath)
771+
let main = path.appending(component: "testCachingBuild.swift")
772+
let mainFileContent = "import C;import E;import G;"
773+
try localFileSystem.writeFileContents(main) {
774+
$0.send(mainFileContent)
775+
}
776+
let ofm = path.appending(component: "ofm.json")
777+
let inputPathsAndContents: [(AbsolutePath, String)] = [(main, mainFileContent)]
778+
OutputFileMapCreator.write(
779+
module: "Test", inputPaths: inputPathsAndContents.map {$0.0},
780+
derivedData: path, to: ofm, excludeMainEntry: false)
781+
782+
let cHeadersPath: AbsolutePath =
783+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
784+
.appending(component: "CHeaders")
785+
let swiftModuleInterfacesPath: AbsolutePath =
786+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
787+
.appending(component: "Swift")
788+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
789+
let bridgingHeaderpath: AbsolutePath =
790+
cHeadersPath.appending(component: "Bridging.h")
791+
var driver = try Driver(args: ["swiftc",
792+
"-I", cHeadersPath.nativePathString(escaped: true),
793+
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
794+
"-explicit-module-build", "-v", "-Rcache-compile-job", "-incremental",
795+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
796+
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
797+
"-import-objc-header", bridgingHeaderpath.nativePathString(escaped: true),
798+
"-output-file-map", ofm.nativePathString(escaped: true),
799+
"-working-directory", path.nativePathString(escaped: true),
800+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting,
801+
env: ProcessEnv.vars)
802+
guard driver.isFeatureSupported(.cache_compile_job) else {
803+
throw XCTSkip("toolchain does not support caching.")
804+
}
805+
let jobs = try driver.planBuild()
806+
try driver.run(jobs: jobs)
807+
XCTAssertFalse(driver.diagnosticEngine.hasErrors)
808+
809+
let dependencyOracle = InterModuleDependencyOracle()
810+
let scanLibPath = try XCTUnwrap(driver.toolchain.lookupSwiftScanLib())
811+
guard try dependencyOracle
812+
.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
813+
swiftScanLibPath: scanLibPath) else {
814+
XCTFail("Dependency scanner library not found")
815+
return
816+
}
817+
818+
let cas = try dependencyOracle.createCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: [])
819+
try checkCASForResults(jobs: jobs, cas: cas, fs: driver.fileSystem)
820+
821+
// try replan the job and make sure some key command-line options are generated.
822+
let rebuildJobs = try driver.planBuild()
823+
for job in rebuildJobs {
824+
if job.kind == .compile || job.kind == .emitModule {
825+
XCTAssertTrue(job.commandLine.contains(.flag(String("-disable-implicit-swift-modules"))))
826+
XCTAssertTrue(job.commandLine.contains(.flag(String("-cache-compile-job"))))
827+
XCTAssertTrue(job.commandLine.contains(.flag(String("-cas-path"))))
828+
XCTAssertTrue(job.commandLine.contains(.flag(String("-bridging-header-pch-key"))))
829+
}
830+
}
831+
}
832+
}
764833
}

0 commit comments

Comments
 (0)