Skip to content

Commit b0027b6

Browse files
authored
Merge pull request #1866 from akyrtzi/akyrtzi/pr/fix-cas-size-limit
[SwiftScanCAS] Make sure that CAS size limitation can take effect
2 parents d269d5a + 7134aa1 commit b0027b6

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

Sources/SwiftDriver/SwiftScan/SwiftScanCAS.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,18 @@ public final class CacheReplayResult {
170170

171171
public final class SwiftScanCAS {
172172
let cas: swiftscan_cas_t
173-
private let scanner: SwiftScan
173+
private var scanner: SwiftScan!
174174
deinit {
175-
scanner.api.swiftscan_cas_dispose(cas)
175+
// FIXME: `cas` needs to be disposed after `scanner`. This is because `scanner` contains a separate
176+
// CAS instance contained in `clang::CASOptions` but `cas` is the one exposed to the build system
177+
// and the one that a size limit is set on. When the `scanner` is disposed last then it's the last
178+
// instance closing the database and it doesn't impose any size limit.
179+
//
180+
// This is extremely fragile, a proper fix would be to either eliminate the extra CAS instance
181+
// from `scanner` or have the `scanner`'s CAS instance exposed to the build system.
182+
let swiftscan_cas_dispose = scanner.api.swiftscan_cas_dispose!
183+
scanner = nil
184+
swiftscan_cas_dispose(cas)
176185
}
177186

178187
init(cas: swiftscan_cas_t, scanner: SwiftScan) {

Tests/SwiftDriverTests/CachingBuildTests.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,4 +1116,92 @@ final class CachingBuildTests: XCTestCase {
11161116
try cas.prune()
11171117
}
11181118
}
1119+
1120+
func testCASSizeLimiting() throws {
1121+
try withTemporaryDirectory { path in
1122+
let moduleCachePath = path.appending(component: "ModuleCache")
1123+
let casPath = path.appending(component: "cas")
1124+
try localFileSystem.createDirectory(moduleCachePath)
1125+
1126+
let main1 = path.appending(component: "testCachingBuild1.swift")
1127+
try localFileSystem.writeFileContents(main1) { $0.send("let x = 1") }
1128+
let main2 = path.appending(component: "testCachingBuild2.swift")
1129+
try localFileSystem.writeFileContents(main2) { $0.send("let x = 1") }
1130+
1131+
let cHeadersPath: AbsolutePath =
1132+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
1133+
.appending(component: "CHeaders")
1134+
let swiftModuleInterfacesPath: AbsolutePath =
1135+
try testInputsPath.appending(component: "ExplicitModuleBuilds")
1136+
.appending(component: "Swift")
1137+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
1138+
1139+
func createDriver(main: AbsolutePath) throws -> Driver {
1140+
return try Driver(args: ["swiftc",
1141+
"-I", cHeadersPath.nativePathString(escaped: true),
1142+
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
1143+
"-explicit-module-build", "-Rcache-compile-job",
1144+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
1145+
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
1146+
"-working-directory", path.nativePathString(escaped: true),
1147+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting)
1148+
}
1149+
1150+
func buildAndGetSwiftCASKeys(main: AbsolutePath, forceCASLimit: Bool) throws -> [String] {
1151+
var driver = try createDriver(main: main)
1152+
let cas = try XCTUnwrap(driver.cas)
1153+
if forceCASLimit {
1154+
try cas.setSizeLimit(10)
1155+
}
1156+
let jobs = try driver.planBuild()
1157+
try driver.run(jobs: jobs)
1158+
XCTAssertFalse(driver.diagnosticEngine.hasErrors)
1159+
1160+
let dependencyOracle = driver.interModuleDependencyOracle
1161+
1162+
let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath())
1163+
try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath)
1164+
1165+
var keys: [String] = []
1166+
for job in jobs {
1167+
guard job.kind.supportCaching else { continue }
1168+
for (path, key) in job.outputCacheKeys {
1169+
if path.type == .swift {
1170+
keys.append(key)
1171+
}
1172+
}
1173+
}
1174+
return keys
1175+
}
1176+
1177+
func verifyKeys(exist: Bool, keys: [String], main: AbsolutePath, file: StaticString = #file, line: UInt = #line) throws {
1178+
let driver = try createDriver(main: main)
1179+
let cas = try XCTUnwrap(driver.cas)
1180+
for key in keys {
1181+
let comp = try cas.queryCacheKey(key, globally: false)
1182+
if exist {
1183+
XCTAssertNotNil(comp, file: file, line: line)
1184+
} else {
1185+
XCTAssertNil(comp, file: file, line: line)
1186+
}
1187+
}
1188+
}
1189+
1190+
do {
1191+
// Without CAS size limitation the keys will be preserved.
1192+
let keys = try buildAndGetSwiftCASKeys(main: main1, forceCASLimit: false)
1193+
_ = try buildAndGetSwiftCASKeys(main: main2, forceCASLimit: false)
1194+
try verifyKeys(exist: true, keys: keys, main: main1)
1195+
}
1196+
1197+
try localFileSystem.removeFileTree(casPath)
1198+
1199+
do {
1200+
// 2 separate builds with CAS size limiting, the keys of first build will not be preserved.
1201+
let keys = try buildAndGetSwiftCASKeys(main: main1, forceCASLimit: true)
1202+
_ = try buildAndGetSwiftCASKeys(main: main2, forceCASLimit: true)
1203+
try verifyKeys(exist: false, keys: keys, main: main1)
1204+
}
1205+
}
1206+
}
11191207
}

0 commit comments

Comments
 (0)