Skip to content

Commit 9565794

Browse files
[Caching] Add swift-scan-test to test libSwiftScan APIs
Add a new test tool, that is a very simple driver to call into libSwiftScan APIs. This enables testing libSwiftScan interfaces directly, without relying on cross repo testing from swift-driver. This allows adding new tests for cache replay feature, that has quite some unique code path from how replay works in swift-frontend.
1 parent 4e1e41a commit 9565794

File tree

7 files changed

+435
-22
lines changed

7 files changed

+435
-22
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/// SWIFTSCAN_VERSION_MINOR should increase when there are API additions.
2626
/// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes.
2727
#define SWIFTSCAN_VERSION_MAJOR 0
28-
#define SWIFTSCAN_VERSION_MINOR 5
28+
#define SWIFTSCAN_VERSION_MINOR 6
2929

3030
SWIFTSCAN_BEGIN_DECLS
3131

@@ -441,6 +441,52 @@ typedef struct swiftscan_cas_options_s *swiftscan_cas_options_t;
441441
/// ActionCache.
442442
typedef struct swiftscan_cas_s *swiftscan_cas_t;
443443

444+
/// Opaque container for a cached compilation.
445+
typedef struct swiftscan_cached_compilation_s *swiftscan_cached_compilation_t;
446+
447+
/// Opaque type for a cache replay instance.
448+
typedef struct swiftscan_cache_replay_instance_s
449+
*swiftscan_cache_replay_instance_t;
450+
451+
/// Opaque type for a cancellation token for async cache operations.
452+
typedef struct swiftscan_cache_cancellation_token_s
453+
*swiftscan_cache_cancellation_token_t;
454+
455+
/// Enum types for output types for cache key computation.
456+
/// List should contain all the output file types, including supplemententary
457+
/// outputs, except diagnostics outputs, which are covered by cached diagnostic
458+
/// entry.
459+
typedef enum {
460+
SWIFTSCAN_OUTPUT_TYPE_OBJECT = 0,
461+
SWIFTSCAN_OUTPUT_TYPE_SWIFTMODULE = 1,
462+
SWIFTSCAN_OUTPUT_TYPE_SWIFTINTERFACE = 2,
463+
SWIFTSCAN_OUTPUT_TYPE_SWIFTPRIVATEINTERFACE = 3,
464+
SWIFTSCAN_OUTPUT_TYPE_CLANG_MODULE = 4,
465+
SWIFTSCAN_OUTPUT_TYPE_CLANG_PCH = 5,
466+
SWIFTSCAN_OUTPUT_TYPE_CLANG_HEADER = 6,
467+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_SOURCE_INFO = 7,
468+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_MODULE_DOC = 8,
469+
SWIFTSCAN_OUTPUT_TYPE_DEPENDENCIES = 9,
470+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_DEPS = 10,
471+
SWIFTSCAN_OUTPUT_TYPE_MODULE_TRACE = 11,
472+
SWIFTSCAN_OUTPUT_TYPE_TBD = 12,
473+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_MODULE_SUMMARY = 13,
474+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_ABI_DESCRIPTOR = 14,
475+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_API_DESCRIPTOR = 15,
476+
SWIFTSCAN_OUTPUT_TYPE_CONST_VALUE = 16,
477+
SWIFTSCAN_OUTPUT_TYPE_MODULE_SEMANTIC_INFO = 17,
478+
SWIFTSCAN_OUTPUT_TYPE_YAML_OPT_RECORD = 18,
479+
SWIFTSCAN_OUTPUT_TYPE_BITSTREAM_OPT_RECORD = 19,
480+
SWIFTSCAN_OUTPUT_TYPE_CACHED_DIAGNOSTICS = 20
481+
} swiftscan_output_kind_t;
482+
483+
/// Enum types for cache result lookup or replay.
484+
typedef enum {
485+
SWIFTSCAN_CACHE_RESULT_SUCCESS = 0,
486+
SWIFTSCAN_CACHE_RESULT_NOT_FOUND = 1,
487+
SWIFTSCAN_CACHE_RESULT_ERROR = 2,
488+
} swiftscan_cache_result_t;
489+
444490
/// Create a \c CASOptions for creating CAS inside scanner specified.
445491
SWIFTSCAN_PUBLIC swiftscan_cas_options_t swiftscan_cas_options_create(void);
446492

