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