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 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 55 : Worker(Service, std::move(FS)) {} 56 57 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 58 const std::vector<std::string> &CommandLine, StringRef CWD, 59 llvm::Optional<StringRef> ModuleName) { 60 /// Prints out all of the gathered dependencies into a string. 61 class MakeDependencyPrinterConsumer : public DependencyConsumer { 62 public: 63 void 64 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 65 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 66 } 67 68 void handleFileDependency(StringRef File) override { 69 Dependencies.push_back(std::string(File)); 70 } 71 72 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 73 // Same as `handleModuleDependency`. 74 } 75 76 void handleModuleDependency(ModuleDeps MD) override { 77 // These are ignored for the make format as it can't support the full 78 // set of deps, and handleFileDependency handles enough for implicitly 79 // built modules to work. 80 } 81 82 void handleContextHash(std::string Hash) override {} 83 84 void printDependencies(std::string &S) { 85 assert(Opts && "Handled dependency output options."); 86 87 class DependencyPrinter : public DependencyFileGenerator { 88 public: 89 DependencyPrinter(DependencyOutputOptions &Opts, 90 ArrayRef<std::string> Dependencies) 91 : DependencyFileGenerator(Opts) { 92 for (const auto &Dep : Dependencies) 93 addDependency(Dep); 94 } 95 96 void printDependencies(std::string &S) { 97 llvm::raw_string_ostream OS(S); 98 outputDependencyFile(OS); 99 } 100 }; 101 102 DependencyPrinter Generator(*Opts, Dependencies); 103 Generator.printDependencies(S); 104 } 105 106 private: 107 std::unique_ptr<DependencyOutputOptions> Opts; 108 std::vector<std::string> Dependencies; 109 }; 110 111 MakeDependencyPrinterConsumer Consumer; 112 auto Result = 113 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 114 if (Result) 115 return std::move(Result); 116 std::string Output; 117 Consumer.printDependencies(Output); 118 return Output; 119 } 120 121 llvm::Expected<FullDependenciesResult> 122 DependencyScanningTool::getFullDependencies( 123 const std::vector<std::string> &CommandLine, StringRef CWD, 124 const llvm::StringSet<> &AlreadySeen, 125 llvm::Optional<StringRef> ModuleName) { 126 class FullDependencyPrinterConsumer : public DependencyConsumer { 127 public: 128 FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) 129 : AlreadySeen(AlreadySeen) {} 130 131 void 132 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {} 133 134 void handleFileDependency(StringRef File) override { 135 Dependencies.push_back(std::string(File)); 136 } 137 138 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 139 PrebuiltModuleDeps.emplace_back(std::move(PMD)); 140 } 141 142 void handleModuleDependency(ModuleDeps MD) override { 143 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD); 144 } 145 146 void handleContextHash(std::string Hash) override { 147 ContextHash = std::move(Hash); 148 } 149 150 FullDependenciesResult getFullDependencies( 151 const std::vector<std::string> &OriginalCommandLine) const { 152 FullDependencies FD; 153 154 FD.OriginalCommandLine = 155 ArrayRef<std::string>(OriginalCommandLine).slice(1); 156 157 FD.ID.ContextHash = std::move(ContextHash); 158 159 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 160 161 for (auto &&M : ClangModuleDeps) { 162 auto &MD = M.second; 163 if (MD.ImportedByMainFile) 164 FD.ClangModuleDeps.push_back(MD.ID); 165 } 166 167 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 168 169 FullDependenciesResult FDR; 170 171 for (auto &&M : ClangModuleDeps) { 172 // TODO: Avoid handleModuleDependency even being called for modules 173 // we've already seen. 174 if (AlreadySeen.count(M.first)) 175 continue; 176 FDR.DiscoveredModules.push_back(std::move(M.second)); 177 } 178 179 FDR.FullDeps = std::move(FD); 180 return FDR; 181 } 182 183 private: 184 std::vector<std::string> Dependencies; 185 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; 186 llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>> ClangModuleDeps; 187 std::string ContextHash; 188 std::vector<std::string> OutputPaths; 189 const llvm::StringSet<> &AlreadySeen; 190 }; 191 192 FullDependencyPrinterConsumer Consumer(AlreadySeen); 193 llvm::Error Result = 194 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 195 if (Result) 196 return std::move(Result); 197 return Consumer.getFullDependencies(CommandLine); 198 } 199