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 using namespace clang; 13 using namespace tooling; 14 using namespace dependencies; 15 16 std::vector<std::string> FullDependencies::getCommandLine( 17 llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)> 18 LookupModuleOutput) const { 19 std::vector<std::string> Ret = getCommandLineWithoutModulePaths(); 20 21 for (ModuleID MID : ClangModuleDeps) { 22 auto PCM = LookupModuleOutput(MID, ModuleOutputKind::ModuleFile); 23 Ret.push_back("-fmodule-file=" + PCM); 24 } 25 26 return Ret; 27 } 28 29 std::vector<std::string> 30 FullDependencies::getCommandLineWithoutModulePaths() const { 31 std::vector<std::string> Args = OriginalCommandLine; 32 33 Args.push_back("-fno-implicit-modules"); 34 Args.push_back("-fno-implicit-module-maps"); 35 for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) 36 Args.push_back("-fmodule-file=" + PMD.PCMFile); 37 38 // These arguments are unused in explicit compiles. 39 llvm::erase_if(Args, [](StringRef Arg) { 40 if (Arg.consume_front("-fmodules-")) { 41 return Arg.startswith("cache-path=") || 42 Arg.startswith("prune-interval=") || 43 Arg.startswith("prune-after=") || 44 Arg == "validate-once-per-build-session"; 45 } 46 return Arg.startswith("-fbuild-session-file="); 47 }); 48 49 return Args; 50 } 51 52 DependencyScanningTool::DependencyScanningTool( 53 DependencyScanningService &Service) 54 : Worker(Service) {} 55 56 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 57 const std::vector<std::string> &CommandLine, StringRef CWD, 58 llvm::Optional<StringRef> ModuleName) { 59 /// Prints out all of the gathered dependencies into a string. 60 class MakeDependencyPrinterConsumer : public DependencyConsumer { 61 public: 62 void 63 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 64 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 65 } 66 67 void handleFileDependency(StringRef File) override { 68 Dependencies.push_back(std::string(File)); 69 } 70 71 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 72 // Same as `handleModuleDependency`. 73 } 74 75 void handleModuleDependency(ModuleDeps MD) override { 76 // These are ignored for the make format as it can't support the full 77 // set of deps, and handleFileDependency handles enough for implicitly 78 // built modules to work. 79 } 80 81 void handleContextHash(std::string Hash) override {} 82 83 void printDependencies(std::string &S) { 84 assert(Opts && "Handled dependency output options."); 85 86 class DependencyPrinter : public DependencyFileGenerator { 87 public: 88 DependencyPrinter(DependencyOutputOptions &Opts, 89 ArrayRef<std::string> Dependencies) 90 : DependencyFileGenerator(Opts) { 91 for (const auto &Dep : Dependencies) 92 addDependency(Dep); 93 } 94 95 void printDependencies(std::string &S) { 96 llvm::raw_string_ostream OS(S); 97 outputDependencyFile(OS); 98 } 99 }; 100 101 DependencyPrinter Generator(*Opts, Dependencies); 102 Generator.printDependencies(S); 103 } 104 105 private: 106 std::unique_ptr<DependencyOutputOptions> Opts; 107 std::vector<std::string> Dependencies; 108 }; 109 110 MakeDependencyPrinterConsumer Consumer; 111 auto Result = 112 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 113 if (Result) 114 return std::move(Result); 115 std::string Output; 116 Consumer.printDependencies(Output); 117 return Output; 118 } 119 120 llvm::Expected<FullDependenciesResult> 121 DependencyScanningTool::getFullDependencies( 122 const std::vector<std::string> &CommandLine, StringRef CWD, 123 const llvm::StringSet<> &AlreadySeen, 124 llvm::Optional<StringRef> ModuleName) { 125 class FullDependencyPrinterConsumer : public DependencyConsumer { 126 public: 127 FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) 128 : AlreadySeen(AlreadySeen) {} 129 130 void 131 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {} 132 133 void handleFileDependency(StringRef File) override { 134 Dependencies.push_back(std::string(File)); 135 } 136 137 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 138 PrebuiltModuleDeps.emplace_back(std::move(PMD)); 139 } 140 141 void handleModuleDependency(ModuleDeps MD) override { 142 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD); 143 } 144 145 void handleContextHash(std::string Hash) override { 146 ContextHash = std::move(Hash); 147 } 148 149 FullDependenciesResult getFullDependencies( 150 const std::vector<std::string> &OriginalCommandLine) const { 151 FullDependencies FD; 152 153 FD.OriginalCommandLine = 154 ArrayRef<std::string>(OriginalCommandLine).slice(1); 155 156 FD.ID.ContextHash = std::move(ContextHash); 157 158 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 159 160 for (auto &&M : ClangModuleDeps) { 161 auto &MD = M.second; 162 if (MD.ImportedByMainFile) 163 FD.ClangModuleDeps.push_back(MD.ID); 164 } 165 166 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 167 168 FullDependenciesResult FDR; 169 170 for (auto &&M : ClangModuleDeps) { 171 // TODO: Avoid handleModuleDependency even being called for modules 172 // we've already seen. 173 if (AlreadySeen.count(M.first)) 174 continue; 175 FDR.DiscoveredModules.push_back(std::move(M.second)); 176 } 177 178 FDR.FullDeps = std::move(FD); 179 return FDR; 180 } 181 182 private: 183 std::vector<std::string> Dependencies; 184 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; 185 llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>> ClangModuleDeps; 186 std::string ContextHash; 187 std::vector<std::string> OutputPaths; 188 const llvm::StringSet<> &AlreadySeen; 189 }; 190 191 FullDependencyPrinterConsumer Consumer(AlreadySeen); 192 llvm::Error Result = 193 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 194 if (Result) 195 return std::move(Result); 196 return Consumer.getFullDependencies(CommandLine); 197 } 198