Thanks to visit codestin.com
Credit goes to clang.llvm.org

clang 22.0.0git
DependencyScanningWorker.cpp
Go to the documentation of this file.
1//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
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
15#include "clang/Driver/Driver.h"
16#include "clang/Driver/Job.h"
17#include "clang/Driver/Tool.h"
28#include "llvm/ADT/IntrusiveRefCntPtr.h"
29#include "llvm/Support/Allocator.h"
30#include "llvm/Support/Error.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/TargetParser/Host.h"
33#include <optional>
34
35using namespace clang;
36using namespace tooling;
37using namespace dependencies;
38
42 : Service(Service) {
43 PCHContainerOps = std::make_shared<PCHContainerOperations>();
44 // We need to read object files from PCH built outside the scanner.
45 PCHContainerOps->registerReader(
46 std::make_unique<ObjectFilePCHContainerReader>());
47 // The scanner itself writes only raw ast files.
48 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
49
50 if (Service.shouldTraceVFS())
51 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
52
53 switch (Service.getMode()) {
55 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
56 Service.getSharedCache(), FS);
57 BaseFS = DepFS;
58 break;
60 DepFS = nullptr;
61 BaseFS = FS;
62 break;
63 }
64}
65
66static std::unique_ptr<DiagnosticOptions>
67createDiagOptions(const std::vector<std::string> &CommandLine) {
68 std::vector<const char *> CLI;
69 for (const std::string &Arg : CommandLine)
70 CLI.push_back(Arg.c_str());
71 auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
72 sanitizeDiagOpts(*DiagOpts);
73 return DiagOpts;
74}
75
77 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
79 std::optional<llvm::MemoryBufferRef> TUBuffer) {
80 // Capture the emitted diagnostics and report them to the client
81 // in the case of a failure.
82 std::string DiagnosticOutput;
83 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
84 auto DiagOpts = createDiagOptions(CommandLine);
85 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
86
87 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
88 DiagPrinter, TUBuffer))
89 return llvm::Error::success();
90 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
91 llvm::inconvertibleErrorCode());
92}
93
95 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
97 StringRef ModuleName) {
98 // Capture the emitted diagnostics and report them to the client
99 // in the case of a failure.
100 std::string DiagnosticOutput;
101 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
102 auto DiagOpts = createDiagOptions(CommandLine);
103 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
104
105 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
106 DiagPrinter, ModuleName))
107 return llvm::Error::success();
108 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
109 llvm::inconvertibleErrorCode());
110}
111
115 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
117 Argv.reserve(ArgStrs.size());
118 for (const std::string &Arg : ArgStrs)
119 Argv.push_back(Arg.c_str());
120
121 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
122 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
123 "clang LLVM compiler", FS);
124 Driver->setTitle("clang_based_tool");
125
126 llvm::BumpPtrAllocator Alloc;
127 bool CLMode = driver::IsClangCL(
128 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
129
130 if (llvm::Error E =
131 driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
132 Diags.Report(diag::err_drv_expand_response_file)
133 << llvm::toString(std::move(E));
134 return false;
135 }
136
137 const std::unique_ptr<driver::Compilation> Compilation(
138 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
139 if (!Compilation)
140 return false;
141
142 if (Compilation->containsError())
143 return false;
144
145 for (const driver::Command &Job : Compilation->getJobs()) {
146 if (!Callback(Job))
147 return false;
148 }
149 return true;
150}
151
153 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
155 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
156 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
157
158 // Save executable path before providing CommandLine to ToolInvocation
159 std::string Executable = CommandLine[0];
160
161 llvm::opt::ArgStringList Argv;
162 for (const std::string &Str : ArrayRef(CommandLine).drop_front())
163 Argv.push_back(Str.c_str());
164
165 auto Invocation = std::make_shared<CompilerInvocation>();
166 if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
167 // FIXME: Should we just go on like cc1_main does?
168 return false;
169 }
170
171 if (!Action.runInvocation(std::move(Invocation), std::move(FS),
172 PCHContainerOps, Diags.getClient()))
173 return false;
174
175 std::vector<std::string> Args = Action.takeLastCC1Arguments();
176 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
177 return true;
178}
179
180bool DependencyScanningWorker::scanDependencies(
181 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
182 DependencyConsumer &Consumer, DependencyActionController &Controller,
183 DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
184 std::optional<StringRef> ModuleName) {
185 std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
186 llvm::transform(CommandLine, CCommandLine.begin(),
187 [](const std::string &Str) { return Str.c_str(); });
188 auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
189 sanitizeDiagOpts(*DiagOpts);
190 auto Diags = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
191 /*ShouldOwnClient=*/false);
192
193 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
194 Controller, DepFS, ModuleName);
195
196 bool Success = false;
197 if (CommandLine[1] == "-cc1") {
198 Success = createAndRunToolInvocation(CommandLine, Action, FS,
199 PCHContainerOps, *Diags, Consumer);
200 } else {
202 CommandLine, *Diags, FS, [&](const driver::Command &Cmd) {
203 if (StringRef(Cmd.getCreator().getName()) != "clang") {
204 // Non-clang command. Just pass through to the dependency
205 // consumer.
206 Consumer.handleBuildCommand(
207 {Cmd.getExecutable(),
208 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
209 return true;
210 }
211
212 // Insert -cc1 comand line options into Argv
213 std::vector<std::string> Argv;
214 Argv.push_back(Cmd.getExecutable());
215 llvm::append_range(Argv, Cmd.getArguments());
216
217 // Create an invocation that uses the underlying file
218 // system to ensure that any file system requests that
219 // are made by the driver do not go through the
220 // dependency scanning filesystem.
221 return createAndRunToolInvocation(std::move(Argv), Action, FS,
222 PCHContainerOps, *Diags, Consumer);
223 });
224 }
225
226 if (Success && !Action.hasScanned())
227 Diags->Report(diag::err_fe_expected_compiler_job)
228 << llvm::join(CommandLine, " ");
229
230 // Ensure finish() is called even if we never reached ExecuteAction().
231 if (!Action.hasDiagConsumerFinished())
232 DC.finish();
233
234 return Success && Action.hasScanned();
235}
236
238 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
239 DependencyConsumer &Consumer, DependencyActionController &Controller,
240 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
241 // Reset what might have been modified in the previous worker invocation.
242 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
243
244 std::optional<std::vector<std::string>> ModifiedCommandLine;
246
247 // If we're scanning based on a module name alone, we don't expect the client
248 // to provide us with an input file. However, the driver really wants to have
249 // one. Let's just make it up to make the driver happy.
250 if (TUBuffer) {
251 auto OverlayFS =
252 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
253 auto InMemoryFS =
254 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
255 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
256 auto InputPath = TUBuffer->getBufferIdentifier();
257 InMemoryFS->addFile(
258 InputPath, 0,
259 llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
261 InMemoryFS;
262
263 OverlayFS->pushOverlay(InMemoryOverlay);
264 ModifiedFS = OverlayFS;
265 ModifiedCommandLine = CommandLine;
266 ModifiedCommandLine->emplace_back(InputPath);
267 }
268
269 const std::vector<std::string> &FinalCommandLine =
270 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
271 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
272
273 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
274 Controller, DC, FinalFS, /*ModuleName=*/std::nullopt);
275}
276
278 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
279 DependencyConsumer &Consumer, DependencyActionController &Controller,
280 DiagnosticConsumer &DC, StringRef ModuleName) {
281 // Reset what might have been modified in the previous worker invocation.
282 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
283
284 // If we're scanning based on a module name alone, we don't expect the client
285 // to provide us with an input file. However, the driver really wants to have
286 // one. Let's just make it up to make the driver happy.
287 auto OverlayFS =
288 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
289 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
290 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
291 SmallString<128> FakeInputPath;
292 // TODO: We should retry the creation if the path already exists.
293 llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
294 /*MakeAbsolute=*/false);
295 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
296 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
297
298 OverlayFS->pushOverlay(InMemoryOverlay);
299 auto ModifiedCommandLine = CommandLine;
300 ModifiedCommandLine.emplace_back(FakeInputPath);
301
302 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
303 Controller, DC, OverlayFS, ModuleName);
304}
305
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
static std::unique_ptr< DiagnosticOptions > createDiagOptions(const std::vector< std::string > &CommandLine)
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:231
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
DiagnosticConsumer * getClient()
Definition Diagnostic.h:606
Command - An executable path/name and argument vector to execute.
Definition Job.h:106
const Tool & getCreator() const
getCreator - Return the Tool which caused the creation of this job.
Definition Job.h:191
const llvm::opt::ArgStringList & getArguments() const
Definition Job.h:224
const char * getExecutable() const
Definition Job.h:222
const char * getName() const
Definition Tool.h:48
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
std::vector< std::string > takeLastCC1Arguments()
Take the cc1 arguments corresponding to the most recent invocation used with this action.
bool runInvocation(std::shared_ptr< CompilerInvocation > Invocation, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)
The dependency scanning service contains shared configuration and state that is used by the individua...
bool computeDependencies(StringRef WorkingDirectory, const std::vector< std::string > &CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, std::optional< llvm::MemoryBufferRef > TUBuffer=std::nullopt)
Run the dependency scanning tool for a given clang driver command-line, and report the discovered dep...
DependencyScanningWorker(DependencyScanningService &Service, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
Construct a dependency scanning worker.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
Definition Driver.cpp:7143
llvm::Error expandResponseFiles(SmallVectorImpl< const char * > &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS=nullptr)
Expand response files from a clang driver or cc1 invocation.
Definition Driver.cpp:7160
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition Driver.cpp:7158
@ DependencyDirectivesScan
This mode is used to compute the dependencies by running the preprocessor with special kind of lexing...
@ CanonicalPreprocessing
This mode is used to compute the dependencies by running the preprocessor over the source files.
void sanitizeDiagOpts(DiagnosticOptions &DiagOpts)
Sanitize diagnostic options for dependency scan.
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Annotation was successful.
Definition Parser.h:65