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