1 //===- DependencyScanningTool.cpp - 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 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 10 #include "clang/Frontend/Utils.h" 11 12 namespace clang{ 13 namespace tooling{ 14 namespace dependencies{ 15 16 std::vector<std::string> FullDependencies::getAdditionalArgs( 17 std::function<StringRef(ModuleID)> LookupPCMPath, 18 std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const { 19 std::vector<std::string> Ret = getAdditionalArgsWithoutModulePaths(); 20 21 std::vector<std::string> PCMPaths; 22 std::vector<std::string> ModMapPaths; 23 dependencies::detail::collectPCMAndModuleMapPaths( 24 ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths); 25 for (const std::string &PCMPath : PCMPaths) 26 Ret.push_back("-fmodule-file=" + PCMPath); 27 for (const std::string &ModMapPath : ModMapPaths) 28 Ret.push_back("-fmodule-map-file=" + ModMapPath); 29 30 return Ret; 31 } 32 33 std::vector<std::string> 34 FullDependencies::getAdditionalArgsWithoutModulePaths() const { 35 std::vector<std::string> Args{ 36 "-fno-implicit-modules", 37 "-fno-implicit-module-maps", 38 }; 39 40 for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) { 41 Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile); 42 Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile); 43 } 44 45 return Args; 46 } 47 48 DependencyScanningTool::DependencyScanningTool( 49 DependencyScanningService &Service) 50 : Worker(Service) {} 51 52 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 53 const tooling::CompilationDatabase &Compilations, StringRef CWD) { 54 /// Prints out all of the gathered dependencies into a string. 55 class MakeDependencyPrinterConsumer : public DependencyConsumer { 56 public: 57 void 58 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 59 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 60 } 61 62 void handleFileDependency(StringRef File) override { 63 Dependencies.push_back(std::string(File)); 64 } 65 66 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 67 // Same as `handleModuleDependency`. 68 } 69 70 void handleModuleDependency(ModuleDeps MD) override { 71 // These are ignored for the make format as it can't support the full 72 // set of deps, and handleFileDependency handles enough for implicitly 73 // built modules to work. 74 } 75 76 void handleContextHash(std::string Hash) override {} 77 78 void printDependencies(std::string &S) { 79 assert(Opts && "Handled dependency output options."); 80 81 class DependencyPrinter : public DependencyFileGenerator { 82 public: 83 DependencyPrinter(DependencyOutputOptions &Opts, 84 ArrayRef<std::string> Dependencies) 85 : DependencyFileGenerator(Opts) { 86 for (const auto &Dep : Dependencies) 87 addDependency(Dep); 88 } 89 90 void printDependencies(std::string &S) { 91 llvm::raw_string_ostream OS(S); 92 outputDependencyFile(OS); 93 } 94 }; 95 96 DependencyPrinter Generator(*Opts, Dependencies); 97 Generator.printDependencies(S); 98 } 99 100 private: 101 std::unique_ptr<DependencyOutputOptions> Opts; 102 std::vector<std::string> Dependencies; 103 }; 104 105 // We expect a single command here because if a source file occurs multiple 106 // times in the original CDB, then `computeDependencies` would run the 107 // `DependencyScanningAction` once for every time the input occured in the 108 // CDB. Instead we split up the CDB into single command chunks to avoid this 109 // behavior. 110 assert(Compilations.getAllCompileCommands().size() == 1 && 111 "Expected a compilation database with a single command!"); 112 std::string Input = Compilations.getAllCompileCommands().front().Filename; 113 114 MakeDependencyPrinterConsumer Consumer; 115 auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); 116 if (Result) 117 return std::move(Result); 118 std::string Output; 119 Consumer.printDependencies(Output); 120 return Output; 121 } 122 123 llvm::Expected<FullDependenciesResult> 124 DependencyScanningTool::getFullDependencies( 125 const tooling::CompilationDatabase &Compilations, StringRef CWD, 126 const llvm::StringSet<> &AlreadySeen) { 127 class FullDependencyPrinterConsumer : public DependencyConsumer { 128 public: 129 FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) 130 : AlreadySeen(AlreadySeen) {} 131 132 void 133 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {} 134 135 void handleFileDependency(StringRef File) override { 136 Dependencies.push_back(std::string(File)); 137 } 138 139 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 140 PrebuiltModuleDeps.emplace_back(std::move(PMD)); 141 } 142 143 void handleModuleDependency(ModuleDeps MD) override { 144 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD); 145 } 146 147 void handleContextHash(std::string Hash) override { 148 ContextHash = std::move(Hash); 149 } 150 151 FullDependenciesResult getFullDependencies() const { 152 FullDependencies FD; 153 154 FD.ID.ContextHash = std::move(ContextHash); 155 156 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 157 158 for (auto &&M : ClangModuleDeps) { 159 auto &MD = M.second; 160 if (MD.ImportedByMainFile) 161 FD.ClangModuleDeps.push_back(MD.ID); 162 } 163 164 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 165 166 FullDependenciesResult FDR; 167 168 for (auto &&M : ClangModuleDeps) { 169 // TODO: Avoid handleModuleDependency even being called for modules 170 // we've already seen. 171 if (AlreadySeen.count(M.first)) 172 continue; 173 FDR.DiscoveredModules.push_back(std::move(M.second)); 174 } 175 176 FDR.FullDeps = std::move(FD); 177 return FDR; 178 } 179 180 private: 181 std::vector<std::string> Dependencies; 182 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; 183 std::unordered_map<std::string, ModuleDeps> ClangModuleDeps; 184 std::string ContextHash; 185 std::vector<std::string> OutputPaths; 186 const llvm::StringSet<> &AlreadySeen; 187 }; 188 189 // We expect a single command here because if a source file occurs multiple 190 // times in the original CDB, then `computeDependencies` would run the 191 // `DependencyScanningAction` once for every time the input occured in the 192 // CDB. Instead we split up the CDB into single command chunks to avoid this 193 // behavior. 194 assert(Compilations.getAllCompileCommands().size() == 1 && 195 "Expected a compilation database with a single command!"); 196 std::string Input = Compilations.getAllCompileCommands().front().Filename; 197 198 FullDependencyPrinterConsumer Consumer(AlreadySeen); 199 llvm::Error Result = 200 Worker.computeDependencies(Input, CWD, Compilations, Consumer); 201 if (Result) 202 return std::move(Result); 203 return Consumer.getFullDependencies(); 204 } 205 206 } // end namespace dependencies 207 } // end namespace tooling 208 } // end namespace clang 209