Skip to content

Commit 1127be3

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 1d4e9d0 commit 1127be3

File tree

8 files changed

+460
-5
lines changed

8 files changed

+460
-5
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,11 @@ ERROR(error_load_input_from_cas, none, "failed to load input '%0' from CAS", (St
544544

545545
ERROR(error_wrong_input_num_for_input_file_key, none, "-input-file-key only support one input file", ())
546546

547+
ERROR(error_gen_reproducer_not_caching, none, "-gen-reproducer only supports swift caching", ())
548+
ERROR(error_cannot_create_reproducer_dir, none, "failed to create reproducer director '%0': %1", (StringRef, StringRef))
549+
550+
NOTE(note_reproducer, none, "reproducer is available at: %0", (StringRef))
551+
547552
// Dependency Verifier Diagnostics
548553
ERROR(missing_member_dependency,none,
549554
"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
@@ -1522,6 +1522,12 @@ def enable_address_dependencies : Flag<["-"], "enable-address-dependencies">,
15221522
def disable_address_dependencies : Flag<["-"], "disable-address-dependencies">,
15231523
HelpText<"Disable enforcement of lifetime dependencies on addressable values.">;
15241524

1525+
def gen_reproducer : Flag<["-"], "gen-reproducer">,
1526+
HelpText<"Generate a reproducer for current compilation.">;
1527+
def gen_reproducer_dir
1528+
: Separate<["-"], "gen-reproducer-dir">,
1529+
HelpText<"Path to directory where reproducers write to.">;
1530+
15251531
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]
15261532

15271533
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
@@ -163,6 +163,9 @@ bool ArgsToFrontendOptionsConverter::convert(
163163
Opts.ParallelDependencyScan = Args.hasFlag(OPT_parallel_scan,
164164
OPT_no_parallel_scan,
165165
true);
166+
Opts.GenReproducer |= Args.hasArg(OPT_gen_reproducer);
167+
Opts.GenReproducerDir = Args.getLastArgValue(OPT_gen_reproducer_dir);
168+
166169
if (const Arg *A = Args.getLastArg(OPT_dependency_scan_cache_path)) {
167170
Opts.SerializedDependencyScannerCachePath = A->getValue();
168171
}

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)