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