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 #include <optional> 12 13 using namespace clang; 14 using namespace tooling; 15 using namespace dependencies; 16 17 static std::vector<std::string> 18 makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) { 19 std::vector<std::string> Args = OriginalCommandLine; 20 21 Args.push_back("-fno-implicit-modules"); 22 Args.push_back("-fno-implicit-module-maps"); 23 24 // These arguments are unused in explicit compiles. 25 llvm::erase_if(Args, [](StringRef Arg) { 26 if (Arg.consume_front("-fmodules-")) { 27 return Arg.startswith("cache-path=") || 28 Arg.startswith("prune-interval=") || 29 Arg.startswith("prune-after=") || 30 Arg == "validate-once-per-build-session"; 31 } 32 return Arg.startswith("-fbuild-session-file="); 33 }); 34 35 return Args; 36 } 37 38 DependencyScanningTool::DependencyScanningTool( 39 DependencyScanningService &Service, 40 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 41 : Worker(Service, std::move(FS)) {} 42 43 namespace { 44 /// Prints out all of the gathered dependencies into a string. 45 class MakeDependencyPrinterConsumer : public DependencyConsumer { 46 public: 47 void handleBuildCommand(Command) override {} 48 49 void 50 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 51 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 52 } 53 54 void handleFileDependency(StringRef File) override { 55 Dependencies.push_back(std::string(File)); 56 } 57 58 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 59 // Same as `handleModuleDependency`. 60 } 61 62 void handleModuleDependency(ModuleDeps MD) override { 63 // These are ignored for the make format as it can't support the full 64 // set of deps, and handleFileDependency handles enough for implicitly 65 // built modules to work. 66 } 67 68 void handleContextHash(std::string Hash) override {} 69 70 std::string lookupModuleOutput(const ModuleID &ID, 71 ModuleOutputKind Kind) override { 72 llvm::report_fatal_error("unexpected call to lookupModuleOutput"); 73 } 74 75 void printDependencies(std::string &S) { 76 assert(Opts && "Handled dependency output options."); 77 78 class DependencyPrinter : public DependencyFileGenerator { 79 public: 80 DependencyPrinter(DependencyOutputOptions &Opts, 81 ArrayRef<std::string> Dependencies) 82 : DependencyFileGenerator(Opts) { 83 for (const auto &Dep : Dependencies) 84 addDependency(Dep); 85 } 86 87 void printDependencies(std::string &S) { 88 llvm::raw_string_ostream OS(S); 89 outputDependencyFile(OS); 90 } 91 }; 92 93 DependencyPrinter Generator(*Opts, Dependencies); 94 Generator.printDependencies(S); 95 } 96 97 protected: 98 std::unique_ptr<DependencyOutputOptions> Opts; 99 std::vector<std::string> Dependencies; 100 }; 101 } // anonymous namespace 102 103 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 104 const std::vector<std::string> &CommandLine, StringRef CWD, 105 std::optional<StringRef> ModuleName) { 106 MakeDependencyPrinterConsumer Consumer; 107 auto Result = 108 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 109 if (Result) 110 return std::move(Result); 111 std::string Output; 112 Consumer.printDependencies(Output); 113 return Output; 114 } 115 116 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile( 117 const CompileCommand &Command, StringRef CWD, 118 std::string &MakeformatOutput, std::string &MakeformatOutputPath) { 119 class P1689ModuleDependencyPrinterConsumer 120 : public MakeDependencyPrinterConsumer { 121 public: 122 P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, 123 const CompileCommand &Command) 124 : Filename(Command.Filename), Rule(Rule) { 125 Rule.PrimaryOutput = Command.Output; 126 } 127 128 void handleProvidedAndRequiredStdCXXModules( 129 std::optional<P1689ModuleInfo> Provided, 130 std::vector<P1689ModuleInfo> Requires) override { 131 Rule.Provides = Provided; 132 if (Rule.Provides) 133 Rule.Provides->SourcePath = Filename.str(); 134 Rule.Requires = Requires; 135 } 136 137 StringRef getMakeFormatDependencyOutputPath() { 138 if (Opts->OutputFormat != DependencyOutputFormat::Make) 139 return {}; 140 return Opts->OutputFile; 141 } 142 143 private: 144 StringRef Filename; 145 P1689Rule &Rule; 146 }; 147 148 P1689Rule Rule; 149 P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); 150 auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer); 151 if (Result) 152 return std::move(Result); 153 154 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); 155 if (!MakeformatOutputPath.empty()) 156 Consumer.printDependencies(MakeformatOutput); 157 return Rule; 158 } 159 160 llvm::Expected<FullDependenciesResult> 161 DependencyScanningTool::getFullDependencies( 162 const std::vector<std::string> &CommandLine, StringRef CWD, 163 const llvm::StringSet<> &AlreadySeen, 164 LookupModuleOutputCallback LookupModuleOutput, 165 std::optional<StringRef> ModuleName) { 166 FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 167 Worker.shouldEagerLoadModules()); 168 llvm::Error Result = 169 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 170 if (Result) 171 return std::move(Result); 172 return Consumer.takeFullDependencies(); 173 } 174 175 llvm::Expected<FullDependenciesResult> 176 DependencyScanningTool::getFullDependenciesLegacyDriverCommand( 177 const std::vector<std::string> &CommandLine, StringRef CWD, 178 const llvm::StringSet<> &AlreadySeen, 179 LookupModuleOutputCallback LookupModuleOutput, 180 std::optional<StringRef> ModuleName) { 181 FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 182 Worker.shouldEagerLoadModules()); 183 llvm::Error Result = 184 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 185 if (Result) 186 return std::move(Result); 187 return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine); 188 } 189 190 FullDependenciesResult FullDependencyConsumer::takeFullDependencies() { 191 FullDependenciesResult FDR; 192 FullDependencies &FD = FDR.FullDeps; 193 194 FD.ID.ContextHash = std::move(ContextHash); 195 FD.FileDeps = std::move(Dependencies); 196 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 197 FD.Commands = std::move(Commands); 198 199 for (auto &&M : ClangModuleDeps) { 200 auto &MD = M.second; 201 if (MD.ImportedByMainFile) 202 FD.ClangModuleDeps.push_back(MD.ID); 203 // TODO: Avoid handleModuleDependency even being called for modules 204 // we've already seen. 205 if (AlreadySeen.count(M.first)) 206 continue; 207 FDR.DiscoveredModules.push_back(std::move(MD)); 208 } 209 210 return FDR; 211 } 212 213 FullDependenciesResult 214 FullDependencyConsumer::getFullDependenciesLegacyDriverCommand( 215 const std::vector<std::string> &OriginalCommandLine) const { 216 FullDependencies FD; 217 218 FD.DriverCommandLine = makeTUCommandLineWithoutPaths( 219 ArrayRef<std::string>(OriginalCommandLine).slice(1)); 220 221 FD.ID.ContextHash = std::move(ContextHash); 222 223 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 224 225 for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) 226 FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile); 227 228 for (auto &&M : ClangModuleDeps) { 229 auto &MD = M.second; 230 if (MD.ImportedByMainFile) { 231 FD.ClangModuleDeps.push_back(MD.ID); 232 auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile); 233 if (EagerLoadModules) { 234 FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath); 235 } else { 236 FD.DriverCommandLine.push_back("-fmodule-map-file=" + 237 MD.ClangModuleMapFile); 238 FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName + 239 "=" + PCMPath); 240 } 241 } 242 } 243 244 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 245 246 FullDependenciesResult FDR; 247 248 for (auto &&M : ClangModuleDeps) { 249 // TODO: Avoid handleModuleDependency even being called for modules 250 // we've already seen. 251 if (AlreadySeen.count(M.first)) 252 continue; 253 FDR.DiscoveredModules.push_back(std::move(M.second)); 254 } 255 256 FDR.FullDeps = std::move(FD); 257 return FDR; 258 } 259