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
12#include "clang/Driver/Driver.h"
13#include "clang/Driver/Tool.h"
14
15using namespace clang;
16using namespace tooling;
17using namespace dependencies;
18
22 : Service(Service) {
23 PCHContainerOps = std::make_shared<PCHContainerOperations>();
24 // We need to read object files from PCH built outside the scanner.
25 PCHContainerOps->registerReader(
26 std::make_unique<ObjectFilePCHContainerReader>());
27 // The scanner itself writes only raw ast files.
28 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
29
30 if (Service.shouldTraceVFS())
31 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
32
33 switch (Service.getMode()) {
35 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
36 Service.getSharedCache(), FS);
37 BaseFS = DepFS;
38 break;
40 DepFS = nullptr;
41 BaseFS = FS;
42 break;
43 }
44}
45
47 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
49 std::optional<llvm::MemoryBufferRef> TUBuffer) {
50 // Capture the emitted diagnostics and report them to the client
51 // in the case of a failure.
52 TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine);
53
54 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
55 DiagPrinterWithOS.DiagPrinter, TUBuffer))
56 return llvm::Error::success();
57 return llvm::make_error<llvm::StringError>(
58 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
59}
60
62 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
64 StringRef ModuleName) {
65 // Capture the emitted diagnostics and report them to the client
66 // in the case of a failure.
67 TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine);
68
69 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
70 DiagPrinterWithOS.DiagPrinter, ModuleName))
71 return llvm::Error::success();
72 return llvm::make_error<llvm::StringError>(
73 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
74}
75
76static bool forEachDriverJob(
79 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
80 // Compilation holds a non-owning a reference to the Driver, hence we need to
81 // keep the Driver alive when we use Compilation.
82 auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS);
83 if (!Compilation)
84 return false;
85 for (const driver::Command &Job : Compilation->getJobs()) {
86 if (!Callback(Job))
87 return false;
88 }
89 return true;
90}
91
93 const std::vector<std::string> &CommandLine,
96 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
97 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
98 auto Invocation = createCompilerInvocation(CommandLine, Diags);
99 if (!Invocation)
100 return false;
101
102 if (!Action.runInvocation(std::move(Invocation), std::move(FS),
103 PCHContainerOps, Diags.getClient()))
104 return false;
105
106 std::vector<std::string> Args = Action.takeLastCC1Arguments();
107 Consumer.handleBuildCommand({CommandLine[0], std::move(Args)});
108 return true;
109}
110
111bool DependencyScanningWorker::scanDependencies(
112 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
113 DependencyConsumer &Consumer, DependencyActionController &Controller,
114 DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
115 std::optional<StringRef> ModuleName) {
116 DignosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC);
117 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
118 Controller, DepFS, ModuleName);
119
120 bool Success = false;
121 if (CommandLine[1] == "-cc1") {
123 CommandLine, Action, FS, PCHContainerOps,
124 *DiagEngineWithCmdAndOpts.DiagEngine, Consumer);
125 } else {
127 CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS,
128 [&](const driver::Command &Cmd) {
129 if (StringRef(Cmd.getCreator().getName()) != "clang") {
130 // Non-clang command. Just pass through to the dependency
131 // consumer.
132 Consumer.handleBuildCommand(
133 {Cmd.getExecutable(),
134 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
135 return true;
136 }
137
138 // Insert -cc1 comand line options into Argv
139 std::vector<std::string> Argv;
140 Argv.push_back(Cmd.getExecutable());
141 llvm::append_range(Argv, Cmd.getArguments());
142
143 // Create an invocation that uses the underlying file
144 // system to ensure that any file system requests that
145 // are made by the driver do not go through the
146 // dependency scanning filesystem.
148 std::move(Argv), Action, FS, PCHContainerOps,
149 *DiagEngineWithCmdAndOpts.DiagEngine, Consumer);
150 });
151 }
152
153 if (Success && !Action.hasScanned())
154 DiagEngineWithCmdAndOpts.DiagEngine->Report(
155 diag::err_fe_expected_compiler_job)
156 << llvm::join(CommandLine, " ");
157
158 // Ensure finish() is called even if we never reached ExecuteAction().
159 if (!Action.hasDiagConsumerFinished())
160 DC.finish();
161
162 return Success && Action.hasScanned();
163}
164
166 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
167 DependencyConsumer &Consumer, DependencyActionController &Controller,
168 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
169 if (TUBuffer) {
170 auto [FinalFS, FinalCommandLine] = initVFSForTUBuferScanning(
171 BaseFS, CommandLine, WorkingDirectory, *TUBuffer);
172 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
173 Controller, DC, FinalFS,
174 /*ModuleName=*/std::nullopt);
175 } else {
176 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
177 return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
178 DC, BaseFS, /*ModuleName=*/std::nullopt);
179 }
180}
181
183 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
184 DependencyConsumer &Consumer, DependencyActionController &Controller,
185 DiagnosticConsumer &DC, StringRef ModuleName) {
186 auto [OverlayFS, ModifiedCommandLine] = initVFSForByNameScanning(
187 BaseFS, CommandLine, WorkingDirectory, ModuleName);
188
189 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
190 Controller, DC, OverlayFS, ModuleName);
191}
192
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
static bool createAndRunToolInvocation(const std::vector< std::string > &CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
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:232
DiagnosticConsumer * getClient()
Definition Diagnostic.h:607
Command - An executable path/name and argument vector to execute.
Definition Job.h:106
const llvm::opt::ArgStringList & getArguments() const
Definition Job.h:224
const char * getExecutable() const
Definition Job.h:222
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
bool runInvocation(std::unique_ptr< CompilerInvocation > Invocation, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)
std::vector< std::string > takeLastCC1Arguments()
Take the cc1 arguments corresponding to the most recent invocation used with this action.
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.
std::pair< std::unique_ptr< driver::Driver >, std::unique_ptr< driver::Compilation > > buildCompilation(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
@ 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.
std::unique_ptr< CompilerInvocation > createCompilerInvocation(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags)
std::pair< IntrusiveRefCntPtr< llvm::vfs::FileSystem >, std::vector< std::string > > initVFSForByNameScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, StringRef ModuleName)
std::pair< IntrusiveRefCntPtr< llvm::vfs::FileSystem >, std::vector< std::string > > initVFSForTUBuferScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, llvm::MemoryBufferRef TUBuffer)
The JSON file list parser is used to communicate input to InstallAPI.
@ Success
Annotation was successful.
Definition Parser.h:65