@@ -492,6 +538,129 @@ SWIFTSCAN_PUBLIC swiftscan_string_ref_t
492538
swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
493539
const char *input, swiftscan_string_ref_t *error);
494540

541+
/// Query the result of the compilation using the output cache key. \c globally
542+
/// suggests if the lookup should check remote cache if such operation exists.
543+
/// Returns the CachedCompilation of the result if found, or nullptr if output
544+
/// is not found or an error occurs. When an error occurs, the error message is
545+
/// returned via \c error parameter and its caller needs to free the message
546+
/// using `swiftscan_string_dispose`. The returned CachedCompilation needs to be
547+
/// freed via `swiftscan_cached_compilation_dispose`.
548+
SWIFTSCAN_PUBLIC swiftscan_cached_compilation_t
549+
swiftscan_cache_query(swiftscan_cas_t cas, const char *key, bool globally,
550+
swiftscan_string_ref_t *error);
551+
552+
/// Async version of `swiftscan_cache_query` where result is returned via
553+
/// callback. Both cache_result enum and CachedCompilation will be provided to
554+
/// callback. \c ctx is an opaque value that passed to the callback and \c
555+
/// swiftscan_cache_cancellation_token_t will return an token that can be used
556+
/// to cancel the async operation. The token needs to be freed by caller using
557+
/// `swiftscan_cache_cancellation_token_dispose`.
558+
SWIFTSCAN_PUBLIC void swiftscan_cache_query_async(
559+
swiftscan_cas_t cas, const char *key, bool globally, void *ctx,
560+
void (*callback)(void *ctx, swiftscan_cache_result_t,
561+
swiftscan_cached_compilation_t,
562+
swiftscan_string_ref_t error),
563+
swiftscan_cache_cancellation_token_t *);
564+
565+
/// Dispose a CachedCompilation.
566+
SWIFTSCAN_PUBLIC void
567+
swiftscan_cached_compilation_dispose(swiftscan_cached_compilation_t);
568+
569+
/// Download and materialized the CASObject referenced by the CASID in the local
570+
/// CAS if needed from a remote CAS.
571+
/// If the return value is SWIFTSCAN_CACHE_RESULT_ERROR, the error message is
572+
/// returned via \c error parameter and its caller needs to free the message
573+
/// using `swiftscan_string_dispose`.
574+
SWIFTSCAN_PUBLIC swiftscan_cache_result_t
575+
swiftscan_cache_load_object(swiftscan_cas_t cas, swiftscan_cached_compilation_t,
576+
swiftscan_string_ref_t *error);
577+
578+
/// Async version of `swiftscan_cache_load_object` where result is returned via
579+
/// callback. \c ctx is an opaque value that passed to the callback and
580+
/// \c swiftscan_cache_cancellation_token_t will return an token that can be
581+
/// used to cancel the async operation. The token needs to be freed by caller
582+
/// using `swiftscan_cache_cancellation_token_dispose`.
583+
SWIFTSCAN_PUBLIC void swiftscan_cache_load_object_async(
584+
swiftscan_cas_t cas, swiftscan_cached_compilation_t, void *ctx,
585+
void (*callback)(void *ctx, swiftscan_cache_result_t,
586+
swiftscan_string_ref_t error),
587+
swiftscan_cache_cancellation_token_t *);
588+
589+
/// Check if CachedCompilation is materialized locally and can be accessed
590+
/// without downloading.
591+
SWIFTSCAN_PUBLIC bool
592+
swiftscan_cache_compilation_is_loaded(swiftscan_cached_compilation_t);
593+
594+
/// Query the number of outputs from a cached compilation. The cached
595+
/// compilation needs to be loaded, otherwise will return error. The error
596+
/// message is returned via \c error parameter and its caller needs to free the
597+
/// message using `swiftscan_string_dispose`.
598+
SWIFTSCAN_PUBLIC unsigned
599+
swiftscan_cache_get_num_cached_outputs(swiftscan_cached_compilation_t,
600+
swiftscan_string_ref_t *error);
601+
602+
/// Query if the kind of otuput exists in a cached compilation. The cached
603+
/// compilation needs to be loaded, otherwise will return error. The error
604+
/// message is returned via \c error parameter and its caller needs to free the
605+
/// message using `swiftscan_string_dispose`.
606+
SWIFTSCAN_PUBLIC bool
607+
swiftscan_cache_compilation_has_output_kind(swiftscan_cached_compilation_t,
608+
swiftscan_output_kind_t,
609+
swiftscan_string_ref_t *error);
610+
611+
/// Make the cache compilation available globally. \c callback will be called
612+
/// on completion.
613+
/// \c swiftscan_cache_cancellation_token_t will return an token that can be
614+
/// used to cancel the async operation. The token needs to be freed by caller
615+
/// using `swiftscan_cache_cancellation_token_dispose`.
616+
SWIFTSCAN_PUBLIC void swiftscan_cache_make_global_async(
617+
swiftscan_cas_t cas, const char *key, void *ctx,
618+
void (*callback)(void *ctx, swiftscan_string_ref_t error),
619+
swiftscan_cache_cancellation_token_t *);
620+
621+
/// Cancel the async cache action that is associated with token.
622+
SWIFTSCAN_PUBLIC void
623+
swiftscan_cache_action_cancel(swiftscan_cache_cancellation_token_t);
624+
625+
/// Dispose the cancellation token.
626+
SWIFTSCAN_PUBLIC void swiftscan_cache_cancellation_token_dispose(
627+
swiftscan_cache_cancellation_token_t);
628+
629+
/// Create a swift cached compilation replay instance with its command-line
630+
/// invocation. Return nullptr when errors occurs and the error message is
631+
/// returned via \c error parameter and its caller needs to free the message
632+
/// using `swiftscan_string_dispose`.
633+
SWIFTSCAN_PUBLIC swiftscan_cache_replay_instance_t
634+
swiftscan_cache_create_replay_instance(int argc, const char **argv,
635+
swiftscan_string_ref_t *error);
636+
637+
/// Dispose swift cached compilation replay instance.
638+
SWIFTSCAN_PUBLIC void
639+
swiftscan_cache_replay_instance_dispose(swiftscan_cache_replay_instance_t);
640+
641+
/// Replay the cached compilation using cached compliation replay instance.
642+
/// If the return value is SWIFTSCAN_CACHE_RESULT_ERROR, the error message is
643+
/// returned via \c error parameter and its caller needs to free the message
644+
/// using `swiftscan_string_dispose`.
645+
SWIFTSCAN_PUBLIC swiftscan_cache_result_t swiftscan_cache_replay_compilation(
646+
swiftscan_cas_t cas, swiftscan_cache_replay_instance_t,
647+
swiftscan_cached_compilation_t, swiftscan_string_ref_t *error);
648+
649+
/// Async replay the cached compilation. Callback function will be provided
650+
/// by the cache replay result, and its stdout, stderr, and any error message.
651+
/// \c ctx is an opaque value that passed to the callback and
652+
/// \c swiftscan_cache_cancellation_token_t will return an token that can be
653+
/// used to cancel the async operation. The token needs to be freed by caller
654+
/// using `swiftscan_cache_cancellation_token_dispose`.
655+
SWIFTSCAN_PUBLIC void swiftscan_cache_replay_compilation_async(
656+
swiftscan_cas_t cas, swiftscan_cache_replay_instance_t,
657+
swiftscan_cached_compilation_t, void *ctx,
658+
void (*callback)(void *ctx, swiftscan_cache_result_t,
659+
swiftscan_string_ref_t std_out,
660+
swiftscan_string_ref_t std_err,
661+
swiftscan_string_ref_t error),
662+
swiftscan_cache_cancellation_token_t *);
663+
495664
//===----------------------------------------------------------------------===//
496665

