Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/tools/driver/cc1gen_reproducer_main.cpp
35236 views
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
29
using namespace clang;
30
31
namespace {
32
33
struct UnsavedFileHash {
34
std::string Name;
35
std::string MD5;
36
};
37
38
struct 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
50
LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
51
52
namespace llvm {
53
namespace yaml {
54
55
template <> struct MappingTraits<UnsavedFileHash> {
56
static void mapping(IO &IO, UnsavedFileHash &Info) {
57
IO.mapRequired("name", Info.Name);
58
IO.mapRequired("md5", Info.MD5);
59
}
60
};
61
62
template <> struct MappingTraits<ClangInvocationInfo> {
63
static void mapping(IO &IO, ClangInvocationInfo &Info) {
64
IO.mapRequired("toolchain", Info.Toolchain);
65
IO.mapOptional("libclang.operation", Info.LibclangOperation);
66
IO.mapOptional("libclang.opts", Info.LibclangOptions);
67
IO.mapRequired("args", Info.Arguments);
68
IO.mapOptional("invocation-args", Info.InvocationArguments);
69
IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
70
}
71
};
72
73
} // end namespace yaml
74
} // end namespace llvm
75
76
static 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(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: " << OS.str() << "\n";
109
return std::move(OS.str());
110
}
111
112
/// Generates a reproducer for a set of arguments from a specific invocation.
113
static std::optional<driver::Driver::CompilationDiagnosticReport>
114
generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
115
const ClangInvocationInfo &Info,
116
const llvm::ToolContext &ToolContext) {
117
using namespace driver;
118
auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
119
120
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
121
122
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
123
DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
124
ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
125
Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(),
126
Diags);
127
TheDriver.setTargetAndMode(TargetAndMode);
128
if (ToolContext.NeedsPrependArg)
129
TheDriver.setPrependArg(ToolContext.PrependArg);
130
131
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
132
if (C && !C->containsError()) {
133
for (const auto &J : C->getJobs()) {
134
if (const Command *Cmd = dyn_cast<Command>(&J)) {
135
Driver::CompilationDiagnosticReport Report;
136
TheDriver.generateCompilationDiagnostics(
137
*C, *Cmd, generateReproducerMetaInfo(Info), &Report);
138
return Report;
139
}
140
}
141
}
142
143
return std::nullopt;
144
}
145
146
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
147
148
static void printReproducerInformation(
149
llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
150
const driver::Driver::CompilationDiagnosticReport &Report) {
151
OS << "REPRODUCER:\n";
152
OS << "{\n";
153
OS << R"("files":[)";
154
for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
155
if (File.index())
156
OS << ',';
157
OS << '"' << File.value() << '"';
158
}
159
OS << "]\n}\n";
160
}
161
162
int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
163
void *MainAddr,
164
const llvm::ToolContext &ToolContext) {
165
if (Argv.size() < 1) {
166
llvm::errs() << "error: missing invocation file\n";
167
return 1;
168
}
169
// Parse the invocation descriptor.
170
StringRef Input = Argv[0];
171
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
172
llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);
173
if (!Buffer) {
174
llvm::errs() << "error: failed to read " << Input << ": "
175
<< Buffer.getError().message() << "\n";
176
return 1;
177
}
178
llvm::yaml::Input YAML(Buffer.get()->getBuffer());
179
ClangInvocationInfo InvocationInfo;
180
YAML >> InvocationInfo;
181
if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
182
InvocationInfo.Dump = true;
183
184
// Create an invocation that will produce the reproducer.
185
std::vector<const char *> DriverArgs;
186
for (const auto &Arg : InvocationInfo.Arguments)
187
DriverArgs.push_back(Arg.c_str());
188
std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
189
DriverArgs[0] = Path.c_str();
190
std::optional<driver::Driver::CompilationDiagnosticReport> Report =
191
generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,
192
ToolContext);
193
194
// Emit the information about the reproduce files to stdout.
195
int Result = 1;
196
if (Report) {
197
printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
198
Result = 0;
199
}
200
201
// Remove the input file.
202
llvm::sys::fs::remove(Input);
203
return Result;
204
}
205
206