Skip to content

Commit d6d5608

Browse files
Merge pull request #1491 from cachemeifyoucan/eng/PR-swift-caching-build-system-API-refine
[Caching] SwiftDriver Caching API refinement for build system
2 parents 4c7a044 + 1dec9d4 commit d6d5608

File tree

6 files changed

+100
-30
lines changed

6 files changed

+100
-30
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

+5
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,11 @@ typedef struct {
332332
void (*swiftscan_cache_cancellation_token_dispose)(
333333
swiftscan_cache_cancellation_token_t);
334334

335+
void (*swiftscan_cache_download_cas_object_async)(
336+
swiftscan_cas_t, const char *id, void *ctx,
337+
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
338+
swiftscan_cache_cancellation_token_t *);
339+
335340
swiftscan_cache_replay_instance_t (*swiftscan_cache_replay_instance_create)(
336341
int argc, const char **argv, swiftscan_string_ref_t *error);
337342
void (*swiftscan_cache_replay_instance_dispose)(

Sources/SwiftDriver/Driver/Driver.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public struct Driver {
275275
let useClangIncludeTree: Bool
276276

277277
/// CAS instance used for compilation.
278-
public var cas: SwiftScanCAS? = nil
278+
var cas: SwiftScanCAS? = nil
279279

280280
/// Is swift caching enabled.
281281
lazy var isCachingEnabled: Bool = {

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public class InterModuleDependencyOracle {
165165
return diags.isEmpty ? nil : diags
166166
}
167167

168-
@_spi(Testing) public func createCAS(pluginPath: AbsolutePath?, onDiskPath: AbsolutePath?, pluginOptions: [(String, String)]) throws -> SwiftScanCAS {
168+
public func createCAS(pluginPath: AbsolutePath?, onDiskPath: AbsolutePath?, pluginOptions: [(String, String)]) throws -> SwiftScanCAS {
169169
guard let swiftScan = swiftScanLibInstance else {
170170
fatalError("Attempting to reset scanner cache with no scanner instance.")
171171
}

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

+2
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ private extension swiftscan_functions_t {
528528
self.swiftscan_cache_action_cancel = try loadOptional("swiftscan_cache_action_cancel")
529529
self.swiftscan_cache_cancellation_token_dispose = try loadOptional("swiftscan_cache_cancellation_token_dispose")
530530

531+
self.swiftscan_cache_download_cas_object_async = try loadOptional("swiftscan_cache_download_cas_object_async")
532+
531533
self.swiftscan_cache_replay_instance_create = try loadOptional("swiftscan_cache_replay_instance_create")
532534
self.swiftscan_cache_replay_instance_dispose = try loadOptional("swiftscan_cache_replay_instance_dispose")
533535
self.swiftscan_cache_replay_compilation = try loadOptional("swiftscan_cache_replay_compilation")

Sources/SwiftDriver/SwiftScan/SwiftScanCAS.swift

+68-26
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,36 @@ public final class CachedCompilation {
3737
lib.api.swiftscan_cached_compilation_is_uncacheable(ptr)
3838
}
3939

40+
public func makeGlobal(_ callback: @escaping (Swift.Error?) -> ()) {
41+
class CallbackContext {
42+
func retain() -> UnsafeMutableRawPointer {
43+
return Unmanaged.passRetained(self).toOpaque()
44+
}
45+
46+
let comp: CachedCompilation
47+
let callback: (Swift.Error?) -> ()
48+
init(_ compilation: CachedCompilation, _ callback: @escaping (Swift.Error?) -> ()) {
49+
self.comp = compilation
50+
self.callback = callback
51+
}
52+
}
53+
54+
func callbackFunc(_ context: UnsafeMutableRawPointer?, _ error: swiftscan_string_ref_t) {
55+
let obj = Unmanaged<CallbackContext>.fromOpaque(context!).takeRetainedValue()
56+
if error.length != 0 {
57+
if let err = try? obj.comp.lib.toSwiftString(error) {
58+
obj.callback(DependencyScanningError.casError(err))
59+
} else {
60+
obj.callback(DependencyScanningError.casError("unknown makeGlobal error"))
61+
}
62+
}
63+
obj.callback(nil)
64+
}
65+
66+
let context = CallbackContext(self, callback)
67+
lib.api.swiftscan_cached_compilation_make_global_async(ptr, context.retain(), callbackFunc, nil)
68+
}
69+
4070
deinit {
4171
lib.api.swiftscan_cached_compilation_dispose(ptr)
4272
}
@@ -235,35 +265,15 @@ extension swiftscan_cache_replay_result_t {
235265
#if canImport(_Concurrency)
236266
// Async API Vendor
237267
extension CachedCompilation {
238-
public func makeGlobal() async throws -> Bool {
239-
class CallbackContext {
240-
func retain() -> UnsafeMutableRawPointer {
241-
return Unmanaged.passRetained(self).toOpaque()
242-
}
243-
244-
let continuation: CheckedContinuation<Bool, Swift.Error>
245-
let comp: CachedCompilation
246-
init(_ continuation: CheckedContinuation<Bool, Swift.Error>, compilation: CachedCompilation) {
247-
self.continuation = continuation
248-
self.comp = compilation
249-
}
250-
}
251-
252-
func callbackFunc(_ context: UnsafeMutableRawPointer?, _ error: swiftscan_string_ref_t) {
253-
let obj = Unmanaged<CallbackContext>.fromOpaque(context!).takeRetainedValue()
254-
if error.length != 0 {
255-
if let err = try? obj.comp.lib.toSwiftString(error) {
256-
obj.continuation.resume(throwing: DependencyScanningError.casError(err))
268+
public func makeGlobal() async throws {
269+
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Swift.Error>) in
270+
makeGlobal { (error: Swift.Error?) in
271+
if let err = error {
272+
continuation.resume(throwing: err)
257273
} else {
258-
obj.continuation.resume(throwing: DependencyScanningError.casError("unknown makeGlobal error"))
274+
continuation.resume(returning: ())
259275
}
260276
}
261-
obj.continuation.resume(returning: true)
262-
}
263-
264-
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Bool, Swift.Error>) in
265-
let context = CallbackContext(continuation, compilation: self)
266-
lib.api.swiftscan_cached_compilation_make_global_async(ptr, context.retain(), callbackFunc, nil)
267277
}
268278
}
269279
}
@@ -334,5 +344,37 @@ extension SwiftScanCAS {
334344
scanner.api.swiftscan_cache_query_async(cas, key.cString(using: .utf8), globally, context.retain(), callbackFunc, nil)
335345
}
336346
}
347+
348+
public func download(with id: String) async throws -> Bool {
349+
class CallbackContext {
350+
func retain() -> UnsafeMutableRawPointer {
351+
return Unmanaged.passRetained(self).toOpaque()
352+
}
353+
354+
let continuation: CheckedContinuation<Bool, Swift.Error>
355+
let cas: SwiftScanCAS
356+
init(_ continuation: CheckedContinuation<Bool, Swift.Error>, cas: SwiftScanCAS) {
357+
self.continuation = continuation
358+
self.cas = cas
359+
}
360+
}
361+
362+
func callbackFunc(_ context: UnsafeMutableRawPointer?, _ success: Bool, _ error: swiftscan_string_ref_t) {
363+
let obj = Unmanaged<CallbackContext>.fromOpaque(context!).takeRetainedValue()
364+
if error.length != 0 {
365+
if let err = try? obj.cas.scanner.toSwiftString(error) {
366+
obj.continuation.resume(throwing: DependencyScanningError.casError(err))
367+
} else {
368+
obj.continuation.resume(throwing: DependencyScanningError.casError("unknown output loading error"))
369+
}
370+
}
371+
obj.continuation.resume(returning: success)
372+
}
373+
374+
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Bool, Swift.Error>) in
375+
let context = CallbackContext(continuation, cas: self)
376+
scanner.api.swiftscan_cache_download_cas_object_async(cas, id.cString(using: .utf8), context.retain(), callbackFunc, nil)
377+
}
378+
}
337379
}
338380
#endif

Tests/SwiftDriverTests/CachingBuildTests.swift

+23-2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ private func checkCASForResults(jobs: [Job], cas: SwiftScanCAS, fs: FileSystem)
119119
XCTAssertTrue(output.isMaterialized, "Cached output not founded in CAS")
120120
let success = try await output.load()
121121
XCTAssertTrue(success, "Cached output not founded in CAS")
122+
123+
// Try async download. Download should succeed even on a local CAS.
124+
let casID = try output.getCASID()
125+
let downloaded = try await cas.download(with: casID)
126+
XCTAssertTrue(downloaded, "Cached output cannot be downloaded")
127+
}
128+
// Execise the uploading path.
129+
try await compilation.makeGlobal()
130+
// Execise call back uploading method.
131+
compilation.makeGlobal { error in
132+
XCTAssertTrue(error == nil, "Upload Error")
122133
}
123134
compilations.append(compilation)
124135
} else {
@@ -487,10 +498,17 @@ final class CachingBuildTests: XCTestCase {
487498
let jobs = try driver.planBuild()
488499
try driver.run(jobs: jobs)
489500
XCTAssertFalse(driver.diagnosticEngine.hasErrors)
490-
guard let cas = driver.cas else {
491-
XCTFail("CAS is not available")
501+
502+
let dependencyOracle = InterModuleDependencyOracle()
503+
let scanLibPath = try XCTUnwrap(driver.toolchain.lookupSwiftScanLib())
504+
guard try dependencyOracle
505+
.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
506+
swiftScanLibPath: scanLibPath) else {
507+
XCTFail("Dependency scanner library not found")
492508
return
493509
}
510+
511+
let cas = try dependencyOracle.createCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: [])
494512
try checkCASForResults(jobs: jobs, cas: cas, fs: driver.fileSystem)
495513
}
496514
}
@@ -555,6 +573,9 @@ final class CachingBuildTests: XCTestCase {
555573
try fooBuildDriver.run(jobs: fooJobs)
556574
XCTAssertFalse(fooBuildDriver.diagnosticEngine.hasErrors)
557575

576+
let cas = try dependencyOracle.createCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: [])
577+
try checkCASForResults(jobs: fooJobs, cas: cas, fs: fooBuildDriver.fileSystem)
578+
558579
var driver = try Driver(args: ["swiftc",
559580
"-I", FooInstallPath.nativePathString(escaped: true),
560581
"-explicit-module-build", "-emit-module", "-emit-module-path",

0 commit comments

Comments
 (0)