Path: blob/main/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp
35236 views
//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This is the entry point to the clang -cc1gen-reproducer functionality, which9// generates reproducers for invocations for clang-based tools.10//11//===----------------------------------------------------------------------===//1213#include "clang/Basic/Diagnostic.h"14#include "clang/Basic/LLVM.h"15#include "clang/Driver/Compilation.h"16#include "clang/Driver/Driver.h"17#include "llvm/ADT/ArrayRef.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/Support/FileSystem.h"20#include "llvm/Support/LLVMDriver.h"21#include "llvm/Support/TargetSelect.h"22#include "llvm/Support/VirtualFileSystem.h"23#include "llvm/Support/YAMLTraits.h"24#include "llvm/Support/raw_ostream.h"25#include "llvm/TargetParser/Host.h"26#include <optional>2728using namespace clang;2930namespace {3132struct UnsavedFileHash {33std::string Name;34std::string MD5;35};3637struct ClangInvocationInfo {38std::string Toolchain;39std::string LibclangOperation;40std::string LibclangOptions;41std::vector<std::string> Arguments;42std::vector<std::string> InvocationArguments;43std::vector<UnsavedFileHash> UnsavedFileHashes;44bool Dump = false;45};4647} // end anonymous namespace4849LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)5051namespace llvm {52namespace yaml {5354template <> struct MappingTraits<UnsavedFileHash> {55static void mapping(IO &IO, UnsavedFileHash &Info) {56IO.mapRequired("name", Info.Name);57IO.mapRequired("md5", Info.MD5);58}59};6061template <> struct MappingTraits<ClangInvocationInfo> {62static void mapping(IO &IO, ClangInvocationInfo &Info) {63IO.mapRequired("toolchain", Info.Toolchain);64IO.mapOptional("libclang.operation", Info.LibclangOperation);65IO.mapOptional("libclang.opts", Info.LibclangOptions);66IO.mapRequired("args", Info.Arguments);67IO.mapOptional("invocation-args", Info.InvocationArguments);68IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);69}70};7172} // end namespace yaml73} // end namespace llvm7475static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {76std::string Result;77llvm::raw_string_ostream OS(Result);78OS << '{';79bool NeedComma = false;80auto EmitKey = [&](StringRef Key) {81if (NeedComma)82OS << ", ";83NeedComma = true;84OS << '"' << Key << "\": ";85};86auto EmitStringKey = [&](StringRef Key, StringRef Value) {87if (Value.empty())88return;89EmitKey(Key);90OS << '"' << Value << '"';91};92EmitStringKey("libclang.operation", Info.LibclangOperation);93EmitStringKey("libclang.opts", Info.LibclangOptions);94if (!Info.InvocationArguments.empty()) {95EmitKey("invocation-args");96OS << '[';97for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {98if (Arg.index())99OS << ',';100OS << '"' << Arg.value() << '"';101}102OS << ']';103}104OS << '}';105// FIXME: Compare unsaved file hashes and report mismatch in the reproducer.106if (Info.Dump)107llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";108return std::move(OS.str());109}110111/// Generates a reproducer for a set of arguments from a specific invocation.112static std::optional<driver::Driver::CompilationDiagnosticReport>113generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,114const ClangInvocationInfo &Info,115const llvm::ToolContext &ToolContext) {116using namespace driver;117auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);118119IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;120121IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());122DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());123ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);124Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(),125Diags);126TheDriver.setTargetAndMode(TargetAndMode);127if (ToolContext.NeedsPrependArg)128TheDriver.setPrependArg(ToolContext.PrependArg);129130std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));131if (C && !C->containsError()) {132for (const auto &J : C->getJobs()) {133if (const Command *Cmd = dyn_cast<Command>(&J)) {134Driver::CompilationDiagnosticReport Report;135TheDriver.generateCompilationDiagnostics(136*C, *Cmd, generateReproducerMetaInfo(Info), &Report);137return Report;138}139}140}141142return std::nullopt;143}144145std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);146147static void printReproducerInformation(148llvm::raw_ostream &OS, const ClangInvocationInfo &Info,149const driver::Driver::CompilationDiagnosticReport &Report) {150OS << "REPRODUCER:\n";151OS << "{\n";152OS << R"("files":[)";153for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {154if (File.index())155OS << ',';156OS << '"' << File.value() << '"';157}158OS << "]\n}\n";159}160161int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,162void *MainAddr,163const llvm::ToolContext &ToolContext) {164if (Argv.size() < 1) {165llvm::errs() << "error: missing invocation file\n";166return 1;167}168// Parse the invocation descriptor.169StringRef Input = Argv[0];170llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =171llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);172if (!Buffer) {173llvm::errs() << "error: failed to read " << Input << ": "174<< Buffer.getError().message() << "\n";175return 1;176}177llvm::yaml::Input YAML(Buffer.get()->getBuffer());178ClangInvocationInfo InvocationInfo;179YAML >> InvocationInfo;180if (Argv.size() > 1 && Argv[1] == StringRef("-v"))181InvocationInfo.Dump = true;182183// Create an invocation that will produce the reproducer.184std::vector<const char *> DriverArgs;185for (const auto &Arg : InvocationInfo.Arguments)186DriverArgs.push_back(Arg.c_str());187std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);188DriverArgs[0] = Path.c_str();189std::optional<driver::Driver::CompilationDiagnosticReport> Report =190generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,191ToolContext);192193// Emit the information about the reproduce files to stdout.194int Result = 1;195if (Report) {196printReproducerInformation(llvm::outs(), InvocationInfo, *Report);197Result = 0;198}199200// Remove the input file.201llvm::sys::fs::remove(Input);202return Result;203}204205206