Skip to content

Commit a24c342

Browse files
[SwiftCaching] Create standalone reproducer from swift caching build
Add a new option `-gen-reproducer` that when swift caching is used, create a standalone reproducer that can be used to reproduce the `swift-frontend` invocation.
1 parent 78552bb commit a24c342

File tree

8 files changed

+479
-5
lines changed

8 files changed

+479
-5
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,11 @@ GROUPED_ERROR(error_load_input_from_cas, CompilationCaching, none, "failed to lo
537537

538538
GROUPED_ERROR(error_wrong_input_num_for_input_file_key, CompilationCaching, none, "-input-file-key only support one input file", ())
539539

540+
ERROR(error_gen_reproducer_not_caching, none, "-gen-reproducer only supports swift caching (-cache-compile-job)", ())
541+
ERROR(error_cannot_create_reproducer_dir, none, "failed to create reproducer director '%0': %1", (StringRef, StringRef))
542+
543+
NOTE(note_reproducer, none, "reproducer is available at: %0", (StringRef))
544+
540545
// Dependency Verifier Diagnostics
541546
ERROR(missing_member_dependency,none,
542547
"expected "

include/swift/Frontend/CompileJobCacheKey.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ llvm::Error printCompileJobCacheKey(llvm::cas::ObjectStore &CAS,
5151
llvm::cas::ObjectRef Key,
5252
llvm::raw_ostream &os);
5353

54+
/// Iterating through command-line options in cache key.
55+
llvm::Error iterateCommandLine(llvm::cas::ObjectStore &CAS,
56+
llvm::cas::ObjectRef Key,
57+
std::function<llvm::Error(StringRef)> Callback);
58+
5459
} // namespace swift
5560

5661
#endif

include/swift/Frontend/FrontendOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ class FrontendOptions {
292292
/// by the Clang importer as part of semantic analysis.
293293
bool ModuleHasBridgingHeader = false;
294294

295+
/// Generate reproducer.
296+
bool GenReproducer = false;
297+
298+
/// Directory to generate reproducer.
299+
std::string GenReproducerDir;
300+
295301
/// Indicates whether or not the frontend should print statistics upon
296302
/// termination.
297303
bool PrintStats = false;

include/swift/Option/FrontendOptions.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,12 @@ def enable_address_dependencies : Flag<["-"], "enable-address-dependencies">,
15331533
def disable_address_dependencies : Flag<["-"], "disable-address-dependencies">,
15341534
HelpText<"Disable enforcement of lifetime dependencies on addressable values.">;
15351535

1536+
def gen_reproducer : Flag<["-"], "gen-reproducer">,
1537+
HelpText<"Generate a reproducer for current compilation.">;
1538+
def gen_reproducer_dir
1539+
: Separate<["-"], "gen-reproducer-dir">,
1540+
HelpText<"Path to directory where reproducers write to.">;
1541+
15361542
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]
15371543

15381544
def disable_experimental_parser_round_trip : Flag<["-"],

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ bool ArgsToFrontendOptionsConverter::convert(
164164
Opts.ParallelDependencyScan = Args.hasFlag(OPT_parallel_scan,
165165
OPT_no_parallel_scan,
166166
true);
167+
Opts.GenReproducer |= Args.hasArg(OPT_gen_reproducer);
168+
Opts.GenReproducerDir = Args.getLastArgValue(OPT_gen_reproducer_dir);
169+
167170
if (const Arg *A = Args.getLastArg(OPT_dependency_scan_cache_path)) {
168171
Opts.SerializedDependencyScannerCachePath = A->getValue();
169172
}

lib/Frontend/CompileJobCacheKey.cpp

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,23 @@ swift::createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
105105
return CAS.storeFromString({BaseKey}, OS.str());
106106
}
107107

108+
static llvm::Error validateCacheKeyNode(llvm::cas::ObjectProxy Proxy) {
109+
if (Proxy.getData().size() != sizeof(uint32_t))
110+
return llvm::createStringError("incorrect size for cache key node");
111+
if (Proxy.getNumReferences() != 1)
112+
return llvm::createStringError("incorrect child number for cache key node");
113+
114+
return llvm::Error::success();
115+
}
116+
108117
llvm::Error swift::printCompileJobCacheKey(llvm::cas::ObjectStore &CAS,
109118
llvm::cas::ObjectRef Key,
110119
llvm::raw_ostream &OS) {
111120
auto Proxy = CAS.getProxy(Key);
112121
if (!Proxy)
113122
return Proxy.takeError();
114-
115-
if (Proxy->getData().size() != sizeof(uint32_t))
116-
return llvm::createStringError("incorrect size for cache key node");
117-
if (Proxy->getNumReferences() != 1)
118-
return llvm::createStringError("incorrect child number for cache key node");
123+
if (auto Err = validateCacheKeyNode(*Proxy))
124+
return Err;
119125

120126
uint32_t InputIndex = llvm::support::endian::read<uint32_t>(
121127
Proxy->getData().data(), llvm::endianness::little);
@@ -153,3 +159,41 @@ llvm::Error swift::printCompileJobCacheKey(llvm::cas::ObjectStore &CAS,
153159

154160
return llvm::Error::success();
155161
}
162+
163+
llvm::Error
164+
swift::iterateCommandLine(llvm::cas::ObjectStore &CAS, llvm::cas::ObjectRef Key,
165+
std::function<llvm::Error(StringRef)> Callback) {
166+
auto Proxy = CAS.getProxy(Key);
167+
if (!Proxy)
168+
return Proxy.takeError();
169+
170+
if (auto Err = validateCacheKeyNode(*Proxy))
171+
return Err;
172+
173+
auto Base = Proxy->getReference(0);
174+
llvm::cas::TreeSchema Schema(CAS);
175+
auto Tree = Schema.load(Base);
176+
if (!Tree)
177+
return Tree.takeError();
178+
179+
std::string BaseStr;
180+
llvm::raw_string_ostream BaseOS(BaseStr);
181+
return Tree->forEachEntry(
182+
[&](const llvm::cas::NamedTreeEntry &Entry) -> llvm::Error {
183+
auto Ref = Entry.getRef();
184+
auto DataProxy = CAS.getProxy(Ref);
185+
if (!DataProxy)
186+
return DataProxy.takeError();
187+
188+
if (Entry.getName() != "command-line")
189+
return llvm::Error::success();
190+
191+
StringRef Line, Remain = DataProxy->getData();
192+
while (!Remain.empty()) {
193+
std::tie(Line, Remain) = Remain.split(0);
194+
if (auto Err = Callback(Line))
195+
return Err;
196+
}
197+
return llvm::Error::success();
198+
});
199+
}

0 commit comments

Comments
 (0)