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