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