1336815Sdim//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===// 2336815Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6336815Sdim// 7336815Sdim//===----------------------------------------------------------------------===// 8336815Sdim// 9336815Sdim// This is the entry point to the clang -cc1gen-reproducer functionality, which 10336815Sdim// generates reproducers for invocations for clang-based tools. 11336815Sdim// 12336815Sdim//===----------------------------------------------------------------------===// 13336815Sdim 14336815Sdim#include "clang/Basic/Diagnostic.h" 15336815Sdim#include "clang/Basic/LLVM.h" 16336815Sdim#include "clang/Driver/Compilation.h" 17336815Sdim#include "clang/Driver/Driver.h" 18336815Sdim#include "llvm/ADT/ArrayRef.h" 19336815Sdim#include "llvm/ADT/STLExtras.h" 20336815Sdim#include "llvm/Support/FileSystem.h" 21336815Sdim#include "llvm/Support/TargetSelect.h" 22344779Sdim#include "llvm/Support/VirtualFileSystem.h" 23336815Sdim#include "llvm/Support/YAMLTraits.h" 24336815Sdim#include "llvm/Support/raw_ostream.h" 25336815Sdim 26336815Sdimusing namespace clang; 27336815Sdim 28336815Sdimnamespace { 29336815Sdim 30336815Sdimstruct UnsavedFileHash { 31336815Sdim std::string Name; 32336815Sdim std::string MD5; 33336815Sdim}; 34336815Sdim 35336815Sdimstruct ClangInvocationInfo { 36336815Sdim std::string Toolchain; 37336815Sdim std::string LibclangOperation; 38336815Sdim std::string LibclangOptions; 39336815Sdim std::vector<std::string> Arguments; 40336815Sdim std::vector<std::string> InvocationArguments; 41336815Sdim std::vector<UnsavedFileHash> UnsavedFileHashes; 42336815Sdim bool Dump = false; 43336815Sdim}; 44336815Sdim 45336815Sdim} // end anonymous namespace 46336815Sdim 47336815SdimLLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash) 48336815Sdim 49336815Sdimnamespace llvm { 50336815Sdimnamespace yaml { 51336815Sdim 52336815Sdimtemplate <> struct MappingTraits<UnsavedFileHash> { 53336815Sdim static void mapping(IO &IO, UnsavedFileHash &Info) { 54336815Sdim IO.mapRequired("name", Info.Name); 55336815Sdim IO.mapRequired("md5", Info.MD5); 56336815Sdim } 57336815Sdim}; 58336815Sdim 59336815Sdimtemplate <> struct MappingTraits<ClangInvocationInfo> { 60336815Sdim static void mapping(IO &IO, ClangInvocationInfo &Info) { 61336815Sdim IO.mapRequired("toolchain", Info.Toolchain); 62336815Sdim IO.mapOptional("libclang.operation", Info.LibclangOperation); 63336815Sdim IO.mapOptional("libclang.opts", Info.LibclangOptions); 64336815Sdim IO.mapRequired("args", Info.Arguments); 65336815Sdim IO.mapOptional("invocation-args", Info.InvocationArguments); 66336815Sdim IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes); 67336815Sdim } 68336815Sdim}; 69336815Sdim 70336815Sdim} // end namespace yaml 71336815Sdim} // end namespace llvm 72336815Sdim 73336815Sdimstatic std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) { 74336815Sdim std::string Result; 75336815Sdim llvm::raw_string_ostream OS(Result); 76336815Sdim OS << '{'; 77336815Sdim bool NeedComma = false; 78336815Sdim auto EmitKey = [&](StringRef Key) { 79336815Sdim if (NeedComma) 80336815Sdim OS << ", "; 81336815Sdim NeedComma = true; 82336815Sdim OS << '"' << Key << "\": "; 83336815Sdim }; 84336815Sdim auto EmitStringKey = [&](StringRef Key, StringRef Value) { 85336815Sdim if (Value.empty()) 86336815Sdim return; 87336815Sdim EmitKey(Key); 88336815Sdim OS << '"' << Value << '"'; 89336815Sdim }; 90336815Sdim EmitStringKey("libclang.operation", Info.LibclangOperation); 91336815Sdim EmitStringKey("libclang.opts", Info.LibclangOptions); 92336815Sdim if (!Info.InvocationArguments.empty()) { 93336815Sdim EmitKey("invocation-args"); 94336815Sdim OS << '['; 95336815Sdim for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) { 96336815Sdim if (Arg.index()) 97336815Sdim OS << ','; 98336815Sdim OS << '"' << Arg.value() << '"'; 99336815Sdim } 100336815Sdim OS << ']'; 101336815Sdim } 102336815Sdim OS << '}'; 103336815Sdim // FIXME: Compare unsaved file hashes and report mismatch in the reproducer. 104336815Sdim if (Info.Dump) 105336815Sdim llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n"; 106336815Sdim return std::move(OS.str()); 107336815Sdim} 108336815Sdim 109336815Sdim/// Generates a reproducer for a set of arguments from a specific invocation. 110336815Sdimstatic llvm::Optional<driver::Driver::CompilationDiagnosticReport> 111336815SdimgenerateReproducerForInvocationArguments(ArrayRef<const char *> Argv, 112336815Sdim const ClangInvocationInfo &Info) { 113336815Sdim using namespace driver; 114336815Sdim auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]); 115336815Sdim 116336815Sdim IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions; 117336815Sdim 118336815Sdim IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 119336815Sdim DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer()); 120336815Sdim ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); 121336815Sdim Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags); 122336815Sdim TheDriver.setTargetAndMode(TargetAndMode); 123336815Sdim 124336815Sdim std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv)); 125336815Sdim if (C && !C->containsError()) { 126336815Sdim for (const auto &J : C->getJobs()) { 127336815Sdim if (const Command *Cmd = dyn_cast<Command>(&J)) { 128336815Sdim Driver::CompilationDiagnosticReport Report; 129336815Sdim TheDriver.generateCompilationDiagnostics( 130336815Sdim *C, *Cmd, generateReproducerMetaInfo(Info), &Report); 131336815Sdim return Report; 132336815Sdim } 133336815Sdim } 134336815Sdim } 135336815Sdim 136336815Sdim return None; 137336815Sdim} 138336815Sdim 139336815Sdimstd::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes); 140336815Sdim 141336815Sdimstatic void printReproducerInformation( 142336815Sdim llvm::raw_ostream &OS, const ClangInvocationInfo &Info, 143336815Sdim const driver::Driver::CompilationDiagnosticReport &Report) { 144336815Sdim OS << "REPRODUCER:\n"; 145336815Sdim OS << "{\n"; 146336815Sdim OS << R"("files":[)"; 147336815Sdim for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) { 148336815Sdim if (File.index()) 149336815Sdim OS << ','; 150336815Sdim OS << '"' << File.value() << '"'; 151336815Sdim } 152336815Sdim OS << "]\n}\n"; 153336815Sdim} 154336815Sdim 155336815Sdimint cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0, 156336815Sdim void *MainAddr) { 157336815Sdim if (Argv.size() < 1) { 158336815Sdim llvm::errs() << "error: missing invocation file\n"; 159336815Sdim return 1; 160336815Sdim } 161336815Sdim // Parse the invocation descriptor. 162336815Sdim StringRef Input = Argv[0]; 163336815Sdim llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = 164336815Sdim llvm::MemoryBuffer::getFile(Input); 165336815Sdim if (!Buffer) { 166336815Sdim llvm::errs() << "error: failed to read " << Input << ": " 167336815Sdim << Buffer.getError().message() << "\n"; 168336815Sdim return 1; 169336815Sdim } 170336815Sdim llvm::yaml::Input YAML(Buffer.get()->getBuffer()); 171336815Sdim ClangInvocationInfo InvocationInfo; 172336815Sdim YAML >> InvocationInfo; 173336815Sdim if (Argv.size() > 1 && Argv[1] == StringRef("-v")) 174336815Sdim InvocationInfo.Dump = true; 175336815Sdim 176336815Sdim // Create an invocation that will produce the reproducer. 177336815Sdim std::vector<const char *> DriverArgs; 178336815Sdim for (const auto &Arg : InvocationInfo.Arguments) 179336815Sdim DriverArgs.push_back(Arg.c_str()); 180336815Sdim std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true); 181336815Sdim DriverArgs[0] = Path.c_str(); 182336815Sdim llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report = 183336815Sdim generateReproducerForInvocationArguments(DriverArgs, InvocationInfo); 184336815Sdim 185336815Sdim // Emit the information about the reproduce files to stdout. 186336815Sdim int Result = 1; 187336815Sdim if (Report) { 188336815Sdim printReproducerInformation(llvm::outs(), InvocationInfo, *Report); 189336815Sdim Result = 0; 190336815Sdim } 191336815Sdim 192336815Sdim // Remove the input file. 193336815Sdim llvm::sys::fs::remove(Input); 194336815Sdim return Result; 195336815Sdim} 196