xref: /freebsd-src/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //===-- cc1gen_reproducer_main.cpp - Clang reproducer generator  ----------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This is the entry point to the clang -cc1gen-reproducer functionality, which
100b57cec5SDimitry Andric // generates reproducers for invocations for clang-based tools.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h"
150b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
160b57cec5SDimitry Andric #include "clang/Driver/Compilation.h"
170b57cec5SDimitry Andric #include "clang/Driver/Driver.h"
180b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
190b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
200b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
21*06c3fb27SDimitry Andric #include "llvm/Support/LLVMDriver.h"
220b57cec5SDimitry Andric #include "llvm/Support/TargetSelect.h"
230b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
240b57cec5SDimitry Andric #include "llvm/Support/YAMLTraits.h"
250b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
26*06c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h"
27bdd1243dSDimitry Andric #include <optional>
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric using namespace clang;
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric namespace {
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric struct UnsavedFileHash {
340b57cec5SDimitry Andric   std::string Name;
350b57cec5SDimitry Andric   std::string MD5;
360b57cec5SDimitry Andric };
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric struct ClangInvocationInfo {
390b57cec5SDimitry Andric   std::string Toolchain;
400b57cec5SDimitry Andric   std::string LibclangOperation;
410b57cec5SDimitry Andric   std::string LibclangOptions;
420b57cec5SDimitry Andric   std::vector<std::string> Arguments;
430b57cec5SDimitry Andric   std::vector<std::string> InvocationArguments;
440b57cec5SDimitry Andric   std::vector<UnsavedFileHash> UnsavedFileHashes;
450b57cec5SDimitry Andric   bool Dump = false;
460b57cec5SDimitry Andric };
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric } // end anonymous namespace
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric namespace llvm {
530b57cec5SDimitry Andric namespace yaml {
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric template <> struct MappingTraits<UnsavedFileHash> {
mappingllvm::yaml::MappingTraits560b57cec5SDimitry Andric   static void mapping(IO &IO, UnsavedFileHash &Info) {
570b57cec5SDimitry Andric     IO.mapRequired("name", Info.Name);
580b57cec5SDimitry Andric     IO.mapRequired("md5", Info.MD5);
590b57cec5SDimitry Andric   }
600b57cec5SDimitry Andric };
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric template <> struct MappingTraits<ClangInvocationInfo> {
mappingllvm::yaml::MappingTraits630b57cec5SDimitry Andric   static void mapping(IO &IO, ClangInvocationInfo &Info) {
640b57cec5SDimitry Andric     IO.mapRequired("toolchain", Info.Toolchain);
650b57cec5SDimitry Andric     IO.mapOptional("libclang.operation", Info.LibclangOperation);
660b57cec5SDimitry Andric     IO.mapOptional("libclang.opts", Info.LibclangOptions);
670b57cec5SDimitry Andric     IO.mapRequired("args", Info.Arguments);
680b57cec5SDimitry Andric     IO.mapOptional("invocation-args", Info.InvocationArguments);
690b57cec5SDimitry Andric     IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
700b57cec5SDimitry Andric   }
710b57cec5SDimitry Andric };
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric } // end namespace yaml
740b57cec5SDimitry Andric } // end namespace llvm
750b57cec5SDimitry Andric 
generateReproducerMetaInfo(const ClangInvocationInfo & Info)760b57cec5SDimitry Andric static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
770b57cec5SDimitry Andric   std::string Result;
780b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Result);
790b57cec5SDimitry Andric   OS << '{';
800b57cec5SDimitry Andric   bool NeedComma = false;
810b57cec5SDimitry Andric   auto EmitKey = [&](StringRef Key) {
820b57cec5SDimitry Andric     if (NeedComma)
830b57cec5SDimitry Andric       OS << ", ";
840b57cec5SDimitry Andric     NeedComma = true;
850b57cec5SDimitry Andric     OS << '"' << Key << "\": ";
860b57cec5SDimitry Andric   };
870b57cec5SDimitry Andric   auto EmitStringKey = [&](StringRef Key, StringRef Value) {
880b57cec5SDimitry Andric     if (Value.empty())
890b57cec5SDimitry Andric       return;
900b57cec5SDimitry Andric     EmitKey(Key);
910b57cec5SDimitry Andric     OS << '"' << Value << '"';
920b57cec5SDimitry Andric   };
930b57cec5SDimitry Andric   EmitStringKey("libclang.operation", Info.LibclangOperation);
940b57cec5SDimitry Andric   EmitStringKey("libclang.opts", Info.LibclangOptions);
950b57cec5SDimitry Andric   if (!Info.InvocationArguments.empty()) {
960b57cec5SDimitry Andric     EmitKey("invocation-args");
970b57cec5SDimitry Andric     OS << '[';
980b57cec5SDimitry Andric     for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
990b57cec5SDimitry Andric       if (Arg.index())
1000b57cec5SDimitry Andric         OS << ',';
1010b57cec5SDimitry Andric       OS << '"' << Arg.value() << '"';
1020b57cec5SDimitry Andric     }
1030b57cec5SDimitry Andric     OS << ']';
1040b57cec5SDimitry Andric   }
1050b57cec5SDimitry Andric   OS << '}';
1060b57cec5SDimitry Andric   // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
1070b57cec5SDimitry Andric   if (Info.Dump)
1080b57cec5SDimitry Andric     llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
1090b57cec5SDimitry Andric   return std::move(OS.str());
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric /// Generates a reproducer for a set of arguments from a specific invocation.
113bdd1243dSDimitry Andric static std::optional<driver::Driver::CompilationDiagnosticReport>
generateReproducerForInvocationArguments(ArrayRef<const char * > Argv,const ClangInvocationInfo & Info,const llvm::ToolContext & ToolContext)1140b57cec5SDimitry Andric generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
115*06c3fb27SDimitry Andric                                          const ClangInvocationInfo &Info,
116*06c3fb27SDimitry Andric                                          const llvm::ToolContext &ToolContext) {
1170b57cec5SDimitry Andric   using namespace driver;
1180b57cec5SDimitry Andric   auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
1230b57cec5SDimitry Andric   DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
1240b57cec5SDimitry Andric   ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
125*06c3fb27SDimitry Andric   Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(),
126*06c3fb27SDimitry Andric                    Diags);
1270b57cec5SDimitry Andric   TheDriver.setTargetAndMode(TargetAndMode);
128*06c3fb27SDimitry Andric   if (ToolContext.NeedsPrependArg)
129*06c3fb27SDimitry Andric     TheDriver.setPrependArg(ToolContext.PrependArg);
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
1320b57cec5SDimitry Andric   if (C && !C->containsError()) {
1330b57cec5SDimitry Andric     for (const auto &J : C->getJobs()) {
1340b57cec5SDimitry Andric       if (const Command *Cmd = dyn_cast<Command>(&J)) {
1350b57cec5SDimitry Andric         Driver::CompilationDiagnosticReport Report;
1360b57cec5SDimitry Andric         TheDriver.generateCompilationDiagnostics(
1370b57cec5SDimitry Andric             *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
1380b57cec5SDimitry Andric         return Report;
1390b57cec5SDimitry Andric       }
1400b57cec5SDimitry Andric     }
1410b57cec5SDimitry Andric   }
1420b57cec5SDimitry Andric 
143bdd1243dSDimitry Andric   return std::nullopt;
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
1470b57cec5SDimitry Andric 
printReproducerInformation(llvm::raw_ostream & OS,const ClangInvocationInfo & Info,const driver::Driver::CompilationDiagnosticReport & Report)1480b57cec5SDimitry Andric static void printReproducerInformation(
1490b57cec5SDimitry Andric     llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
1500b57cec5SDimitry Andric     const driver::Driver::CompilationDiagnosticReport &Report) {
1510b57cec5SDimitry Andric   OS << "REPRODUCER:\n";
1520b57cec5SDimitry Andric   OS << "{\n";
1530b57cec5SDimitry Andric   OS << R"("files":[)";
1540b57cec5SDimitry Andric   for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
1550b57cec5SDimitry Andric     if (File.index())
1560b57cec5SDimitry Andric       OS << ',';
1570b57cec5SDimitry Andric     OS << '"' << File.value() << '"';
1580b57cec5SDimitry Andric   }
1590b57cec5SDimitry Andric   OS << "]\n}\n";
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric 
cc1gen_reproducer_main(ArrayRef<const char * > Argv,const char * Argv0,void * MainAddr,const llvm::ToolContext & ToolContext)1620b57cec5SDimitry Andric int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
163*06c3fb27SDimitry Andric                            void *MainAddr,
164*06c3fb27SDimitry Andric                            const llvm::ToolContext &ToolContext) {
1650b57cec5SDimitry Andric   if (Argv.size() < 1) {
1660b57cec5SDimitry Andric     llvm::errs() << "error: missing invocation file\n";
1670b57cec5SDimitry Andric     return 1;
1680b57cec5SDimitry Andric   }
1690b57cec5SDimitry Andric   // Parse the invocation descriptor.
1700b57cec5SDimitry Andric   StringRef Input = Argv[0];
1710b57cec5SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
172fe6060f1SDimitry Andric       llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);
1730b57cec5SDimitry Andric   if (!Buffer) {
1740b57cec5SDimitry Andric     llvm::errs() << "error: failed to read " << Input << ": "
1750b57cec5SDimitry Andric                  << Buffer.getError().message() << "\n";
1760b57cec5SDimitry Andric     return 1;
1770b57cec5SDimitry Andric   }
1780b57cec5SDimitry Andric   llvm::yaml::Input YAML(Buffer.get()->getBuffer());
1790b57cec5SDimitry Andric   ClangInvocationInfo InvocationInfo;
1800b57cec5SDimitry Andric   YAML >> InvocationInfo;
1810b57cec5SDimitry Andric   if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
1820b57cec5SDimitry Andric     InvocationInfo.Dump = true;
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric   // Create an invocation that will produce the reproducer.
1850b57cec5SDimitry Andric   std::vector<const char *> DriverArgs;
1860b57cec5SDimitry Andric   for (const auto &Arg : InvocationInfo.Arguments)
1870b57cec5SDimitry Andric     DriverArgs.push_back(Arg.c_str());
1880b57cec5SDimitry Andric   std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
1890b57cec5SDimitry Andric   DriverArgs[0] = Path.c_str();
190bdd1243dSDimitry Andric   std::optional<driver::Driver::CompilationDiagnosticReport> Report =
191*06c3fb27SDimitry Andric       generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,
192*06c3fb27SDimitry Andric                                                ToolContext);
1930b57cec5SDimitry Andric 
1940b57cec5SDimitry Andric   // Emit the information about the reproduce files to stdout.
1950b57cec5SDimitry Andric   int Result = 1;
1960b57cec5SDimitry Andric   if (Report) {
1970b57cec5SDimitry Andric     printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
1980b57cec5SDimitry Andric     Result = 0;
1990b57cec5SDimitry Andric   }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   // Remove the input file.
2020b57cec5SDimitry Andric   llvm::sys::fs::remove(Input);
2030b57cec5SDimitry Andric   return Result;
2040b57cec5SDimitry Andric }
205