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