xref: /llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp (revision 5845688e91d85d46c0f47daaf4edfdfc772853cf)
1045c514fSAlex Lorenz //===-- cc1gen_reproducer_main.cpp - Clang reproducer generator  ----------===//
2045c514fSAlex Lorenz //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6045c514fSAlex Lorenz //
7045c514fSAlex Lorenz //===----------------------------------------------------------------------===//
8045c514fSAlex Lorenz //
9045c514fSAlex Lorenz // This is the entry point to the clang -cc1gen-reproducer functionality, which
10045c514fSAlex Lorenz // generates reproducers for invocations for clang-based tools.
11045c514fSAlex Lorenz //
12045c514fSAlex Lorenz //===----------------------------------------------------------------------===//
13045c514fSAlex Lorenz 
14045c514fSAlex Lorenz #include "clang/Basic/Diagnostic.h"
15045c514fSAlex Lorenz #include "clang/Basic/LLVM.h"
16045c514fSAlex Lorenz #include "clang/Driver/Compilation.h"
17045c514fSAlex Lorenz #include "clang/Driver/Driver.h"
18045c514fSAlex Lorenz #include "llvm/ADT/ArrayRef.h"
19045c514fSAlex Lorenz #include "llvm/ADT/STLExtras.h"
20045c514fSAlex Lorenz #include "llvm/Support/FileSystem.h"
213e57aa30SAlex Brachet #include "llvm/Support/LLVMDriver.h"
22045c514fSAlex Lorenz #include "llvm/Support/TargetSelect.h"
23fc51490bSJonas Devlieghere #include "llvm/Support/VirtualFileSystem.h"
24045c514fSAlex Lorenz #include "llvm/Support/YAMLTraits.h"
25045c514fSAlex Lorenz #include "llvm/Support/raw_ostream.h"
26d768bf99SArchibald Elliott #include "llvm/TargetParser/Host.h"
27a1580d7bSKazu Hirata #include <optional>
28045c514fSAlex Lorenz 
29045c514fSAlex Lorenz using namespace clang;
30045c514fSAlex Lorenz 
31045c514fSAlex Lorenz namespace {
32045c514fSAlex Lorenz 
33045c514fSAlex Lorenz struct UnsavedFileHash {
34045c514fSAlex Lorenz   std::string Name;
35045c514fSAlex Lorenz   std::string MD5;
36045c514fSAlex Lorenz };
37045c514fSAlex Lorenz 
38045c514fSAlex Lorenz struct ClangInvocationInfo {
39045c514fSAlex Lorenz   std::string Toolchain;
40045c514fSAlex Lorenz   std::string LibclangOperation;
41045c514fSAlex Lorenz   std::string LibclangOptions;
42045c514fSAlex Lorenz   std::vector<std::string> Arguments;
43045c514fSAlex Lorenz   std::vector<std::string> InvocationArguments;
44045c514fSAlex Lorenz   std::vector<UnsavedFileHash> UnsavedFileHashes;
45045c514fSAlex Lorenz   bool Dump = false;
46045c514fSAlex Lorenz };
47045c514fSAlex Lorenz 
48045c514fSAlex Lorenz } // end anonymous namespace
49045c514fSAlex Lorenz 
50045c514fSAlex Lorenz LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
51045c514fSAlex Lorenz 
52045c514fSAlex Lorenz namespace llvm {
53045c514fSAlex Lorenz namespace yaml {
54045c514fSAlex Lorenz 
55045c514fSAlex Lorenz template <> struct MappingTraits<UnsavedFileHash> {
56045c514fSAlex Lorenz   static void mapping(IO &IO, UnsavedFileHash &Info) {
57045c514fSAlex Lorenz     IO.mapRequired("name", Info.Name);
58045c514fSAlex Lorenz     IO.mapRequired("md5", Info.MD5);
59045c514fSAlex Lorenz   }
60045c514fSAlex Lorenz };
61045c514fSAlex Lorenz 
62045c514fSAlex Lorenz template <> struct MappingTraits<ClangInvocationInfo> {
63045c514fSAlex Lorenz   static void mapping(IO &IO, ClangInvocationInfo &Info) {
64045c514fSAlex Lorenz     IO.mapRequired("toolchain", Info.Toolchain);
65045c514fSAlex Lorenz     IO.mapOptional("libclang.operation", Info.LibclangOperation);
66045c514fSAlex Lorenz     IO.mapOptional("libclang.opts", Info.LibclangOptions);
67045c514fSAlex Lorenz     IO.mapRequired("args", Info.Arguments);
68045c514fSAlex Lorenz     IO.mapOptional("invocation-args", Info.InvocationArguments);
69045c514fSAlex Lorenz     IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
70045c514fSAlex Lorenz   }
71045c514fSAlex Lorenz };
72045c514fSAlex Lorenz 
73045c514fSAlex Lorenz } // end namespace yaml
74045c514fSAlex Lorenz } // end namespace llvm
75045c514fSAlex Lorenz 
76045c514fSAlex Lorenz static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
77045c514fSAlex Lorenz   std::string Result;
78045c514fSAlex Lorenz   llvm::raw_string_ostream OS(Result);
79045c514fSAlex Lorenz   OS << '{';
80045c514fSAlex Lorenz   bool NeedComma = false;
81045c514fSAlex Lorenz   auto EmitKey = [&](StringRef Key) {
82045c514fSAlex Lorenz     if (NeedComma)
83045c514fSAlex Lorenz       OS << ", ";
84045c514fSAlex Lorenz     NeedComma = true;
85045c514fSAlex Lorenz     OS << '"' << Key << "\": ";
86045c514fSAlex Lorenz   };
87045c514fSAlex Lorenz   auto EmitStringKey = [&](StringRef Key, StringRef Value) {
88045c514fSAlex Lorenz     if (Value.empty())
89045c514fSAlex Lorenz       return;
90045c514fSAlex Lorenz     EmitKey(Key);
91045c514fSAlex Lorenz     OS << '"' << Value << '"';
92045c514fSAlex Lorenz   };
93045c514fSAlex Lorenz   EmitStringKey("libclang.operation", Info.LibclangOperation);
94045c514fSAlex Lorenz   EmitStringKey("libclang.opts", Info.LibclangOptions);
95045c514fSAlex Lorenz   if (!Info.InvocationArguments.empty()) {
96045c514fSAlex Lorenz     EmitKey("invocation-args");
97045c514fSAlex Lorenz     OS << '[';
98045c514fSAlex Lorenz     for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
99045c514fSAlex Lorenz       if (Arg.index())
100045c514fSAlex Lorenz         OS << ',';
101045c514fSAlex Lorenz       OS << '"' << Arg.value() << '"';
102045c514fSAlex Lorenz     }
103045c514fSAlex Lorenz     OS << ']';
104045c514fSAlex Lorenz   }
105045c514fSAlex Lorenz   OS << '}';
106045c514fSAlex Lorenz   // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
107045c514fSAlex Lorenz   if (Info.Dump)
108918972bdSJOE1994     llvm::outs() << "REPRODUCER METAINFO: " << Result << "\n";
109918972bdSJOE1994   return Result;
110045c514fSAlex Lorenz }
111045c514fSAlex Lorenz 
112045c514fSAlex Lorenz /// Generates a reproducer for a set of arguments from a specific invocation.
1136ad0788cSKazu Hirata static std::optional<driver::Driver::CompilationDiagnosticReport>
114045c514fSAlex Lorenz generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
1153e57aa30SAlex Brachet                                          const ClangInvocationInfo &Info,
1163e57aa30SAlex Brachet                                          const llvm::ToolContext &ToolContext) {
117045c514fSAlex Lorenz   using namespace driver;
118045c514fSAlex Lorenz   auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
119045c514fSAlex Lorenz 
120045c514fSAlex Lorenz   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
121045c514fSAlex Lorenz 
122045c514fSAlex Lorenz   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
123045c514fSAlex Lorenz   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
124*5845688eSKadir Cetinkaya   auto VFS = llvm::vfs::getRealFileSystem();
125*5845688eSKadir Cetinkaya   ProcessWarningOptions(Diags, *DiagOpts, *VFS, /*ReportDiags=*/false);
126*5845688eSKadir Cetinkaya   Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(), Diags,
127*5845688eSKadir Cetinkaya                    /*Title=*/"clang LLVM compiler", VFS);
128045c514fSAlex Lorenz   TheDriver.setTargetAndMode(TargetAndMode);
1293e57aa30SAlex Brachet   if (ToolContext.NeedsPrependArg)
1303e57aa30SAlex Brachet     TheDriver.setPrependArg(ToolContext.PrependArg);
131045c514fSAlex Lorenz 
132045c514fSAlex Lorenz   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
133045c514fSAlex Lorenz   if (C && !C->containsError()) {
134045c514fSAlex Lorenz     for (const auto &J : C->getJobs()) {
135045c514fSAlex Lorenz       if (const Command *Cmd = dyn_cast<Command>(&J)) {
136045c514fSAlex Lorenz         Driver::CompilationDiagnosticReport Report;
137045c514fSAlex Lorenz         TheDriver.generateCompilationDiagnostics(
138045c514fSAlex Lorenz             *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
139045c514fSAlex Lorenz         return Report;
140045c514fSAlex Lorenz       }
141045c514fSAlex Lorenz     }
142045c514fSAlex Lorenz   }
143045c514fSAlex Lorenz 
1445891420eSKazu Hirata   return std::nullopt;
145045c514fSAlex Lorenz }
146045c514fSAlex Lorenz 
147045c514fSAlex Lorenz std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
148045c514fSAlex Lorenz 
149045c514fSAlex Lorenz static void printReproducerInformation(
150045c514fSAlex Lorenz     llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
151045c514fSAlex Lorenz     const driver::Driver::CompilationDiagnosticReport &Report) {
152045c514fSAlex Lorenz   OS << "REPRODUCER:\n";
153045c514fSAlex Lorenz   OS << "{\n";
154045c514fSAlex Lorenz   OS << R"("files":[)";
155045c514fSAlex Lorenz   for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
156045c514fSAlex Lorenz     if (File.index())
157045c514fSAlex Lorenz       OS << ',';
158045c514fSAlex Lorenz     OS << '"' << File.value() << '"';
159045c514fSAlex Lorenz   }
160045c514fSAlex Lorenz   OS << "]\n}\n";
161045c514fSAlex Lorenz }
162045c514fSAlex Lorenz 
163045c514fSAlex Lorenz int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
1643e57aa30SAlex Brachet                            void *MainAddr,
1653e57aa30SAlex Brachet                            const llvm::ToolContext &ToolContext) {
166045c514fSAlex Lorenz   if (Argv.size() < 1) {
167045c514fSAlex Lorenz     llvm::errs() << "error: missing invocation file\n";
168045c514fSAlex Lorenz     return 1;
169045c514fSAlex Lorenz   }
170045c514fSAlex Lorenz   // Parse the invocation descriptor.
171045c514fSAlex Lorenz   StringRef Input = Argv[0];
172045c514fSAlex Lorenz   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
17305b4babcSAbhina Sreeskantharajan       llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);
174045c514fSAlex Lorenz   if (!Buffer) {
175045c514fSAlex Lorenz     llvm::errs() << "error: failed to read " << Input << ": "
176045c514fSAlex Lorenz                  << Buffer.getError().message() << "\n";
177045c514fSAlex Lorenz     return 1;
178045c514fSAlex Lorenz   }
179045c514fSAlex Lorenz   llvm::yaml::Input YAML(Buffer.get()->getBuffer());
180045c514fSAlex Lorenz   ClangInvocationInfo InvocationInfo;
181045c514fSAlex Lorenz   YAML >> InvocationInfo;
182045c514fSAlex Lorenz   if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
183045c514fSAlex Lorenz     InvocationInfo.Dump = true;
184045c514fSAlex Lorenz 
185045c514fSAlex Lorenz   // Create an invocation that will produce the reproducer.
186045c514fSAlex Lorenz   std::vector<const char *> DriverArgs;
187045c514fSAlex Lorenz   for (const auto &Arg : InvocationInfo.Arguments)
188045c514fSAlex Lorenz     DriverArgs.push_back(Arg.c_str());
189045c514fSAlex Lorenz   std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
190045c514fSAlex Lorenz   DriverArgs[0] = Path.c_str();
1916ad0788cSKazu Hirata   std::optional<driver::Driver::CompilationDiagnosticReport> Report =
1923e57aa30SAlex Brachet       generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,
1933e57aa30SAlex Brachet                                                ToolContext);
194045c514fSAlex Lorenz 
195045c514fSAlex Lorenz   // Emit the information about the reproduce files to stdout.
196045c514fSAlex Lorenz   int Result = 1;
197045c514fSAlex Lorenz   if (Report) {
198045c514fSAlex Lorenz     printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
199045c514fSAlex Lorenz     Result = 0;
200045c514fSAlex Lorenz   }
201045c514fSAlex Lorenz 
202045c514fSAlex Lorenz   // Remove the input file.
203045c514fSAlex Lorenz   llvm::sys::fs::remove(Input);
204045c514fSAlex Lorenz   return Result;
205045c514fSAlex Lorenz }
206