1480093f4SDimitry Andric //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric 9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 10a7dea167SDimitry Andric #include "clang/Frontend/Utils.h" 11a7dea167SDimitry Andric 12a7dea167SDimitry Andric namespace clang{ 13a7dea167SDimitry Andric namespace tooling{ 14a7dea167SDimitry Andric namespace dependencies{ 15a7dea167SDimitry Andric 16*5ffd83dbSDimitry Andric std::vector<std::string> FullDependencies::getAdditionalCommandLine( 17*5ffd83dbSDimitry Andric std::function<StringRef(ClangModuleDep)> LookupPCMPath, 18*5ffd83dbSDimitry Andric std::function<const ModuleDeps &(ClangModuleDep)> LookupModuleDeps) const { 19*5ffd83dbSDimitry Andric std::vector<std::string> Ret = AdditionalNonPathCommandLine; 20*5ffd83dbSDimitry Andric 21*5ffd83dbSDimitry Andric dependencies::detail::appendCommonModuleArguments( 22*5ffd83dbSDimitry Andric ClangModuleDeps, LookupPCMPath, LookupModuleDeps, Ret); 23*5ffd83dbSDimitry Andric 24*5ffd83dbSDimitry Andric return Ret; 25*5ffd83dbSDimitry Andric } 26*5ffd83dbSDimitry Andric 27480093f4SDimitry Andric DependencyScanningTool::DependencyScanningTool( 28480093f4SDimitry Andric DependencyScanningService &Service) 29*5ffd83dbSDimitry Andric : Worker(Service) {} 30a7dea167SDimitry Andric 31480093f4SDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 32480093f4SDimitry Andric const tooling::CompilationDatabase &Compilations, StringRef CWD) { 33a7dea167SDimitry Andric /// Prints out all of the gathered dependencies into a string. 34480093f4SDimitry Andric class MakeDependencyPrinterConsumer : public DependencyConsumer { 35a7dea167SDimitry Andric public: 36a7dea167SDimitry Andric void handleFileDependency(const DependencyOutputOptions &Opts, 37a7dea167SDimitry Andric StringRef File) override { 38a7dea167SDimitry Andric if (!this->Opts) 39a7dea167SDimitry Andric this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 40*5ffd83dbSDimitry Andric Dependencies.push_back(std::string(File)); 41a7dea167SDimitry Andric } 42a7dea167SDimitry Andric 43480093f4SDimitry Andric void handleModuleDependency(ModuleDeps MD) override { 44480093f4SDimitry Andric // These are ignored for the make format as it can't support the full 45480093f4SDimitry Andric // set of deps, and handleFileDependency handles enough for implicitly 46480093f4SDimitry Andric // built modules to work. 47480093f4SDimitry Andric } 48480093f4SDimitry Andric 49480093f4SDimitry Andric void handleContextHash(std::string Hash) override {} 50480093f4SDimitry Andric 51a7dea167SDimitry Andric void printDependencies(std::string &S) { 52a7dea167SDimitry Andric if (!Opts) 53a7dea167SDimitry Andric return; 54a7dea167SDimitry Andric 55a7dea167SDimitry Andric class DependencyPrinter : public DependencyFileGenerator { 56a7dea167SDimitry Andric public: 57a7dea167SDimitry Andric DependencyPrinter(DependencyOutputOptions &Opts, 58a7dea167SDimitry Andric ArrayRef<std::string> Dependencies) 59a7dea167SDimitry Andric : DependencyFileGenerator(Opts) { 60a7dea167SDimitry Andric for (const auto &Dep : Dependencies) 61a7dea167SDimitry Andric addDependency(Dep); 62a7dea167SDimitry Andric } 63a7dea167SDimitry Andric 64a7dea167SDimitry Andric void printDependencies(std::string &S) { 65a7dea167SDimitry Andric llvm::raw_string_ostream OS(S); 66a7dea167SDimitry Andric outputDependencyFile(OS); 67a7dea167SDimitry Andric } 68a7dea167SDimitry Andric }; 69a7dea167SDimitry Andric 70a7dea167SDimitry Andric DependencyPrinter Generator(*Opts, Dependencies); 71a7dea167SDimitry Andric Generator.printDependencies(S); 72a7dea167SDimitry Andric } 73a7dea167SDimitry Andric 74a7dea167SDimitry Andric private: 75a7dea167SDimitry Andric std::unique_ptr<DependencyOutputOptions> Opts; 76a7dea167SDimitry Andric std::vector<std::string> Dependencies; 77a7dea167SDimitry Andric }; 78a7dea167SDimitry Andric 79*5ffd83dbSDimitry Andric // We expect a single command here because if a source file occurs multiple 80*5ffd83dbSDimitry Andric // times in the original CDB, then `computeDependencies` would run the 81*5ffd83dbSDimitry Andric // `DependencyScanningAction` once for every time the input occured in the 82*5ffd83dbSDimitry Andric // CDB. Instead we split up the CDB into single command chunks to avoid this 83*5ffd83dbSDimitry Andric // behavior. 84*5ffd83dbSDimitry Andric assert(Compilations.getAllCompileCommands().size() == 1 && 85*5ffd83dbSDimitry Andric "Expected a compilation database with a single command!"); 86*5ffd83dbSDimitry Andric std::string Input = Compilations.getAllCompileCommands().front().Filename; 87*5ffd83dbSDimitry Andric 88*5ffd83dbSDimitry Andric MakeDependencyPrinterConsumer Consumer; 89*5ffd83dbSDimitry Andric auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); 90*5ffd83dbSDimitry Andric if (Result) 91*5ffd83dbSDimitry Andric return std::move(Result); 92*5ffd83dbSDimitry Andric std::string Output; 93*5ffd83dbSDimitry Andric Consumer.printDependencies(Output); 94*5ffd83dbSDimitry Andric return Output; 95*5ffd83dbSDimitry Andric } 96*5ffd83dbSDimitry Andric 97*5ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult> 98*5ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies( 99*5ffd83dbSDimitry Andric const tooling::CompilationDatabase &Compilations, StringRef CWD, 100*5ffd83dbSDimitry Andric const llvm::StringSet<> &AlreadySeen) { 101480093f4SDimitry Andric class FullDependencyPrinterConsumer : public DependencyConsumer { 102480093f4SDimitry Andric public: 103*5ffd83dbSDimitry Andric FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) 104*5ffd83dbSDimitry Andric : AlreadySeen(AlreadySeen) {} 105*5ffd83dbSDimitry Andric 106480093f4SDimitry Andric void handleFileDependency(const DependencyOutputOptions &Opts, 107480093f4SDimitry Andric StringRef File) override { 108*5ffd83dbSDimitry Andric Dependencies.push_back(std::string(File)); 109480093f4SDimitry Andric } 110480093f4SDimitry Andric 111480093f4SDimitry Andric void handleModuleDependency(ModuleDeps MD) override { 112480093f4SDimitry Andric ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD); 113480093f4SDimitry Andric } 114480093f4SDimitry Andric 115480093f4SDimitry Andric void handleContextHash(std::string Hash) override { 116480093f4SDimitry Andric ContextHash = std::move(Hash); 117480093f4SDimitry Andric } 118480093f4SDimitry Andric 119*5ffd83dbSDimitry Andric FullDependenciesResult getFullDependencies() const { 120*5ffd83dbSDimitry Andric FullDependencies FD; 121480093f4SDimitry Andric 122*5ffd83dbSDimitry Andric FD.ContextHash = std::move(ContextHash); 123480093f4SDimitry Andric 124*5ffd83dbSDimitry Andric FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 125480093f4SDimitry Andric 126*5ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 127*5ffd83dbSDimitry Andric auto &MD = M.second; 128480093f4SDimitry Andric if (MD.ImportedByMainFile) 129*5ffd83dbSDimitry Andric FD.ClangModuleDeps.push_back({MD.ModuleName, ContextHash}); 130480093f4SDimitry Andric } 131480093f4SDimitry Andric 132*5ffd83dbSDimitry Andric FullDependenciesResult FDR; 133*5ffd83dbSDimitry Andric 134*5ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 135*5ffd83dbSDimitry Andric // TODO: Avoid handleModuleDependency even being called for modules 136*5ffd83dbSDimitry Andric // we've already seen. 137*5ffd83dbSDimitry Andric if (AlreadySeen.count(M.first)) 138*5ffd83dbSDimitry Andric continue; 139*5ffd83dbSDimitry Andric FDR.DiscoveredModules.push_back(std::move(M.second)); 140480093f4SDimitry Andric } 141480093f4SDimitry Andric 142*5ffd83dbSDimitry Andric FDR.FullDeps = std::move(FD); 143*5ffd83dbSDimitry Andric return FDR; 144480093f4SDimitry Andric } 145480093f4SDimitry Andric 146480093f4SDimitry Andric private: 147480093f4SDimitry Andric std::vector<std::string> Dependencies; 148480093f4SDimitry Andric std::unordered_map<std::string, ModuleDeps> ClangModuleDeps; 149480093f4SDimitry Andric std::string ContextHash; 150*5ffd83dbSDimitry Andric std::vector<std::string> OutputPaths; 151*5ffd83dbSDimitry Andric const llvm::StringSet<> &AlreadySeen; 152480093f4SDimitry Andric }; 153480093f4SDimitry Andric 154480093f4SDimitry Andric // We expect a single command here because if a source file occurs multiple 155480093f4SDimitry Andric // times in the original CDB, then `computeDependencies` would run the 156480093f4SDimitry Andric // `DependencyScanningAction` once for every time the input occured in the 157480093f4SDimitry Andric // CDB. Instead we split up the CDB into single command chunks to avoid this 158480093f4SDimitry Andric // behavior. 159480093f4SDimitry Andric assert(Compilations.getAllCompileCommands().size() == 1 && 160480093f4SDimitry Andric "Expected a compilation database with a single command!"); 161480093f4SDimitry Andric std::string Input = Compilations.getAllCompileCommands().front().Filename; 162480093f4SDimitry Andric 163*5ffd83dbSDimitry Andric FullDependencyPrinterConsumer Consumer(AlreadySeen); 164*5ffd83dbSDimitry Andric llvm::Error Result = 165a7dea167SDimitry Andric Worker.computeDependencies(Input, CWD, Compilations, Consumer); 166a7dea167SDimitry Andric if (Result) 167a7dea167SDimitry Andric return std::move(Result); 168*5ffd83dbSDimitry Andric return Consumer.getFullDependencies(); 169a7dea167SDimitry Andric } 170a7dea167SDimitry Andric 171a7dea167SDimitry Andric } // end namespace dependencies 172a7dea167SDimitry Andric } // end namespace tooling 173a7dea167SDimitry Andric } // end namespace clang 174