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