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