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