1 //===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===//
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
7 //===----------------------------------------------------------------------===//
9 // This is the entry point to the clang -cc1gen-reproducer functionality, which
10 // generates reproducers for invocations for clang-based tools.
12 //===----------------------------------------------------------------------===//
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"
29 using namespace clang
;
33 struct UnsavedFileHash
{
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
;
48 } // end anonymous namespace
50 LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash
)
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
);
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
);
73 } // end namespace yaml
74 } // end namespace llvm
76 static std::string
generateReproducerMetaInfo(const ClangInvocationInfo
&Info
) {
78 llvm::raw_string_ostream
OS(Result
);
80 bool NeedComma
= false;
81 auto EmitKey
= [&](StringRef Key
) {
85 OS
<< '"' << Key
<< "\": ";
87 auto EmitStringKey
= [&](StringRef Key
, StringRef Value
) {
91 OS
<< '"' << Value
<< '"';
93 EmitStringKey("libclang.operation", Info
.LibclangOperation
);
94 EmitStringKey("libclang.opts", Info
.LibclangOptions
);
95 if (!Info
.InvocationArguments
.empty()) {
96 EmitKey("invocation-args");
98 for (const auto &Arg
: llvm::enumerate(Info
.InvocationArguments
)) {
101 OS
<< '"' << Arg
.value() << '"';
106 // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
108 llvm::outs() << "REPRODUCER METAINFO: " << OS
.str() << "\n";
109 return std::move(OS
.str());
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]);
120 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
= new DiagnosticOptions
;
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(),
127 TheDriver
.setTargetAndMode(TargetAndMode
);
128 if (ToolContext
.NeedsPrependArg
)
129 TheDriver
.setPrependArg(ToolContext
.PrependArg
);
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
);
146 std::string
GetExecutablePath(const char *Argv0
, bool CanonicalPrefixes
);
148 static void printReproducerInformation(
149 llvm::raw_ostream
&OS
, const ClangInvocationInfo
&Info
,
150 const driver::Driver::CompilationDiagnosticReport
&Report
) {
151 OS
<< "REPRODUCER:\n";
153 OS
<< R
"("files
":[)";
154 for (const auto &File
: llvm::enumerate(Report
.TemporaryFiles
)) {
157 OS
<< '"' << File
.value() << '"';
162 int cc1gen_reproducer_main(ArrayRef
<const char *> Argv
, const char *Argv0
,
164 const llvm::ToolContext
&ToolContext
) {
165 if (Argv
.size() < 1) {
166 llvm::errs() << "error: missing invocation file\n";
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);
174 llvm::errs() << "error: failed to read " << Input
<< ": "
175 << Buffer
.getError().message() << "\n";
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;
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
,
194 // Emit the information about the reproduce files to stdout.
197 printReproducerInformation(llvm::outs(), InvocationInfo
, *Report
);
201 // Remove the input file.
202 llvm::sys::fs::remove(Input
);