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 #include "llvm/Support/JSON.h" 12 13 static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) { 14 std::vector<llvm::StringRef> Strings; 15 for (auto &&I : Set) 16 Strings.push_back(I.getKey()); 17 std::sort(Strings.begin(), Strings.end()); 18 return llvm::json::Array(Strings); 19 } 20 21 namespace clang{ 22 namespace tooling{ 23 namespace dependencies{ 24 25 DependencyScanningTool::DependencyScanningTool( 26 DependencyScanningService &Service) 27 : Format(Service.getFormat()), Worker(Service) { 28 } 29 30 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 31 const tooling::CompilationDatabase &Compilations, StringRef CWD) { 32 /// Prints out all of the gathered dependencies into a string. 33 class MakeDependencyPrinterConsumer : public DependencyConsumer { 34 public: 35 void handleFileDependency(const DependencyOutputOptions &Opts, 36 StringRef File) override { 37 if (!this->Opts) 38 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 39 Dependencies.push_back(File); 40 } 41 42 void handleModuleDependency(ModuleDeps MD) override { 43 // These are ignored for the make format as it can't support the full 44 // set of deps, and handleFileDependency handles enough for implicitly 45 // built modules to work. 46 } 47 48 void handleContextHash(std::string Hash) override {} 49 50 void printDependencies(std::string &S) { 51 if (!Opts) 52 return; 53 54 class DependencyPrinter : public DependencyFileGenerator { 55 public: 56 DependencyPrinter(DependencyOutputOptions &Opts, 57 ArrayRef<std::string> Dependencies) 58 : DependencyFileGenerator(Opts) { 59 for (const auto &Dep : Dependencies) 60 addDependency(Dep); 61 } 62 63 void printDependencies(std::string &S) { 64 llvm::raw_string_ostream OS(S); 65 outputDependencyFile(OS); 66 } 67 }; 68 69 DependencyPrinter Generator(*Opts, Dependencies); 70 Generator.printDependencies(S); 71 } 72 73 private: 74 std::unique_ptr<DependencyOutputOptions> Opts; 75 std::vector<std::string> Dependencies; 76 }; 77 78 class FullDependencyPrinterConsumer : public DependencyConsumer { 79 public: 80 void handleFileDependency(const DependencyOutputOptions &Opts, 81 StringRef File) override { 82 Dependencies.push_back(File); 83 } 84 85 void handleModuleDependency(ModuleDeps MD) override { 86 ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD); 87 } 88 89 void handleContextHash(std::string Hash) override { 90 ContextHash = std::move(Hash); 91 } 92 93 void printDependencies(std::string &S, StringRef MainFile) { 94 // Sort the modules by name to get a deterministic order. 95 std::vector<StringRef> Modules; 96 for (auto &&Dep : ClangModuleDeps) 97 Modules.push_back(Dep.first); 98 std::sort(Modules.begin(), Modules.end()); 99 100 llvm::raw_string_ostream OS(S); 101 102 using namespace llvm::json; 103 104 Array Imports; 105 for (auto &&ModName : Modules) { 106 auto &MD = ClangModuleDeps[ModName]; 107 if (MD.ImportedByMainFile) 108 Imports.push_back(MD.ModuleName); 109 } 110 111 Array Mods; 112 for (auto &&ModName : Modules) { 113 auto &MD = ClangModuleDeps[ModName]; 114 Object Mod{ 115 {"name", MD.ModuleName}, 116 {"file-deps", toJSONSorted(MD.FileDeps)}, 117 {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, 118 {"clang-modulemap-file", MD.ClangModuleMapFile}, 119 }; 120 Mods.push_back(std::move(Mod)); 121 } 122 123 Object O{ 124 {"input-file", MainFile}, 125 {"clang-context-hash", ContextHash}, 126 {"file-deps", Dependencies}, 127 {"clang-module-deps", std::move(Imports)}, 128 {"clang-modules", std::move(Mods)}, 129 }; 130 131 S = llvm::formatv("{0:2},\n", Value(std::move(O))).str(); 132 return; 133 } 134 135 private: 136 std::vector<std::string> Dependencies; 137 std::unordered_map<std::string, ModuleDeps> ClangModuleDeps; 138 std::string ContextHash; 139 }; 140 141 142 // We expect a single command here because if a source file occurs multiple 143 // times in the original CDB, then `computeDependencies` would run the 144 // `DependencyScanningAction` once for every time the input occured in the 145 // CDB. Instead we split up the CDB into single command chunks to avoid this 146 // behavior. 147 assert(Compilations.getAllCompileCommands().size() == 1 && 148 "Expected a compilation database with a single command!"); 149 std::string Input = Compilations.getAllCompileCommands().front().Filename; 150 151 if (Format == ScanningOutputFormat::Make) { 152 MakeDependencyPrinterConsumer Consumer; 153 auto Result = 154 Worker.computeDependencies(Input, CWD, Compilations, Consumer); 155 if (Result) 156 return std::move(Result); 157 std::string Output; 158 Consumer.printDependencies(Output); 159 return Output; 160 } else { 161 FullDependencyPrinterConsumer Consumer; 162 auto Result = 163 Worker.computeDependencies(Input, CWD, Compilations, Consumer); 164 if (Result) 165 return std::move(Result); 166 std::string Output; 167 Consumer.printDependencies(Output, Input); 168 return Output; 169 } 170 } 171 172 } // end namespace dependencies 173 } // end namespace tooling 174 } // end namespace clang 175