Skip to content

Commit 43bcf89

Browse files
Merge pull request #1789 from cachemeifyoucan/eng/PR-135718227
[Caching] Invalidate built module if missing from CAS
2 parents 072409a + 77ffaa2 commit 43bcf89

File tree

6 files changed

+109
-4
lines changed

6 files changed

+109
-4
lines changed

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ internal extension InterModuleDependencyGraph {
158158
/// between it and the root (source module being built by this driver
159159
/// instance) must also be re-built.
160160
func computeInvalidatedModuleDependencies(fileSystem: FileSystem,
161+
cas: SwiftScanCAS?,
161162
forRebuild: Bool,
162163
reporter: IncrementalCompilationState.Reporter? = nil)
163164
throws -> Set<ModuleDependencyId> {
@@ -169,7 +170,7 @@ internal extension InterModuleDependencyGraph {
169170
for dependencyId in mainModuleInfo.directDependencies ?? [] {
170171
try outOfDateModuleScan(from: dependencyId, visited: &visited,
171172
modulesRequiringRebuild: &modulesRequiringRebuild,
172-
fileSystem: fileSystem, forRebuild: forRebuild,
173+
fileSystem: fileSystem, cas: cas, forRebuild: forRebuild,
173174
reporter: reporter)
174175
}
175176

@@ -183,10 +184,11 @@ internal extension InterModuleDependencyGraph {
183184
/// filter out those with a fully up-to-date output
184185
func filterMandatoryModuleDependencyCompileJobs(_ allJobs: [Job],
185186
fileSystem: FileSystem,
187+
cas: SwiftScanCAS?,
186188
reporter: IncrementalCompilationState.Reporter? = nil) throws -> [Job] {
187189
// Determine which module pre-build jobs must be re-run
188190
let modulesRequiringReBuild =
189-
try computeInvalidatedModuleDependencies(fileSystem: fileSystem, forRebuild: true, reporter: reporter)
191+
try computeInvalidatedModuleDependencies(fileSystem: fileSystem, cas: cas, forRebuild: true, reporter: reporter)
190192

191193
// Filter the `.generatePCM` and `.compileModuleFromInterface` jobs for
192194
// modules which do *not* need re-building.
@@ -209,6 +211,7 @@ internal extension InterModuleDependencyGraph {
209211
visited: inout Set<ModuleDependencyId>,
210212
modulesRequiringRebuild: inout Set<ModuleDependencyId>,
211213
fileSystem: FileSystem,
214+
cas: SwiftScanCAS?,
212215
forRebuild: Bool,
213216
reporter: IncrementalCompilationState.Reporter? = nil) throws {
214217
let reportOutOfDate = { (name: String, reason: String) in
@@ -227,7 +230,7 @@ internal extension InterModuleDependencyGraph {
227230
if !visited.contains(dependencyId) {
228231
try outOfDateModuleScan(from: dependencyId, visited: &visited,
229232
modulesRequiringRebuild: &modulesRequiringRebuild,
230-
fileSystem: fileSystem, forRebuild: forRebuild,
233+
fileSystem: fileSystem, cas: cas, forRebuild: forRebuild,
231234
reporter: reporter)
232235
}
233236
// Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
@@ -237,7 +240,7 @@ internal extension InterModuleDependencyGraph {
237240
if hasOutOfDateModuleDependency {
238241
reportOutOfDate(sourceModuleId.moduleNameForDiagnostic, "Invalidated by downstream dependency")
239242
modulesRequiringRebuild.insert(sourceModuleId)
240-
} else if try !verifyModuleDependencyUpToDate(moduleID: sourceModuleId, fileSystem: fileSystem, reporter: reporter) {
243+
} else if try !verifyModuleDependencyUpToDate(moduleID: sourceModuleId, fileSystem: fileSystem, cas:cas, reporter: reporter) {
241244
reportOutOfDate(sourceModuleId.moduleNameForDiagnostic, "Out-of-date")
242245
modulesRequiringRebuild.insert(sourceModuleId)
243246
}
@@ -246,10 +249,44 @@ internal extension InterModuleDependencyGraph {
246249
visited.insert(sourceModuleId)
247250
}
248251

252+
func outputMissingFromCAS(moduleInfo: ModuleInfo,
253+
cas: SwiftScanCAS?) throws -> Bool {
254+
func casOutputMissing(_ key: String?) throws -> Bool {
255+
// Caching not enabled.
256+
guard let id = key, let cas = cas else { return false }
257+
// Do a local query to see if the output exists.
258+
let result = try cas.queryCacheKey(id, globally: false)
259+
// Make sure all outputs are available in local CAS.
260+
guard let outputs = result else { return true }
261+
return !outputs.allSatisfy { $0.isMaterialized }
262+
}
263+
264+
switch moduleInfo.details {
265+
case .swift(let swiftDetails):
266+
return try casOutputMissing(swiftDetails.moduleCacheKey)
267+
case .clang(let clangDetails):
268+
return try casOutputMissing(clangDetails.moduleCacheKey)
269+
case .swiftPrebuiltExternal(_):
270+
return false;
271+
case .swiftPlaceholder(_):
272+
// TODO: This should never ever happen. Hard error?
273+
return true;
274+
}
275+
}
276+
249277
func verifyModuleDependencyUpToDate(moduleID: ModuleDependencyId,
250278
fileSystem: FileSystem,
279+
cas: SwiftScanCAS?,
251280
reporter: IncrementalCompilationState.Reporter?) throws -> Bool {
252281
let checkedModuleInfo = try moduleInfo(of: moduleID)
282+
// Check if there is a module cache key available, then the content that pointed by the cache key must
283+
// exist for module to be up-to-date. Treat any CAS error as missing.
284+
let missingFromCAS = (try? outputMissingFromCAS(moduleInfo: checkedModuleInfo, cas: cas)) ?? true
285+
if missingFromCAS {
286+
reporter?.reportExplicitDependencyMissingFromCAS(moduleID.moduleName)
287+
return false
288+
}
289+
253290
// Verify that the specified input exists and is older than the specified output
254291
let verifyInputOlderThanOutputModTime: (String, VirtualPath, TimePoint) -> Bool =
255292
{ moduleName, inputPath, outputModTime in

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ extension IncrementalCompilationState {
317317
report("Following explicit module dependencies will be re-built: [\(modules.map { $0.moduleNameForDiagnostic }.sorted().joined(separator: ", "))]")
318318
}
319319

320+
func reportExplicitDependencyMissingFromCAS(_ moduleName: String) {
321+
report("Dependency module \(moduleName) is missing from CAS")
322+
}
323+
320324
// Emits a remark indicating incremental compilation has been disabled.
321325
func reportDisablingIncrementalBuild(_ why: String) {
322326
report("Disabling incremental build: \(why)")

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
120120

121121
// Verify that each dependnecy is up-to-date with respect to its inputs
122122
guard try priorInterModuleDependencyGraph.computeInvalidatedModuleDependencies(fileSystem: buildRecordInfo.fileSystem,
123+
cas: driver.cas,
123124
forRebuild: false,
124125
reporter: reporter).isEmpty else {
125126
reporter?.reportExplicitBuildMustReScan("Not all dependencies are up-to-date.")

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ extension Driver {
236236
mandatoryModuleCompileJobs =
237237
try resolvedDependencyGraph.filterMandatoryModuleDependencyCompileJobs(modulePrebuildJobs,
238238
fileSystem: fileSystem,
239+
cas: cas,
239240
reporter: reporter)
240241
}
241242
mandatoryModuleCompileJobs.forEach(addJob)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name O -parse-stdlib
3+
public func none() { }

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ final class IncrementalCompilationTests: XCTestCase {
4949
var priorsPath: AbsolutePath {
5050
derivedDataPath.appending(component: "\(module)-master.priors")
5151
}
52+
var casPath: AbsolutePath {
53+
derivedDataPath.appending(component: "cas")
54+
}
5255
func swiftDepsPath(basename: String) -> AbsolutePath {
5356
derivedDataPath.appending(component: "\(basename).swiftdeps")
5457
}
@@ -594,6 +597,59 @@ extension IncrementalCompilationTests {
594597
}
595598
}
596599

600+
// MARK: - Explicit compilation caching incremental tests
601+
extension IncrementalCompilationTests {
602+
func testIncrementalCompilationCaching() throws {
603+
#if os(Windows)
604+
throw XCTSkip("caching not supported on windows")
605+
#else
606+
let driver = try Driver(args: ["swiftc"])
607+
guard driver.isFeatureSupported(.compilation_caching) else {
608+
throw XCTSkip("caching not supported")
609+
}
610+
#endif
611+
let extraArguments = ["-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true), "-O", "-parse-stdlib"]
612+
replace(contentsOf: "other", with: "import O;")
613+
// Simplified initial build.
614+
try doABuild(
615+
"Initial Simplified Build with Caching",
616+
checkDiagnostics: false,
617+
extraArguments: explicitBuildArgs + extraArguments,
618+
whenAutolinking: autolinkLifecycleExpectedDiags) {
619+
startCompilingExplicitSwiftDependency("O")
620+
finishCompilingExplicitSwiftDependency("O")
621+
compiling("main", "other")
622+
}
623+
624+
// Delete the CAS, touch a file then rebuild.
625+
try localFileSystem.removeFileTree(casPath)
626+
627+
// Deleting the CAS should cause a full rebuild since all modules are missing from CAS.
628+
try doABuild(
629+
"Deleting CAS and rebuild",
630+
checkDiagnostics: false,
631+
extraArguments: explicitBuildArgs + extraArguments,
632+
whenAutolinking: autolinkLifecycleExpectedDiags
633+
) {
634+
readGraph
635+
readInterModuleGraph
636+
explicitDependencyModuleMissingFromCAS("O")
637+
moduleInfoStaleOutOfDate("O")
638+
explicitMustReScanDueToChangedDependencyInput
639+
moduleWillBeRebuiltOutOfDate("O")
640+
explicitModulesWillBeRebuilt(["O"])
641+
compilingExplicitSwiftDependency("O")
642+
foundBatchableJobs(2)
643+
formingOneBatch
644+
addingToBatchThenForming("main", "other")
645+
startCompilingExplicitSwiftDependency("O")
646+
finishCompilingExplicitSwiftDependency("O")
647+
compiling("main", "other")
648+
}
649+
}
650+
}
651+
652+
597653
// MARK: - Simpler incremental tests
598654
extension IncrementalCompilationTests {
599655

@@ -1764,6 +1820,9 @@ extension DiagVerifiable {
17641820
@DiagsBuilder func explicitModulesWillBeRebuilt(_ moduleNames: [String]) -> [Diagnostic.Message] {
17651821
"Incremental compilation: Following explicit module dependencies will be re-built: [\(moduleNames.joined(separator: ", "))]"
17661822
}
1823+
@DiagsBuilder func explicitDependencyModuleMissingFromCAS(_ dependencyModuleName: String) -> [Diagnostic.Message] {
1824+
"Dependency module \(dependencyModuleName) is missing from CAS"
1825+
}
17671826

17681827
// MARK: - misc
17691828
@DiagsBuilder func disabledForRemoval(_ removedInput: String) -> [Diagnostic.Message] {

0 commit comments

Comments
 (0)