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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/tools/driver/cc1gen_reproducer_main.cpp