xref: /llvm-project/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h (revision 24a92f509a4e9ebaf5ae431409520d30055ea6fc)
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 
23 namespace clang {
24 namespace tooling {
25 namespace dependencies {
26 
27 /// A callback to lookup module outputs for "-fmodule-file=", "-o" etc.
28 using LookupModuleOutputCallback =
29     std::function<std::string(const ModuleID &, ModuleOutputKind)>;
30 
31 /// Graph of modular dependencies.
32 using ModuleDepsGraph = std::vector<ModuleDeps>;
33 
34 /// The full dependencies and module graph for a specific input.
35 struct 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 
71 struct 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.
79 class DependencyScanningTool {
80 public:
81   /// Construct a dependency scanning tool.
82   DependencyScanningTool(DependencyScanningService &Service,
83                          llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
84                              llvm::vfs::createPhysicalFileSystem());
85 
86   /// Print out the dependency information into a string using the dependency
87   /// file format that is specified in the options (-MD is the default) and
88   /// return it.
89   ///
90   /// \returns A \c StringError with the diagnostic output if clang errors
91   /// occurred, dependency file contents otherwise.
92   llvm::Expected<std::string>
93   getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD);
94 
95   /// Collect the module dependency in P1689 format for C++20 named modules.
96   ///
97   /// \param MakeformatOutput The output parameter for dependency information
98   /// in make format if the command line requires to generate make-format
99   /// dependency information by `-MD -MF <dep_file>`.
100   ///
101   /// \param MakeformatOutputPath The output parameter for the path to
102   /// \param MakeformatOutput.
103   ///
104   /// \returns A \c StringError with the diagnostic output if clang errors
105   /// occurred, P1689 dependency format rules otherwise.
106   llvm::Expected<P1689Rule>
107   getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
108                                StringRef CWD, std::string &MakeformatOutput,
109                                std::string &MakeformatOutputPath);
110   llvm::Expected<P1689Rule>
111   getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
112                                StringRef CWD) {
113     std::string MakeformatOutput;
114     std::string MakeformatOutputPath;
115 
116     return getP1689ModuleDependencyFile(Command, CWD, MakeformatOutput,
117                                         MakeformatOutputPath);
118   }
119 
120   /// Given a Clang driver command-line for a translation unit, gather the
121   /// modular dependencies and return the information needed for explicit build.
122   ///
123   /// \param AlreadySeen This stores modules which have previously been
124   ///                    reported. Use the same instance for all calls to this
125   ///                    function for a single \c DependencyScanningTool in a
126   ///                    single build. Use a different one for different tools,
127   ///                    and clear it between builds.
128   /// \param LookupModuleOutput This function is called to fill in
129   ///                           "-fmodule-file=", "-o" and other output
130   ///                           arguments for dependencies.
131   ///
132   /// \returns a \c StringError with the diagnostic output if clang errors
133   /// occurred, \c TranslationUnitDeps otherwise.
134   llvm::Expected<TranslationUnitDeps>
135   getTranslationUnitDependencies(const std::vector<std::string> &CommandLine,
136                                  StringRef CWD,
137                                  const llvm::DenseSet<ModuleID> &AlreadySeen,
138                                  LookupModuleOutputCallback LookupModuleOutput);
139 
140   /// Given a compilation context specified via the Clang driver command-line,
141   /// gather modular dependencies of module with the given name, and return the
142   /// information needed for explicit build.
143   llvm::Expected<ModuleDepsGraph> getModuleDependencies(
144       StringRef ModuleName, const std::vector<std::string> &CommandLine,
145       StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
146       LookupModuleOutputCallback LookupModuleOutput);
147 
148   llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); }
149 
150 private:
151   DependencyScanningWorker Worker;
152 };
153 
154 class FullDependencyConsumer : public DependencyConsumer {
155 public:
156   FullDependencyConsumer(const llvm::DenseSet<ModuleID> &AlreadySeen)
157       : AlreadySeen(AlreadySeen) {}
158 
159   void handleBuildCommand(Command Cmd) override {
160     Commands.push_back(std::move(Cmd));
161   }
162 
163   void handleDependencyOutputOpts(const DependencyOutputOptions &) override {}
164 
165   void handleFileDependency(StringRef File) override {
166     Dependencies.push_back(std::string(File));
167   }
168 
169   void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
170     PrebuiltModuleDeps.emplace_back(std::move(PMD));
171   }
172 
173   void handleModuleDependency(ModuleDeps MD) override {
174     ClangModuleDeps[MD.ID] = std::move(MD);
175   }
176 
177   void handleDirectModuleDependency(ModuleID ID) override {
178     DirectModuleDeps.push_back(ID);
179   }
180 
181   void handleContextHash(std::string Hash) override {
182     ContextHash = std::move(Hash);
183   }
184 
185   TranslationUnitDeps takeTranslationUnitDeps();
186   ModuleDepsGraph takeModuleGraphDeps();
187 
188 private:
189   std::vector<std::string> Dependencies;
190   std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
191   llvm::MapVector<ModuleID, ModuleDeps> ClangModuleDeps;
192   std::vector<ModuleID> DirectModuleDeps;
193   std::vector<Command> Commands;
194   std::string ContextHash;
195   std::vector<std::string> OutputPaths;
196   const llvm::DenseSet<ModuleID> &AlreadySeen;
197 };
198 
199 /// A simple dependency action controller that uses a callback. If no callback
200 /// is provided, it is assumed that looking up module outputs is unreachable.
201 class CallbackActionController : public DependencyActionController {
202 public:
203   virtual ~CallbackActionController();
204 
205   CallbackActionController(LookupModuleOutputCallback LMO)
206       : LookupModuleOutput(std::move(LMO)) {
207     if (!LookupModuleOutput) {
208       LookupModuleOutput = [](const ModuleID &,
209                               ModuleOutputKind) -> std::string {
210         llvm::report_fatal_error("unexpected call to lookupModuleOutput");
211       };
212     }
213   }
214 
215   std::string lookupModuleOutput(const ModuleID &ID,
216                                  ModuleOutputKind Kind) override {
217     return LookupModuleOutput(ID, Kind);
218   }
219 
220 private:
221   LookupModuleOutputCallback LookupModuleOutput;
222 };
223 
224 } // end namespace dependencies
225 } // end namespace tooling
226 } // end namespace clang
227 
228 #endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H
229