Skip to content

Commit d11ea74

Browse files
authored
Merge pull request #1868 from akyrtzi/akyrtzi/pr/6.2-fix-cas-size-limit
[6.2][SwiftScanCAS] Make sure that CAS size limitation can take effect
2 parents 6f5e8fe + e5bacf8 commit d11ea74

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

0 commit comments

Comments
 (0)