1e5dd7070Spatrick //===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This is the entry point to the clang -cc1gen-reproducer functionality, which
10e5dd7070Spatrick // generates reproducers for invocations for clang-based tools.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/Basic/Diagnostic.h"
15e5dd7070Spatrick #include "clang/Basic/LLVM.h"
16e5dd7070Spatrick #include "clang/Driver/Compilation.h"
17e5dd7070Spatrick #include "clang/Driver/Driver.h"
18e5dd7070Spatrick #include "llvm/ADT/ArrayRef.h"
19e5dd7070Spatrick #include "llvm/ADT/STLExtras.h"
20e5dd7070Spatrick #include "llvm/Support/FileSystem.h"
21ec727ea7Spatrick #include "llvm/Support/Host.h"
22e5dd7070Spatrick #include "llvm/Support/TargetSelect.h"
23e5dd7070Spatrick #include "llvm/Support/VirtualFileSystem.h"
24e5dd7070Spatrick #include "llvm/Support/YAMLTraits.h"
25e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
26*12c85518Srobert #include <optional>
27e5dd7070Spatrick
28e5dd7070Spatrick using namespace clang;
29e5dd7070Spatrick
30e5dd7070Spatrick namespace {
31e5dd7070Spatrick
32e5dd7070Spatrick struct UnsavedFileHash {
33e5dd7070Spatrick std::string Name;
34e5dd7070Spatrick std::string MD5;
35e5dd7070Spatrick };
36e5dd7070Spatrick
37e5dd7070Spatrick struct ClangInvocationInfo {
38e5dd7070Spatrick std::string Toolchain;
39e5dd7070Spatrick std::string LibclangOperation;
40e5dd7070Spatrick std::string LibclangOptions;
41e5dd7070Spatrick std::vector<std::string> Arguments;
42e5dd7070Spatrick std::vector<std::string> InvocationArguments;
43e5dd7070Spatrick std::vector<UnsavedFileHash> UnsavedFileHashes;
44e5dd7070Spatrick bool Dump = false;
45e5dd7070Spatrick };
46e5dd7070Spatrick
47e5dd7070Spatrick } // end anonymous namespace
48e5dd7070Spatrick
49e5dd7070Spatrick LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
50e5dd7070Spatrick
51e5dd7070Spatrick namespace llvm {
52e5dd7070Spatrick namespace yaml {
53e5dd7070Spatrick
54e5dd7070Spatrick template <> struct MappingTraits<UnsavedFileHash> {
mappingllvm::yaml::MappingTraits55e5dd7070Spatrick static void mapping(IO &IO, UnsavedFileHash &Info) {
56e5dd7070Spatrick IO.mapRequired("name", Info.Name);
57e5dd7070Spatrick IO.mapRequired("md5", Info.MD5);
58e5dd7070Spatrick }
59e5dd7070Spatrick };
60e5dd7070Spatrick
61e5dd7070Spatrick template <> struct MappingTraits<ClangInvocationInfo> {
mappingllvm::yaml::MappingTraits62e5dd7070Spatrick static void mapping(IO &IO, ClangInvocationInfo &Info) {
63e5dd7070Spatrick IO.mapRequired("toolchain", Info.Toolchain);
64e5dd7070Spatrick IO.mapOptional("libclang.operation", Info.LibclangOperation);
65e5dd7070Spatrick IO.mapOptional("libclang.opts", Info.LibclangOptions);
66e5dd7070Spatrick IO.mapRequired("args", Info.Arguments);
67e5dd7070Spatrick IO.mapOptional("invocation-args", Info.InvocationArguments);
68e5dd7070Spatrick IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
69e5dd7070Spatrick }
70e5dd7070Spatrick };
71e5dd7070Spatrick
72e5dd7070Spatrick } // end namespace yaml
73e5dd7070Spatrick } // end namespace llvm
74e5dd7070Spatrick
generateReproducerMetaInfo(const ClangInvocationInfo & Info)75e5dd7070Spatrick static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
76e5dd7070Spatrick std::string Result;
77e5dd7070Spatrick llvm::raw_string_ostream OS(Result);
78e5dd7070Spatrick OS << '{';
79e5dd7070Spatrick bool NeedComma = false;
80e5dd7070Spatrick auto EmitKey = [&](StringRef Key) {
81e5dd7070Spatrick if (NeedComma)
82e5dd7070Spatrick OS << ", ";
83e5dd7070Spatrick NeedComma = true;
84e5dd7070Spatrick OS << '"' << Key << "\": ";
85e5dd7070Spatrick };
86e5dd7070Spatrick auto EmitStringKey = [&](StringRef Key, StringRef Value) {
87e5dd7070Spatrick if (Value.empty())
88e5dd7070Spatrick return;
89e5dd7070Spatrick EmitKey(Key);
90e5dd7070Spatrick OS << '"' << Value << '"';
91e5dd7070Spatrick };
92e5dd7070Spatrick EmitStringKey("libclang.operation", Info.LibclangOperation);
93e5dd7070Spatrick EmitStringKey("libclang.opts", Info.LibclangOptions);
94e5dd7070Spatrick if (!Info.InvocationArguments.empty()) {
95e5dd7070Spatrick EmitKey("invocation-args");
96e5dd7070Spatrick OS << '[';
97e5dd7070Spatrick for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
98e5dd7070Spatrick if (Arg.index())
99e5dd7070Spatrick OS << ',';
100e5dd7070Spatrick OS << '"' << Arg.value() << '"';
101e5dd7070Spatrick }
102e5dd7070Spatrick OS << ']';
103e5dd7070Spatrick }
104e5dd7070Spatrick OS << '}';
105e5dd7070Spatrick // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
106e5dd7070Spatrick if (Info.Dump)
107e5dd7070Spatrick llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
108e5dd7070Spatrick return std::move(OS.str());
109e5dd7070Spatrick }
110e5dd7070Spatrick
111e5dd7070Spatrick /// Generates a reproducer for a set of arguments from a specific invocation.
112*12c85518Srobert static std::optional<driver::Driver::CompilationDiagnosticReport>
generateReproducerForInvocationArguments(ArrayRef<const char * > Argv,const ClangInvocationInfo & Info)113e5dd7070Spatrick generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
114e5dd7070Spatrick const ClangInvocationInfo &Info) {
115e5dd7070Spatrick using namespace driver;
116e5dd7070Spatrick auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
117e5dd7070Spatrick
118e5dd7070Spatrick IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
119e5dd7070Spatrick
120e5dd7070Spatrick IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
121e5dd7070Spatrick DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
122e5dd7070Spatrick ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
123e5dd7070Spatrick Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
124e5dd7070Spatrick TheDriver.setTargetAndMode(TargetAndMode);
125e5dd7070Spatrick
126e5dd7070Spatrick std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
127e5dd7070Spatrick if (C && !C->containsError()) {
128e5dd7070Spatrick for (const auto &J : C->getJobs()) {
129e5dd7070Spatrick if (const Command *Cmd = dyn_cast<Command>(&J)) {
130e5dd7070Spatrick Driver::CompilationDiagnosticReport Report;
131e5dd7070Spatrick TheDriver.generateCompilationDiagnostics(
132e5dd7070Spatrick *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
133e5dd7070Spatrick return Report;
134e5dd7070Spatrick }
135e5dd7070Spatrick }
136e5dd7070Spatrick }
137e5dd7070Spatrick
138*12c85518Srobert return std::nullopt;
139e5dd7070Spatrick }
140e5dd7070Spatrick
141e5dd7070Spatrick std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
142e5dd7070Spatrick
printReproducerInformation(llvm::raw_ostream & OS,const ClangInvocationInfo & Info,const driver::Driver::CompilationDiagnosticReport & Report)143e5dd7070Spatrick static void printReproducerInformation(
144e5dd7070Spatrick llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
145e5dd7070Spatrick const driver::Driver::CompilationDiagnosticReport &Report) {
146e5dd7070Spatrick OS << "REPRODUCER:\n";
147e5dd7070Spatrick OS << "{\n";
148e5dd7070Spatrick OS << R"("files":[)";
149e5dd7070Spatrick for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
150e5dd7070Spatrick if (File.index())
151e5dd7070Spatrick OS << ',';
152e5dd7070Spatrick OS << '"' << File.value() << '"';
153e5dd7070Spatrick }
154e5dd7070Spatrick OS << "]\n}\n";
155e5dd7070Spatrick }
156e5dd7070Spatrick
cc1gen_reproducer_main(ArrayRef<const char * > Argv,const char * Argv0,void * MainAddr)157e5dd7070Spatrick int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
158e5dd7070Spatrick void *MainAddr) {
159e5dd7070Spatrick if (Argv.size() < 1) {
160e5dd7070Spatrick llvm::errs() << "error: missing invocation file\n";
161e5dd7070Spatrick return 1;
162e5dd7070Spatrick }
163e5dd7070Spatrick // Parse the invocation descriptor.
164e5dd7070Spatrick StringRef Input = Argv[0];
165e5dd7070Spatrick llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
166a9ac8606Spatrick llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);
167e5dd7070Spatrick if (!Buffer) {
168e5dd7070Spatrick llvm::errs() << "error: failed to read " << Input << ": "
169e5dd7070Spatrick << Buffer.getError().message() << "\n";
170e5dd7070Spatrick return 1;
171e5dd7070Spatrick }
172e5dd7070Spatrick llvm::yaml::Input YAML(Buffer.get()->getBuffer());
173e5dd7070Spatrick ClangInvocationInfo InvocationInfo;
174e5dd7070Spatrick YAML >> InvocationInfo;
175e5dd7070Spatrick if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
176e5dd7070Spatrick InvocationInfo.Dump = true;
177e5dd7070Spatrick
178e5dd7070Spatrick // Create an invocation that will produce the reproducer.
179e5dd7070Spatrick std::vector<const char *> DriverArgs;
180e5dd7070Spatrick for (const auto &Arg : InvocationInfo.Arguments)
181e5dd7070Spatrick DriverArgs.push_back(Arg.c_str());
182e5dd7070Spatrick std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
183e5dd7070Spatrick DriverArgs[0] = Path.c_str();
184*12c85518Srobert std::optional<driver::Driver::CompilationDiagnosticReport> Report =
185e5dd7070Spatrick generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
186e5dd7070Spatrick
187e5dd7070Spatrick // Emit the information about the reproduce files to stdout.
188e5dd7070Spatrick int Result = 1;
189e5dd7070Spatrick if (Report) {
190e5dd7070Spatrick printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
191e5dd7070Spatrick Result = 0;
192e5dd7070Spatrick }
193e5dd7070Spatrick
194e5dd7070Spatrick // Remove the input file.
195e5dd7070Spatrick llvm::sys::fs::remove(Input);
196e5dd7070Spatrick return Result;
197e5dd7070Spatrick }
198