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