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