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