Skip to content

Commit 7134aa1

Browse files
committed
[SwiftScanCAS] Make sure that CAS size limitation can take effect
Previously the last CAS instance that was closing the database was not the one that was getting the CAS size limitation setting, resulting in the limit not being in effect. rdar://148836977
1 parent e9ca0c6 commit 7134aa1

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)