1//===- DependencyScanningTool.h - 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
9#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H
10#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H
11
12#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
13#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
14#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
15#include "clang/Tooling/JSONCompilationDatabase.h"
16#include "llvm/ADT/DenseSet.h"
17#include "llvm/ADT/MapVector.h"
18#include <functional>
19#include <optional>
20#include <string>
21#include <vector>
22
23namespace clang {
24namespace tooling {
25namespace dependencies {
26
27/// A callback to lookup module outputs for "-fmodule-file=", "-o" etc.
28using LookupModuleOutputCallback =
29 llvm::function_ref<std::string(const ModuleDeps &, ModuleOutputKind)>;
30
31/// Graph of modular dependencies.
32using ModuleDepsGraph = std::vector<ModuleDeps>;
33
34/// The full dependencies and module graph for a specific input.
35struct TranslationUnitDeps {
36 /// The graph of direct and transitive modular dependencies.
37 ModuleDepsGraph ModuleGraph;
38
39 /// The identifier of the C++20 module this translation unit exports.
40 ///
41 /// If the translation unit is not a module then \c ID.ModuleName is empty.
42 ModuleID ID;
43
44 /// A collection of absolute paths to files that this translation unit
45 /// directly depends on, not including transitive dependencies.
46 std::vector<std::string> FileDeps;
47
48 /// A collection of prebuilt modules this translation unit directly depends
49 /// on, not including transitive dependencies.
50 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
51
52 /// A list of modules this translation unit directly depends on, not including
53 /// transitive dependencies.
54 ///
55 /// This may include modules with a different context hash when it can be
56 /// determined that the differences are benign for this compilation.
57 std::vector<ModuleID> ClangModuleDeps;
58
59 /// The sequence of commands required to build the translation unit. Commands
60 /// should be executed in order.
61 ///
62 /// FIXME: If we add support for multi-arch builds in clang-scan-deps, we
63 /// should make the dependencies between commands explicit to enable parallel
64 /// builds of each architecture.
65 std::vector<Command> Commands;
66
67 /// Deprecated driver command-line. This will be removed in a future version.
68 std::vector<std::string> DriverCommandLine;
69};
70
71struct P1689Rule {
72 std::string PrimaryOutput;
73 std::optional<P1689ModuleInfo> Provides;
74 std::vector<P1689ModuleInfo> Requires;
75};
76
77/// The high-level implementation of the dependency discovery tool that runs on
78/// an individual worker thread.
79class DependencyScanningTool {
80public:
81 /// Construct a dependency scanning tool.
82 ///
83 /// @param Service The parent service. Must outlive the tool.
84 /// @param FS The filesystem for the tool to use. Defaults to the physical FS.
85 DependencyScanningTool(DependencyScanningService &Service,
86 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
87 llvm::vfs::createPhysicalFileSystem());
88
89 /// Print out the dependency information into a string using the dependency
90 /// file format that is specified in the options (-MD is the default) and
91 /// return it.
92 ///
93 /// \returns A \c StringError with the diagnostic output if clang errors
94 /// occurred, dependency file contents otherwise.
95 llvm::Expected<std::string>
96 getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD);
97
98 /// Collect the module dependency in P1689 format for C++20 named modules.
99 ///
100 /// \param MakeformatOutput The output parameter for dependency information
101 /// in make format if the command line requires to generate make-format
102 /// dependency information by `-MD -MF <dep_file>`.
103 ///
104 /// \param MakeformatOutputPath The output parameter for the path to
105 /// \param MakeformatOutput.
106 ///
107 /// \returns A \c StringError with the diagnostic output if clang errors
108 /// occurred, P1689 dependency format rules otherwise.
109 llvm::Expected<P1689Rule>
110 getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
111 StringRef CWD, std::string &MakeformatOutput,
112 std::string &MakeformatOutputPath);
113 llvm::Expected<P1689Rule>
114 getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
115 StringRef CWD) {
116 std::string MakeformatOutput;
117 std::string MakeformatOutputPath;
118
119 return getP1689ModuleDependencyFile(Command, CWD, MakeformatOutput,
120 MakeformatOutputPath);
121 }
122
123 /// Given a Clang driver command-line for a translation unit, gather the
124 /// modular dependencies and return the information needed for explicit build.
125 ///
126 /// \param AlreadySeen This stores modules which have previously been
127 /// reported. Use the same instance for all calls to this
128 /// function for a single \c DependencyScanningTool in a
129 /// single build. Use a different one for different tools,
130 /// and clear it between builds.
131 /// \param LookupModuleOutput This function is called to fill in
132 /// "-fmodule-file=", "-o" and other output
133 /// arguments for dependencies.
134 /// \param TUBuffer Optional memory buffer for translation unit input. If
135 /// TUBuffer is nullopt, the input should be included in the
136 /// Commandline already.
137 ///
138 /// \returns a \c StringError with the diagnostic output if clang errors
139 /// occurred, \c TranslationUnitDeps otherwise.
140 llvm::Expected<TranslationUnitDeps> getTranslationUnitDependencies(
141 const std::vector<std::string> &CommandLine, StringRef CWD,
142 const llvm::DenseSet<ModuleID> &AlreadySeen,
143 LookupModuleOutputCallback LookupModuleOutput,
144 std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
145
146 /// Given a compilation context specified via the Clang driver command-line,
147 /// gather modular dependencies of module with the given name, and return the
148 /// information needed for explicit build.
149 llvm::Expected<ModuleDepsGraph> getModuleDependencies(
150 StringRef ModuleName, const std::vector<std::string> &CommandLine,
151 StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
152 LookupModuleOutputCallback LookupModuleOutput);
153
154 llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); }
155
156private:
157 DependencyScanningWorker Worker;
158};
159
160class FullDependencyConsumer : public DependencyConsumer {
161public:
162 FullDependencyConsumer(const llvm::DenseSet<ModuleID> &AlreadySeen)
163 : AlreadySeen(AlreadySeen) {}
164
165 void handleBuildCommand(Command Cmd) override {
166 Commands.push_back(x: std::move(Cmd));
167 }
168
169 void handleDependencyOutputOpts(const DependencyOutputOptions &) override {}
170
171 void handleFileDependency(StringRef File) override {
172 Dependencies.push_back(x: std::string(File));
173 }
174
175 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
176 PrebuiltModuleDeps.emplace_back(args: std::move(PMD));
177 }
178
179 void handleModuleDependency(ModuleDeps MD) override {
180 ClangModuleDeps[MD.ID] = std::move(MD);
181 }
182
183 void handleDirectModuleDependency(ModuleID ID) override {
184 DirectModuleDeps.push_back(x: ID);
185 }
186
187 void handleContextHash(std::string Hash) override {
188 ContextHash = std::move(Hash);
189 }
190
191 TranslationUnitDeps takeTranslationUnitDeps();
192 ModuleDepsGraph takeModuleGraphDeps();
193
194private:
195 std::vector<std::string> Dependencies;
196 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
197 llvm::MapVector<ModuleID, ModuleDeps> ClangModuleDeps;
198 std::vector<ModuleID> DirectModuleDeps;
199 std::vector<Command> Commands;
200 std::string ContextHash;
201 std::vector<std::string> OutputPaths;
202 const llvm::DenseSet<ModuleID> &AlreadySeen;
203};
204
205/// A simple dependency action controller that uses a callback. If no callback
206/// is provided, it is assumed that looking up module outputs is unreachable.
207class CallbackActionController : public DependencyActionController {
208public:
209 virtual ~CallbackActionController();
210
211 static std::string lookupUnreachableModuleOutput(const ModuleDeps &MD,
212 ModuleOutputKind Kind) {
213 llvm::report_fatal_error(reason: "unexpected call to lookupModuleOutput");
214 };
215
216 CallbackActionController(LookupModuleOutputCallback LMO)
217 : LookupModuleOutput(std::move(LMO)) {
218 if (!LookupModuleOutput) {
219 LookupModuleOutput = lookupUnreachableModuleOutput;
220 }
221 }
222
223 std::string lookupModuleOutput(const ModuleDeps &MD,
224 ModuleOutputKind Kind) override {
225 return LookupModuleOutput(MD, Kind);
226 }
227
228private:
229 LookupModuleOutputCallback LookupModuleOutput;
230};
231
232} // end namespace dependencies
233} // end namespace tooling
234} // end namespace clang
235
236#endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H
237

source code of clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h