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
79 StringRef CWD) {
80 MakeDependencyPrinterConsumer Consumer;
81 CallbackActionController Controller(nullptr);
82 auto Result =
83 Worker.computeDependencies(CWD, CommandLine, Consumer, Controller);
84 if (Result)
85 return std::move(Result);
86 std::string Output;
87 Consumer.printDependencies(Output);
88 return Output;
89}
90
92 const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
93 std::string &MakeformatOutputPath) {
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 auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer,
136 Controller);
137 if (Result)
138 return std::move(Result);
139
140 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
141 if (!MakeformatOutputPath.empty())
142 Consumer.printDependencies(MakeformatOutput);
143 return Rule;
144}
145
148 ArrayRef<std::string> CommandLine, StringRef CWD,
149 const llvm::DenseSet<ModuleID> &AlreadySeen,
150 LookupModuleOutputCallback LookupModuleOutput,
151 std::optional<llvm::MemoryBufferRef> TUBuffer) {
152 FullDependencyConsumer Consumer(AlreadySeen);
153 CallbackActionController Controller(LookupModuleOutput);
154 llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer,
155 Controller, TUBuffer);
156
157 if (Result)
158 return std::move(Result);
159 return Consumer.takeTranslationUnitDeps();
160}
161
164 StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
165 const llvm::DenseSet<ModuleID> &AlreadySeen,
166 LookupModuleOutputCallback LookupModuleOutput) {
167 if (auto Error =
169 return Error;
170
172 ModuleName, AlreadySeen, LookupModuleOutput);
173
175 return Error;
176
177 return Result;
178}
179
180/// Constructs the full -cc1 command line, including executable, for the given
181/// driver \c Cmd.
182static std::vector<std::string>
184 const auto &Args = Cmd.getArguments();
185 std::vector<std::string> Out;
186 Out.reserve(Args.size() + 1);
187 Out.emplace_back(Cmd.getExecutable());
188 llvm::append_range(Out, Args);
189 return Out;
190}
191
192static std::optional<std::vector<std::string>> getFirstCC1CommandLine(
193 ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags,
195 // Compilation holds a non-owning a reference to the Driver, hence we need to
196 // keep the Driver alive when we use Compilation. Arguments to commands may be
197 // owned by Alloc when expanded from response files.
198 llvm::BumpPtrAllocator Alloc;
199 const auto [Driver, Compilation] =
200 buildCompilation(CommandLine, Diags, ScanFS, Alloc);
201 if (!Compilation)
202 return std::nullopt;
203
204 const auto IsClangCmd = [](const driver::Command &Cmd) {
205 return StringRef(Cmd.getCreator().getName()) == "clang";
206 };
207
208 const auto &Jobs = Compilation->getJobs();
209 if (const auto It = llvm::find_if(Jobs, IsClangCmd); It != Jobs.end())
210 return buildCC1CommandLine(*It);
211 return std::nullopt;
212}
213
214static llvm::Error makeErrorFromDiagnosticsOS(
215 TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
216 return llvm::make_error<llvm::StringError>(
217 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
218}
219
220llvm::Error
222 StringRef CWD, ArrayRef<std::string> CommandLine) {
223 DiagPrinterWithOS =
224 std::make_unique<TextDiagnosticsPrinterWithOutput>(CommandLine);
225
226 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
227 // The input command line is already a -cc1 invocation; initialize the
228 // compiler instance directly from it.
229 if (Worker.initializeCompilerInstanceWithContext(
230 CWD, CommandLine, DiagPrinterWithOS->DiagPrinter))
231 return llvm::Error::success();
232 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
233 }
234
235 // The input command line is either a driver-style command line, or
236 // ill-formed. In this case, we will first call the Driver to build a -cc1
237 // command line for this compilation or diagnose any ill-formed input.
238 auto OverlayFSAndArgs = initVFSForByNameScanning(
239 &Worker.getVFS(), CommandLine, CWD, "ScanningByName");
240 auto &OverlayFS = OverlayFSAndArgs.first;
241 const auto &ModifiedCommandLine = OverlayFSAndArgs.second;
242
243 auto DiagEngineWithCmdAndOpts =
244 std::make_unique<DiagnosticsEngineWithDiagOpts>(
245 ModifiedCommandLine, OverlayFS, DiagPrinterWithOS->DiagPrinter);
246
247 const auto MaybeFirstCC1 = getFirstCC1CommandLine(
248 ModifiedCommandLine, *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS);
249 if (!MaybeFirstCC1)
250 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
251
252 if (Worker.initializeCompilerInstanceWithContext(
253 CWD, *MaybeFirstCC1, std::move(DiagEngineWithCmdAndOpts), OverlayFS))
254 return llvm::Error::success();
255 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
256}
257
260 StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen,
261 LookupModuleOutputCallback LookupModuleOutput) {
262 FullDependencyConsumer Consumer(AlreadySeen);
263 CallbackActionController Controller(LookupModuleOutput);
264 if (Worker.computeDependenciesByNameWithContext(ModuleName, Consumer,
265 Controller))
266 return Consumer.takeTranslationUnitDeps();
267 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
268}
269
270llvm::Error
272 if (Worker.finalizeCompilerInstanceWithContext())
273 return llvm::Error::success();
274 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
275}
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.
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...
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
DependencyScanningTool(dependencies::DependencyScanningService &Service, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=llvm::vfs::createPhysicalFileSystem())
Construct a dependency scanning tool.
llvm::Expected< dependencies::TranslationUnitDeps > getTranslationUnitDependencies(ArrayRef< std::string > CommandLine, StringRef CWD, 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::Error initializeCompilerInstanceWithContextOrError(StringRef CWD, ArrayRef< std::string > CommandLine)
The following three methods provide a new interface to perform by name dependency scan.
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.
llvm::Expected< P1689Rule > getP1689ModuleDependencyFile(const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, std::string &MakeformatOutputPath)
Collect the module dependency in P1689 format for C++20 named modules.
llvm::Expected< std::string > getDependencyFile(ArrayRef< std::string > CommandLine, StringRef CWD)
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.