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