clang 22.0.0git
DependencyScanningTool.cpp
Go to the documentation of this file.
1//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
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/Tool.h"
14#include <optional>
15
16using namespace clang;
17using namespace tooling;
18using namespace dependencies;
19
24
25namespace {
26/// Prints out all of the gathered dependencies into a string.
27class MakeDependencyPrinterConsumer : public DependencyConsumer {
28public:
29 void handleBuildCommand(Command) override {}
30
31 void
32 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
33 this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
34 }
35
36 void handleFileDependency(StringRef File) override {
37 Dependencies.push_back(std::string(File));
38 }
39
40 // These are ignored for the make format as it can't support the full
41 // set of deps, and handleFileDependency handles enough for implicitly
42 // built modules to work.
43 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
44 void handleModuleDependency(ModuleDeps MD) override {}
45 void handleDirectModuleDependency(ModuleID ID) override {}
46 void handleVisibleModule(std::string ModuleName) override {}
47 void handleContextHash(std::string Hash) override {}
48
49 void printDependencies(std::string &S) {
50 assert(Opts && "Handled dependency output options.");
51
52 class DependencyPrinter : public DependencyFileGenerator {
53 public:
54 DependencyPrinter(DependencyOutputOptions &Opts,
55 ArrayRef<std::string> Dependencies)
56 : DependencyFileGenerator(Opts) {
57 for (const auto &Dep : Dependencies)
58 addDependency(Dep);
59 }
60
61 void printDependencies(std::string &S) {
62 llvm::raw_string_ostream OS(S);
63 outputDependencyFile(OS);
64 }
65 };
66
67 DependencyPrinter Generator(*Opts, Dependencies);
68 Generator.printDependencies(S);
69 }
70
71protected:
72 std::unique_ptr<DependencyOutputOptions> Opts;
73 std::vector<std::string> Dependencies;
74};
75} // anonymous namespace
76
77std::optional<std::string>
79 StringRef CWD,
80 DiagnosticConsumer &DiagConsumer) {
81 MakeDependencyPrinterConsumer DepConsumer;
82 CallbackActionController Controller(nullptr);
83 if (!Worker.computeDependencies(CWD, CommandLine, DepConsumer, Controller,
84 DiagConsumer))
85 return std::nullopt;
86 std::string Output;
87 DepConsumer.printDependencies(Output);
88 return Output;
89}
90
92 const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
93 std::string &MakeformatOutputPath, DiagnosticConsumer &DiagConsumer) {
94 class P1689ModuleDependencyPrinterConsumer
95 : public MakeDependencyPrinterConsumer {
96 public:
97 P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
99 : Filename(Command.Filename), Rule(Rule) {
100 Rule.PrimaryOutput = Command.Output;
101 }
102
103 void handleProvidedAndRequiredStdCXXModules(
104 std::optional<P1689ModuleInfo> Provided,
105 std::vector<P1689ModuleInfo> Requires) override {
106 Rule.Provides = Provided;
107 if (Rule.Provides)
108 Rule.Provides->SourcePath = Filename.str();
109 Rule.Requires = Requires;
110 }
111
112 StringRef getMakeFormatDependencyOutputPath() {
113 if (Opts->OutputFormat != DependencyOutputFormat::Make)
114 return {};
115 return Opts->OutputFile;
116 }
117
118 private:
119 StringRef Filename;
120 P1689Rule &Rule;
121 };
122
123 class P1689ActionController : public DependencyActionController {
124 public:
125 // The lookupModuleOutput is for clang modules. P1689 format don't need it.
126 std::string lookupModuleOutput(const ModuleDeps &,
127 ModuleOutputKind Kind) override {
128 return "";
129 }
130 };
131
132 P1689Rule Rule;
133 P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
134 P1689ActionController Controller;
135 if (!Worker.computeDependencies(CWD, Command.CommandLine, Consumer,
136 Controller, DiagConsumer))
137 return std::nullopt;
138
139 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
140 if (!MakeformatOutputPath.empty())
141 Consumer.printDependencies(MakeformatOutput);
142 return Rule;
143}
144
145std::optional<TranslationUnitDeps>
147 ArrayRef<std::string> CommandLine, StringRef CWD,
148 DiagnosticConsumer &DiagConsumer,
149 const llvm::DenseSet<ModuleID> &AlreadySeen,
150 LookupModuleOutputCallback LookupModuleOutput,
151 std::optional<llvm::MemoryBufferRef> TUBuffer) {
152 FullDependencyConsumer Consumer(AlreadySeen);
153 CallbackActionController Controller(LookupModuleOutput);
154
155 if (!Worker.computeDependencies(CWD, CommandLine, Consumer, Controller,
156 DiagConsumer, TUBuffer))
157 return std::nullopt;
158 return Consumer.takeTranslationUnitDeps();
159}
160
163 StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
164 const llvm::DenseSet<ModuleID> &AlreadySeen,
165 LookupModuleOutputCallback LookupModuleOutput) {
166 if (auto Error =
168 return Error;
169
171 ModuleName, AlreadySeen, LookupModuleOutput);
172
174 return Error;
175
176 return Result;
177}
178
179/// Constructs the full -cc1 command line, including executable, for the given
180/// driver \c Cmd.
181static std::vector<std::string>
183 const auto &Args = Cmd.getArguments();
184 std::vector<std::string> Out;
185 Out.reserve(Args.size() + 1);
186 Out.emplace_back(Cmd.getExecutable());
187 llvm::append_range(Out, Args);
188 return Out;
189}
190
191static std::optional<std::vector<std::string>> getFirstCC1CommandLine(
192 ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags,
194 // Compilation holds a non-owning a reference to the Driver, hence we need to
195 // keep the Driver alive when we use Compilation. Arguments to commands may be
196 // owned by Alloc when expanded from response files.
197 llvm::BumpPtrAllocator Alloc;
198 const auto [Driver, Compilation] =
199 buildCompilation(CommandLine, Diags, ScanFS, Alloc);
200 if (!Compilation)
201 return std::nullopt;
202
203 const auto IsClangCmd = [](const driver::Command &Cmd) {
204 return StringRef(Cmd.getCreator().getName()) == "clang";
205 };
206
207 const auto &Jobs = Compilation->getJobs();
208 if (const auto It = llvm::find_if(Jobs, IsClangCmd); It != Jobs.end())
209 return buildCC1CommandLine(*It);
210 return std::nullopt;
211}
212
213static llvm::Error makeErrorFromDiagnosticsOS(
214 TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
215 return llvm::make_error<llvm::StringError>(
216 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
217}
218
220 DependencyScanningWorker &Worker, StringRef CWD,
221 ArrayRef<std::string> CommandLine, DiagnosticConsumer &DC) {
222 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
223 // The input command line is already a -cc1 invocation; initialize the
224 // compiler instance directly from it.
225 return Worker.initializeCompilerInstanceWithContext(CWD, CommandLine, DC);
226 }
227
228 // The input command line is either a driver-style command line, or
229 // ill-formed. In this case, we will first call the Driver to build a -cc1
230 // command line for this compilation or diagnose any ill-formed input.
231 auto [OverlayFS, ModifiedCommandLine] = initVFSForByNameScanning(
232 &Worker.getVFS(), CommandLine, CWD, "ScanningByName");
233 auto DiagEngineWithCmdAndOpts =
234 std::make_unique<DiagnosticsEngineWithDiagOpts>(ModifiedCommandLine,
235 OverlayFS, DC);
236
237 const auto MaybeFirstCC1 = getFirstCC1CommandLine(
238 ModifiedCommandLine, *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS);
239 if (!MaybeFirstCC1)
240 return false;
241
242 return Worker.initializeCompilerInstanceWithContext(
243 CWD, *MaybeFirstCC1, std::move(DiagEngineWithCmdAndOpts), OverlayFS);
244}
245
246llvm::Error
248 StringRef CWD, ArrayRef<std::string> CommandLine) {
249 DiagPrinterWithOS =
250 std::make_unique<TextDiagnosticsPrinterWithOutput>(CommandLine);
251
253 Worker, CWD, CommandLine, DiagPrinterWithOS->DiagPrinter);
254
255 if (Result)
256 return llvm::Error::success();
257 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
258}
259
262 StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen,
263 LookupModuleOutputCallback LookupModuleOutput) {
264 FullDependencyConsumer Consumer(AlreadySeen);
265 CallbackActionController Controller(LookupModuleOutput);
266 if (Worker.computeDependenciesByNameWithContext(ModuleName, Consumer,
267 Controller))
268 return Consumer.takeTranslationUnitDeps();
269 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
270}
271
272llvm::Error
274 if (Worker.finalizeCompilerInstanceWithContext())
275 return llvm::Error::success();
276 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
277}
static std::optional< std::vector< std::string > > getFirstCC1CommandLine(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags, llvm::IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem > ScanFS)
static llvm::Error makeErrorFromDiagnosticsOS(TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS)
static std::vector< std::string > buildCC1CommandLine(const driver::Command &Cmd)
Constructs the full -cc1 command line, including executable, for the given driver Cmd.
DependencyOutputOptions - Options for controlling the compiler dependency file generation.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:232
A simple dependency action controller that uses a callback.
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
The dependency scanning service contains shared configuration and state that is used by the individua...
An individual dependency scanning worker that is able to run on its own thread.
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
static bool initializeWorkerCIWithContextFromCommandline(clang::dependencies::DependencyScanningWorker &Worker, StringRef CWD, ArrayRef< std::string > CommandLine, DiagnosticConsumer &DC)
Initialize the worker's compiler instance from the commandline.
DependencyScanningTool(dependencies::DependencyScanningService &Service, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=llvm::vfs::createPhysicalFileSystem())
Construct a dependency scanning tool.
llvm::Error initializeCompilerInstanceWithContextOrError(StringRef CWD, ArrayRef< std::string > CommandLine)
The following three methods provide a new interface to perform by name dependency scan.
std::optional< dependencies::TranslationUnitDeps > getTranslationUnitDependencies(ArrayRef< std::string > CommandLine, StringRef CWD, DiagnosticConsumer &DiagConsumer, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput, std::optional< llvm::MemoryBufferRef > TUBuffer=std::nullopt)
Given a Clang driver command-line for a translation unit, gather the modular dependencies and return ...
llvm::Expected< dependencies::TranslationUnitDeps > getModuleDependencies(StringRef ModuleName, ArrayRef< std::string > CommandLine, StringRef CWD, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput)
Given a compilation context specified via the Clang driver command-line, gather modular dependencies ...
llvm::Error finalizeCompilerInstanceWithContextOrError()
This method finializes the compiler instance.
llvm::Expected< dependencies::TranslationUnitDeps > computeDependenciesByNameWithContextOrError(StringRef ModuleName, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput)
Computes the dependeny for the module named ModuleName.
std::optional< P1689Rule > getP1689ModuleDependencyFile(const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, std::string &MakeformatOutputPath, DiagnosticConsumer &DiagConsumer)
Collect the module dependency in P1689 format for C++20 named modules.
std::optional< std::string > getDependencyFile(ArrayRef< std::string > CommandLine, StringRef CWD, DiagnosticConsumer &DiagConsumer)
Print out the dependency information into a string using the dependency file format that is specified...
llvm::function_ref< std::string(const ModuleDeps &, ModuleOutputKind)> LookupModuleOutputCallback
A callback to lookup module outputs for "-fmodule-file=", "-o" etc.
ModuleOutputKind
An output from a module compilation, such as the path of the module file.
std::pair< IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem >, std::vector< std::string > > initVFSForByNameScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, StringRef ModuleName)
std::pair< std::unique_ptr< driver::Driver >, std::unique_ptr< driver::Compilation > > buildCompilation(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::BumpPtrAllocator &Alloc)
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
std::shared_ptr< MatchComputation< T > > Generator
Definition RewriteRule.h:65
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
A command-line tool invocation that is part of building a TU.
Specifies the working directory and command of a compilation.