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