14 #include "clang/Basic/Diagnostic.h"
15 #include "clang/Basic/LLVM.h"
16 #include "clang/Driver/Compilation.h"
17 #include "clang/Driver/Driver.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/Host.h"
22 #include "llvm/Support/TargetSelect.h"
23 #include "llvm/Support/VirtualFileSystem.h"
24 #include "llvm/Support/YAMLTraits.h"
25 #include "llvm/Support/raw_ostream.h"
28 using namespace clang;
32 struct UnsavedFileHash {
37 struct ClangInvocationInfo {
38 std::string Toolchain;
39 std::string LibclangOperation;
40 std::string LibclangOptions;
41 std::vector<std::string> Arguments;
42 std::vector<std::string> InvocationArguments;
43 std::vector<UnsavedFileHash> UnsavedFileHashes;
49 LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
54 template <>
struct MappingTraits<UnsavedFileHash> {
55 static void mapping(IO &IO, UnsavedFileHash &Info) {
56 IO.mapRequired(
"name", Info.Name);
57 IO.mapRequired(
"md5", Info.MD5);
61 template <>
struct MappingTraits<ClangInvocationInfo> {
62 static void mapping(IO &IO, ClangInvocationInfo &Info) {
63 IO.mapRequired(
"toolchain", Info.Toolchain);
64 IO.mapOptional(
"libclang.operation", Info.LibclangOperation);
65 IO.mapOptional(
"libclang.opts", Info.LibclangOptions);
66 IO.mapRequired(
"args", Info.Arguments);
67 IO.mapOptional(
"invocation-args", Info.InvocationArguments);
68 IO.mapOptional(
"unsaved_file_hashes", Info.UnsavedFileHashes);
75 static std::string generateReproducerMetaInfo(
const ClangInvocationInfo &Info) {
77 llvm::raw_string_ostream OS(Result);
79 bool NeedComma =
false;
80 auto EmitKey = [&](StringRef Key) {
84 OS <<
'"' << Key <<
"\": ";
86 auto EmitStringKey = [&](StringRef Key, StringRef Value) {
90 OS <<
'"' << Value <<
'"';
92 EmitStringKey(
"libclang.operation", Info.LibclangOperation);
93 EmitStringKey(
"libclang.opts", Info.LibclangOptions);
94 if (!Info.InvocationArguments.empty()) {
95 EmitKey(
"invocation-args");
97 for (
const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
100 OS <<
'"' << Arg.value() <<
'"';
107 llvm::outs() <<
"REPRODUCER METAINFO: " << OS.str() <<
"\n";
108 return std::move(OS.str());
112 static std::optional<driver::Driver::CompilationDiagnosticReport>
113 generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
const ClangInvocationInfo &Info) {
114 using namespace driver;
115 auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
117 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
new DiagnosticOptions;
119 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(
new DiagnosticIDs());
120 DiagnosticsEngine Diags(DiagID, &*DiagOpts,
new IgnoringDiagConsumer());
121 ProcessWarningOptions(Diags, *DiagOpts,
false);
122 Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
123 TheDriver.setTargetAndMode(TargetAndMode);
125 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
126 if (C && !C->containsError()) {
127 for (
const auto &J : C->getJobs()) {
128 if (
const Command *Cmd = dyn_cast<Command>(&J)) {
129 Driver::CompilationDiagnosticReport Report;
130 TheDriver.generateCompilationDiagnostics(*C, *Cmd, generateReproducerMetaInfo(Info), &Report);
139 std::string GetExecutablePath(
const char *Argv0,
bool CanonicalPrefixes);
141 static void printReproducerInformation(llvm::raw_ostream &OS,
142 const ClangInvocationInfo &Info,
143 const driver::Driver::CompilationDiagnosticReport &Report) {
144 OS <<
"REPRODUCER:\n";
146 OS << R
"("files":[)";
147 for (
const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
150 OS <<
'"' << File.value() <<
'"';
155 int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
const char *Argv0,
void *MainAddr) {
156 if (Argv.size() < 1) {
157 llvm::errs() <<
"error: missing invocation file\n";
161 StringRef Input = Argv[0];
162 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = llvm::MemoryBuffer::getFile(Input,
true);
164 llvm::errs() <<
"error: failed to read " << Input <<
": " << Buffer.getError().message() <<
"\n";
167 llvm::yaml::Input YAML(Buffer.get()->getBuffer());
168 ClangInvocationInfo InvocationInfo;
169 YAML >> InvocationInfo;
170 if (Argv.size() > 1 && Argv[1] == StringRef(
"-v"))
171 InvocationInfo.Dump =
true;
174 std::vector<const char *> DriverArgs;
175 for (
const auto &Arg : InvocationInfo.Arguments)
176 DriverArgs.push_back(Arg.c_str());
177 std::string Path = GetExecutablePath(Argv0,
true);
178 DriverArgs[0] = Path.c_str();
179 std::optional<driver::Driver::CompilationDiagnosticReport> Report =
180 generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
185 printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
190 llvm::sys::fs::remove(Input);