forked from OSchip/llvm-project
197 lines
6.3 KiB
C++
197 lines
6.3 KiB
C++
//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This is the entry point to the clang -cc1gen-reproducer functionality, which
|
|
// generates reproducers for invocations for clang-based tools.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Driver/Compilation.h"
|
|
#include "clang/Driver/Driver.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "llvm/Support/YAMLTraits.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
|
|
struct UnsavedFileHash {
|
|
std::string Name;
|
|
std::string MD5;
|
|
};
|
|
|
|
struct ClangInvocationInfo {
|
|
std::string Toolchain;
|
|
std::string LibclangOperation;
|
|
std::string LibclangOptions;
|
|
std::vector<std::string> Arguments;
|
|
std::vector<std::string> InvocationArguments;
|
|
std::vector<UnsavedFileHash> UnsavedFileHashes;
|
|
bool Dump = false;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
|
|
|
|
namespace llvm {
|
|
namespace yaml {
|
|
|
|
template <> struct MappingTraits<UnsavedFileHash> {
|
|
static void mapping(IO &IO, UnsavedFileHash &Info) {
|
|
IO.mapRequired("name", Info.Name);
|
|
IO.mapRequired("md5", Info.MD5);
|
|
}
|
|
};
|
|
|
|
template <> struct MappingTraits<ClangInvocationInfo> {
|
|
static void mapping(IO &IO, ClangInvocationInfo &Info) {
|
|
IO.mapRequired("toolchain", Info.Toolchain);
|
|
IO.mapOptional("libclang.operation", Info.LibclangOperation);
|
|
IO.mapOptional("libclang.opts", Info.LibclangOptions);
|
|
IO.mapRequired("args", Info.Arguments);
|
|
IO.mapOptional("invocation-args", Info.InvocationArguments);
|
|
IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
|
|
}
|
|
};
|
|
|
|
} // end namespace yaml
|
|
} // end namespace llvm
|
|
|
|
static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
|
|
std::string Result;
|
|
llvm::raw_string_ostream OS(Result);
|
|
OS << '{';
|
|
bool NeedComma = false;
|
|
auto EmitKey = [&](StringRef Key) {
|
|
if (NeedComma)
|
|
OS << ", ";
|
|
NeedComma = true;
|
|
OS << '"' << Key << "\": ";
|
|
};
|
|
auto EmitStringKey = [&](StringRef Key, StringRef Value) {
|
|
if (Value.empty())
|
|
return;
|
|
EmitKey(Key);
|
|
OS << '"' << Value << '"';
|
|
};
|
|
EmitStringKey("libclang.operation", Info.LibclangOperation);
|
|
EmitStringKey("libclang.opts", Info.LibclangOptions);
|
|
if (!Info.InvocationArguments.empty()) {
|
|
EmitKey("invocation-args");
|
|
OS << '[';
|
|
for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
|
|
if (Arg.index())
|
|
OS << ',';
|
|
OS << '"' << Arg.value() << '"';
|
|
}
|
|
OS << ']';
|
|
}
|
|
OS << '}';
|
|
// FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
|
|
if (Info.Dump)
|
|
llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
|
|
return std::move(OS.str());
|
|
}
|
|
|
|
/// Generates a reproducer for a set of arguments from a specific invocation.
|
|
static llvm::Optional<driver::Driver::CompilationDiagnosticReport>
|
|
generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
|
|
const ClangInvocationInfo &Info) {
|
|
using namespace driver;
|
|
auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
|
|
|
|
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
|
|
|
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
|
|
ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
|
|
Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
|
TheDriver.setTargetAndMode(TargetAndMode);
|
|
|
|
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
|
|
if (C && !C->containsError()) {
|
|
for (const auto &J : C->getJobs()) {
|
|
if (const Command *Cmd = dyn_cast<Command>(&J)) {
|
|
Driver::CompilationDiagnosticReport Report;
|
|
TheDriver.generateCompilationDiagnostics(
|
|
*C, *Cmd, generateReproducerMetaInfo(Info), &Report);
|
|
return Report;
|
|
}
|
|
}
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
|
|
|
|
static void printReproducerInformation(
|
|
llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
|
|
const driver::Driver::CompilationDiagnosticReport &Report) {
|
|
OS << "REPRODUCER:\n";
|
|
OS << "{\n";
|
|
OS << R"("files":[)";
|
|
for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
|
|
if (File.index())
|
|
OS << ',';
|
|
OS << '"' << File.value() << '"';
|
|
}
|
|
OS << "]\n}\n";
|
|
}
|
|
|
|
int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
|
|
void *MainAddr) {
|
|
if (Argv.size() < 1) {
|
|
llvm::errs() << "error: missing invocation file\n";
|
|
return 1;
|
|
}
|
|
// Parse the invocation descriptor.
|
|
StringRef Input = Argv[0];
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
|
|
llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);
|
|
if (!Buffer) {
|
|
llvm::errs() << "error: failed to read " << Input << ": "
|
|
<< Buffer.getError().message() << "\n";
|
|
return 1;
|
|
}
|
|
llvm::yaml::Input YAML(Buffer.get()->getBuffer());
|
|
ClangInvocationInfo InvocationInfo;
|
|
YAML >> InvocationInfo;
|
|
if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
|
|
InvocationInfo.Dump = true;
|
|
|
|
// Create an invocation that will produce the reproducer.
|
|
std::vector<const char *> DriverArgs;
|
|
for (const auto &Arg : InvocationInfo.Arguments)
|
|
DriverArgs.push_back(Arg.c_str());
|
|
std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
|
|
DriverArgs[0] = Path.c_str();
|
|
llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report =
|
|
generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
|
|
|
|
// Emit the information about the reproduce files to stdout.
|
|
int Result = 1;
|
|
if (Report) {
|
|
printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
|
|
Result = 0;
|
|
}
|
|
|
|
// Remove the input file.
|
|
llvm::sys::fs::remove(Input);
|
|
return Result;
|
|
}
|