1480093f4SDimitry Andric //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric 9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 10a7dea167SDimitry Andric #include "clang/Frontend/Utils.h" 11bdd1243dSDimitry Andric #include <optional> 12a7dea167SDimitry Andric 13753f127fSDimitry Andric using namespace clang; 14753f127fSDimitry Andric using namespace tooling; 15753f127fSDimitry Andric using namespace dependencies; 16a7dea167SDimitry Andric 17bdd1243dSDimitry Andric static std::vector<std::string> 18bdd1243dSDimitry Andric makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) { 1981ad6265SDimitry Andric std::vector<std::string> Args = OriginalCommandLine; 20fe6060f1SDimitry Andric 2181ad6265SDimitry Andric Args.push_back("-fno-implicit-modules"); 2281ad6265SDimitry Andric Args.push_back("-fno-implicit-module-maps"); 23fe6060f1SDimitry Andric 2481ad6265SDimitry Andric // These arguments are unused in explicit compiles. 2581ad6265SDimitry Andric llvm::erase_if(Args, [](StringRef Arg) { 2681ad6265SDimitry Andric if (Arg.consume_front("-fmodules-")) { 2781ad6265SDimitry Andric return Arg.startswith("cache-path=") || 2881ad6265SDimitry Andric Arg.startswith("prune-interval=") || 2981ad6265SDimitry Andric Arg.startswith("prune-after=") || 3081ad6265SDimitry Andric Arg == "validate-once-per-build-session"; 3181ad6265SDimitry Andric } 3281ad6265SDimitry Andric return Arg.startswith("-fbuild-session-file="); 3381ad6265SDimitry Andric }); 3481ad6265SDimitry Andric 35fe6060f1SDimitry Andric return Args; 36fe6060f1SDimitry Andric } 37fe6060f1SDimitry Andric 38480093f4SDimitry Andric DependencyScanningTool::DependencyScanningTool( 39fcaf7f86SDimitry Andric DependencyScanningService &Service, 40fcaf7f86SDimitry Andric llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 41fcaf7f86SDimitry Andric : Worker(Service, std::move(FS)) {} 42a7dea167SDimitry Andric 43*1ac55f4cSDimitry Andric namespace { 44a7dea167SDimitry Andric /// Prints out all of the gathered dependencies into a string. 45480093f4SDimitry Andric class MakeDependencyPrinterConsumer : public DependencyConsumer { 46a7dea167SDimitry Andric public: 47bdd1243dSDimitry Andric void handleBuildCommand(Command) override {} 48bdd1243dSDimitry Andric 49fe6060f1SDimitry Andric void 50fe6060f1SDimitry Andric handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 51a7dea167SDimitry Andric this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 52fe6060f1SDimitry Andric } 53fe6060f1SDimitry Andric 54fe6060f1SDimitry Andric void handleFileDependency(StringRef File) override { 555ffd83dbSDimitry Andric Dependencies.push_back(std::string(File)); 56a7dea167SDimitry Andric } 57a7dea167SDimitry Andric 58fe6060f1SDimitry Andric void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 59fe6060f1SDimitry Andric // Same as `handleModuleDependency`. 60fe6060f1SDimitry Andric } 61fe6060f1SDimitry Andric 62480093f4SDimitry Andric void handleModuleDependency(ModuleDeps MD) override { 63480093f4SDimitry Andric // These are ignored for the make format as it can't support the full 64480093f4SDimitry Andric // set of deps, and handleFileDependency handles enough for implicitly 65480093f4SDimitry Andric // built modules to work. 66480093f4SDimitry Andric } 67480093f4SDimitry Andric 68480093f4SDimitry Andric void handleContextHash(std::string Hash) override {} 69480093f4SDimitry Andric 70bdd1243dSDimitry Andric std::string lookupModuleOutput(const ModuleID &ID, 71bdd1243dSDimitry Andric ModuleOutputKind Kind) override { 72bdd1243dSDimitry Andric llvm::report_fatal_error("unexpected call to lookupModuleOutput"); 73bdd1243dSDimitry Andric } 74bdd1243dSDimitry Andric 75a7dea167SDimitry Andric void printDependencies(std::string &S) { 76fe6060f1SDimitry Andric assert(Opts && "Handled dependency output options."); 77a7dea167SDimitry Andric 78a7dea167SDimitry Andric class DependencyPrinter : public DependencyFileGenerator { 79a7dea167SDimitry Andric public: 80a7dea167SDimitry Andric DependencyPrinter(DependencyOutputOptions &Opts, 81a7dea167SDimitry Andric ArrayRef<std::string> Dependencies) 82a7dea167SDimitry Andric : DependencyFileGenerator(Opts) { 83a7dea167SDimitry Andric for (const auto &Dep : Dependencies) 84a7dea167SDimitry Andric addDependency(Dep); 85a7dea167SDimitry Andric } 86a7dea167SDimitry Andric 87a7dea167SDimitry Andric void printDependencies(std::string &S) { 88a7dea167SDimitry Andric llvm::raw_string_ostream OS(S); 89a7dea167SDimitry Andric outputDependencyFile(OS); 90a7dea167SDimitry Andric } 91a7dea167SDimitry Andric }; 92a7dea167SDimitry Andric 93a7dea167SDimitry Andric DependencyPrinter Generator(*Opts, Dependencies); 94a7dea167SDimitry Andric Generator.printDependencies(S); 95a7dea167SDimitry Andric } 96a7dea167SDimitry Andric 97*1ac55f4cSDimitry Andric protected: 98a7dea167SDimitry Andric std::unique_ptr<DependencyOutputOptions> Opts; 99a7dea167SDimitry Andric std::vector<std::string> Dependencies; 100a7dea167SDimitry Andric }; 101*1ac55f4cSDimitry Andric } // anonymous namespace 102a7dea167SDimitry Andric 103*1ac55f4cSDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 104*1ac55f4cSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 105*1ac55f4cSDimitry Andric std::optional<StringRef> ModuleName) { 1065ffd83dbSDimitry Andric MakeDependencyPrinterConsumer Consumer; 107349cc55cSDimitry Andric auto Result = 108349cc55cSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 1095ffd83dbSDimitry Andric if (Result) 1105ffd83dbSDimitry Andric return std::move(Result); 1115ffd83dbSDimitry Andric std::string Output; 1125ffd83dbSDimitry Andric Consumer.printDependencies(Output); 1135ffd83dbSDimitry Andric return Output; 1145ffd83dbSDimitry Andric } 1155ffd83dbSDimitry Andric 116*1ac55f4cSDimitry Andric llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile( 117*1ac55f4cSDimitry Andric const CompileCommand &Command, StringRef CWD, 118*1ac55f4cSDimitry Andric std::string &MakeformatOutput, std::string &MakeformatOutputPath) { 119*1ac55f4cSDimitry Andric class P1689ModuleDependencyPrinterConsumer 120*1ac55f4cSDimitry Andric : public MakeDependencyPrinterConsumer { 121*1ac55f4cSDimitry Andric public: 122*1ac55f4cSDimitry Andric P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, 123*1ac55f4cSDimitry Andric const CompileCommand &Command) 124*1ac55f4cSDimitry Andric : Filename(Command.Filename), Rule(Rule) { 125*1ac55f4cSDimitry Andric Rule.PrimaryOutput = Command.Output; 126*1ac55f4cSDimitry Andric } 127*1ac55f4cSDimitry Andric 128*1ac55f4cSDimitry Andric void handleProvidedAndRequiredStdCXXModules( 129*1ac55f4cSDimitry Andric std::optional<P1689ModuleInfo> Provided, 130*1ac55f4cSDimitry Andric std::vector<P1689ModuleInfo> Requires) override { 131*1ac55f4cSDimitry Andric Rule.Provides = Provided; 132*1ac55f4cSDimitry Andric if (Rule.Provides) 133*1ac55f4cSDimitry Andric Rule.Provides->SourcePath = Filename.str(); 134*1ac55f4cSDimitry Andric Rule.Requires = Requires; 135*1ac55f4cSDimitry Andric } 136*1ac55f4cSDimitry Andric 137*1ac55f4cSDimitry Andric StringRef getMakeFormatDependencyOutputPath() { 138*1ac55f4cSDimitry Andric if (Opts->OutputFormat != DependencyOutputFormat::Make) 139*1ac55f4cSDimitry Andric return {}; 140*1ac55f4cSDimitry Andric return Opts->OutputFile; 141*1ac55f4cSDimitry Andric } 142*1ac55f4cSDimitry Andric 143*1ac55f4cSDimitry Andric private: 144*1ac55f4cSDimitry Andric StringRef Filename; 145*1ac55f4cSDimitry Andric P1689Rule &Rule; 146*1ac55f4cSDimitry Andric }; 147*1ac55f4cSDimitry Andric 148*1ac55f4cSDimitry Andric P1689Rule Rule; 149*1ac55f4cSDimitry Andric P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); 150*1ac55f4cSDimitry Andric auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer); 151*1ac55f4cSDimitry Andric if (Result) 152*1ac55f4cSDimitry Andric return std::move(Result); 153*1ac55f4cSDimitry Andric 154*1ac55f4cSDimitry Andric MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); 155*1ac55f4cSDimitry Andric if (!MakeformatOutputPath.empty()) 156*1ac55f4cSDimitry Andric Consumer.printDependencies(MakeformatOutput); 157*1ac55f4cSDimitry Andric return Rule; 158*1ac55f4cSDimitry Andric } 159*1ac55f4cSDimitry Andric 1605ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult> 1615ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies( 162349cc55cSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 163349cc55cSDimitry Andric const llvm::StringSet<> &AlreadySeen, 164bdd1243dSDimitry Andric LookupModuleOutputCallback LookupModuleOutput, 165bdd1243dSDimitry Andric std::optional<StringRef> ModuleName) { 166bdd1243dSDimitry Andric FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 167bdd1243dSDimitry Andric Worker.shouldEagerLoadModules()); 168bdd1243dSDimitry Andric llvm::Error Result = 169bdd1243dSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 170bdd1243dSDimitry Andric if (Result) 171bdd1243dSDimitry Andric return std::move(Result); 172bdd1243dSDimitry Andric return Consumer.takeFullDependencies(); 173480093f4SDimitry Andric } 174480093f4SDimitry Andric 175bdd1243dSDimitry Andric llvm::Expected<FullDependenciesResult> 176bdd1243dSDimitry Andric DependencyScanningTool::getFullDependenciesLegacyDriverCommand( 177bdd1243dSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 178bdd1243dSDimitry Andric const llvm::StringSet<> &AlreadySeen, 179bdd1243dSDimitry Andric LookupModuleOutputCallback LookupModuleOutput, 180bdd1243dSDimitry Andric std::optional<StringRef> ModuleName) { 181bdd1243dSDimitry Andric FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 182bdd1243dSDimitry Andric Worker.shouldEagerLoadModules()); 183bdd1243dSDimitry Andric llvm::Error Result = 184bdd1243dSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 185bdd1243dSDimitry Andric if (Result) 186bdd1243dSDimitry Andric return std::move(Result); 187bdd1243dSDimitry Andric return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine); 188fe6060f1SDimitry Andric } 189fe6060f1SDimitry Andric 190bdd1243dSDimitry Andric FullDependenciesResult FullDependencyConsumer::takeFullDependencies() { 191bdd1243dSDimitry Andric FullDependenciesResult FDR; 192bdd1243dSDimitry Andric FullDependencies &FD = FDR.FullDeps; 19381ad6265SDimitry Andric 194fe6060f1SDimitry Andric FD.ID.ContextHash = std::move(ContextHash); 195bdd1243dSDimitry Andric FD.FileDeps = std::move(Dependencies); 196bdd1243dSDimitry Andric FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 197bdd1243dSDimitry Andric FD.Commands = std::move(Commands); 198480093f4SDimitry Andric 1995ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 2005ffd83dbSDimitry Andric auto &MD = M.second; 201480093f4SDimitry Andric if (MD.ImportedByMainFile) 202fe6060f1SDimitry Andric FD.ClangModuleDeps.push_back(MD.ID); 203bdd1243dSDimitry Andric // TODO: Avoid handleModuleDependency even being called for modules 204bdd1243dSDimitry Andric // we've already seen. 205bdd1243dSDimitry Andric if (AlreadySeen.count(M.first)) 206bdd1243dSDimitry Andric continue; 207bdd1243dSDimitry Andric FDR.DiscoveredModules.push_back(std::move(MD)); 208bdd1243dSDimitry Andric } 209bdd1243dSDimitry Andric 210bdd1243dSDimitry Andric return FDR; 211bdd1243dSDimitry Andric } 212bdd1243dSDimitry Andric 213bdd1243dSDimitry Andric FullDependenciesResult 214bdd1243dSDimitry Andric FullDependencyConsumer::getFullDependenciesLegacyDriverCommand( 215bdd1243dSDimitry Andric const std::vector<std::string> &OriginalCommandLine) const { 216bdd1243dSDimitry Andric FullDependencies FD; 217bdd1243dSDimitry Andric 218bdd1243dSDimitry Andric FD.DriverCommandLine = makeTUCommandLineWithoutPaths( 219bdd1243dSDimitry Andric ArrayRef<std::string>(OriginalCommandLine).slice(1)); 220bdd1243dSDimitry Andric 221bdd1243dSDimitry Andric FD.ID.ContextHash = std::move(ContextHash); 222bdd1243dSDimitry Andric 223bdd1243dSDimitry Andric FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 224bdd1243dSDimitry Andric 225bdd1243dSDimitry Andric for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) 226bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile); 227bdd1243dSDimitry Andric 228bdd1243dSDimitry Andric for (auto &&M : ClangModuleDeps) { 229bdd1243dSDimitry Andric auto &MD = M.second; 230bdd1243dSDimitry Andric if (MD.ImportedByMainFile) { 231bdd1243dSDimitry Andric FD.ClangModuleDeps.push_back(MD.ID); 232bdd1243dSDimitry Andric auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile); 233bdd1243dSDimitry Andric if (EagerLoadModules) { 234bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath); 235bdd1243dSDimitry Andric } else { 236bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-map-file=" + 237bdd1243dSDimitry Andric MD.ClangModuleMapFile); 238bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName + 239bdd1243dSDimitry Andric "=" + PCMPath); 240bdd1243dSDimitry Andric } 241bdd1243dSDimitry Andric } 242480093f4SDimitry Andric } 243480093f4SDimitry Andric 244fe6060f1SDimitry Andric FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 245fe6060f1SDimitry Andric 2465ffd83dbSDimitry Andric FullDependenciesResult FDR; 2475ffd83dbSDimitry Andric 2485ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 2495ffd83dbSDimitry Andric // TODO: Avoid handleModuleDependency even being called for modules 2505ffd83dbSDimitry Andric // we've already seen. 2515ffd83dbSDimitry Andric if (AlreadySeen.count(M.first)) 2525ffd83dbSDimitry Andric continue; 2535ffd83dbSDimitry Andric FDR.DiscoveredModules.push_back(std::move(M.second)); 254480093f4SDimitry Andric } 255480093f4SDimitry Andric 2565ffd83dbSDimitry Andric FDR.FullDeps = std::move(FD); 2575ffd83dbSDimitry Andric return FDR; 258480093f4SDimitry Andric } 259