497666
SWIFTSCAN_END_DECLS

lib/DriverTool/swift_cache_tool_main.cpp

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ enum class SwiftCacheToolAction {
5050

5151
struct OutputEntry {
5252
std::string InputPath;
53-
std::string OutputPath;
54-
std::string OutputKind;
5553
std::string CacheKey;
54+
std::vector<std::pair<std::string, std::string>> Outputs;
5655
};
5756

5857
enum ID {
@@ -285,44 +284,41 @@ int SwiftCacheToolInvocation::printOutputKeys() {
285284

286285
std::vector<OutputEntry> OutputKeys;
287286
bool hasError = false;
288-
auto addOutputKey = [&](StringRef InputPath, file_types::ID OutputKind,
289-
StringRef OutputPath) {
287+
auto addFromInputFile = [&](const InputFile &Input) {
288+
auto InputPath = Input.getFileName();
290289
auto OutputKey =
291290
createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputPath);
292291
if (!OutputKey) {
293-
llvm::errs() << "cannot create cache key for " << OutputPath << ": "
292+
llvm::errs() << "cannot create cache key for " << InputPath << ": "
294293
<< toString(OutputKey.takeError()) << "\n";
295294
hasError = true;
296295
}
297296
OutputKeys.emplace_back(
298-
OutputEntry{InputPath.str(), OutputPath.str(),
299-
file_types::getTypeName(OutputKind).str(),
300-
CAS.getID(*OutputKey).toString()});
301-
};
302-
auto addFromInputFile = [&](const InputFile &Input) {
303-
auto InputPath = Input.getFileName();
297+
OutputEntry{InputPath, CAS.getID(*OutputKey).toString(), {}});
298+
auto &Outputs = OutputKeys.back().Outputs;
304299
if (!Input.outputFilename().empty())
305-
addOutputKey(InputPath,
306-
Invocation.getFrontendOptions()
307-
.InputsAndOutputs.getPrincipalOutputType(),
308-
Input.outputFilename());
300+
Outputs.emplace_back(file_types::getTypeName(
301+
Invocation.getFrontendOptions()
302+
.InputsAndOutputs.getPrincipalOutputType()),
303+
Input.outputFilename());
309304
Input.getPrimarySpecificPaths()
310305
.SupplementaryOutputs.forEachSetOutputAndType(
311306
[&](const std::string &File, file_types::ID ID) {
312307
// Dont print serialized diagnostics.
313308
if (file_types::isProducedFromDiagnostics(ID))
314309
return;
315-
316-
addOutputKey(InputPath, ID, File);
310+
Outputs.emplace_back(file_types::getTypeName(ID), File);
317311
});
318312
};
319313
llvm::for_each(
320314
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs(),
321315
addFromInputFile);
322316

323317
// Add diagnostics file.
324-
addOutputKey("<cached-diagnostics>", file_types::ID::TY_CachedDiagnostics,
325-
"<cached-diagnostics>");
318+
if (!OutputKeys.empty())
319+
OutputKeys.front().Outputs.emplace_back(
320+
file_types::getTypeName(file_types::ID::TY_CachedDiagnostics),
321+
"<cached-diagnostics>");
326322

327323
if (hasError)
328324
return 1;
@@ -331,10 +327,16 @@ int SwiftCacheToolInvocation::printOutputKeys() {
331327
Out.array([&] {
332328
for (const auto &E : OutputKeys) {
333329
Out.object([&] {
334-
Out.attribute("OutputPath", E.OutputPath);
335-
Out.attribute("OutputKind", E.OutputKind);
336330
Out.attribute("Input", E.InputPath);
337331
Out.attribute("CacheKey", E.CacheKey);
332+
Out.attributeArray("Outputs", [&] {
333+
for (const auto &OutEntry : E.Outputs) {
334+
Out.object([&] {
335+
Out.attribute("Kind", OutEntry.first);
336+
Out.attribute("Path", OutEntry.second);
337+
});
338+
}
339+
});
338340
});
339341
}
340342
});

test/CAS/swift-scan-test.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -cache-compile-job -Rcache-compile-job %s -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies \
4+
// RUN: -module-name Test -o %t/test.o -cas-path %t/cas -allow-unstable-cache-key-for-testing
5+
6+
// RUN: %swift-scan-test -action compute_cache_key -cas-path %t/cas -input %s -- %target-swift-frontend -cache-compile-job -Rcache-compile-job %s \
7+
// RUN: -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies -module-name Test -o %t/test.o -cas-path %t/cas \
8+
// RUN: -allow-unstable-cache-key-for-testing > %t/key.casid
9+
10+
// RUN: %swift-scan-test -action cache_query -id @%t/key.casid -cas-path %t/cas | %FileCheck %s --check-prefix=CHECK-QUERY
11+
12+
// RUN: %swift-scan-test -action replay_result -cas-path %t/cas -id @%t/key.casid -- %target-swift-frontend -cache-compile-job -Rcache-compile-job %s \
13+
// RUN: -emit-module -emit-module-path %t/Test2.swiftmodule -c -emit-dependencies -module-name Test -o %t/test2.o -cas-path %t/cas \
14+
// RUN: -allow-unstable-cache-key-for-testing
15+
16+
// RUN: diff %t/Test.swiftmodule %t/Test2.swiftmodule
17+
// RUN: diff %t/test.o %t/test.o
18+
19+
// CHECK-QUERY: Cached Compilation for key "llvmcas://{{.*}}" has 4 outputs
20+
21+
func testFunc() {}

test/lit.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ config.benchmark_o = inferSwiftBinary('Benchmark_O')
345345
config.benchmark_driver = inferSwiftBinary('Benchmark_Driver')
346346
config.wasm_ld = inferSwiftBinary('wasm-ld')
347347
config.swift_plugin_server = inferSwiftBinary('swift-plugin-server')
348+
config.swift_scan_test = inferSwiftBinary('swift-scan-test')
348349

349350
config.swift_utils = make_path(config.swift_src_root, 'utils')
350351
config.line_directive = make_path(config.swift_utils, 'line-directive')
@@ -636,6 +637,7 @@ config.substitutions.append( ('%llvm-strings', config.llvm_strings) )
636637
config.substitutions.append( ('%target-ptrauth', run_ptrauth ) )
637638
config.substitutions.append( ('%swift-path', config.swift) )
638639
config.substitutions.append( ('%swift-plugin-server', config.swift_plugin_server) )
640+
config.substitutions.append( ('%swift-scan-test', config.swift_scan_test) )
639641
config.substitutions.append( ('%validate-json', f"{config.python} -m json.tool") )
640642

641643
# This must come after all substitutions containing "%swift".

tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_swift_tool_subdirectory(libSwiftScan)
2828
add_swift_tool_subdirectory(libStaticMirror)
2929
add_swift_tool_subdirectory(libMockPlugin)
3030
add_swift_tool_subdirectory(swift-plugin-server)
31+
add_swift_tool_subdirectory(swift-scan-test)
3132

3233
if(SWIFT_INCLUDE_TESTS OR SWIFT_INCLUDE_TEST_BINARIES)
3334
add_swift_tool_subdirectory(swift-ide-test)

tools/swift-scan-test/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
add_swift_host_tool(swift-scan-test
2+
swift-scan-test.cpp
3+
LLVM_LINK_COMPONENTS support
4+
SWIFT_COMPONENT tools
5+
THINLTO_LD64_ADD_FLTO_CODEGEN_ONLY
6+
DOES_NOT_USE_SWIFT
7+
)
8+
9+
target_link_libraries(swift-scan-test
10+
PRIVATE
11+
libSwiftScan)

0 commit comments

Comments
 (0)