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" 11*bdd1243dSDimitry Andric #include <optional> 12a7dea167SDimitry Andric 13753f127fSDimitry Andric using namespace clang; 14753f127fSDimitry Andric using namespace tooling; 15753f127fSDimitry Andric using namespace dependencies; 16a7dea167SDimitry Andric 17*bdd1243dSDimitry Andric static std::vector<std::string> 18*bdd1243dSDimitry 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 43480093f4SDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 44349cc55cSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 45*bdd1243dSDimitry Andric std::optional<StringRef> ModuleName) { 46a7dea167SDimitry Andric /// Prints out all of the gathered dependencies into a string. 47480093f4SDimitry Andric class MakeDependencyPrinterConsumer : public DependencyConsumer { 48a7dea167SDimitry Andric public: 49*bdd1243dSDimitry Andric void handleBuildCommand(Command) override {} 50*bdd1243dSDimitry Andric 51fe6060f1SDimitry Andric void 52fe6060f1SDimitry Andric handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 53a7dea167SDimitry Andric this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 54fe6060f1SDimitry Andric } 55fe6060f1SDimitry Andric 56fe6060f1SDimitry Andric void handleFileDependency(StringRef File) override { 575ffd83dbSDimitry Andric Dependencies.push_back(std::string(File)); 58a7dea167SDimitry Andric } 59a7dea167SDimitry Andric 60fe6060f1SDimitry Andric void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 61fe6060f1SDimitry Andric // Same as `handleModuleDependency`. 62fe6060f1SDimitry Andric } 63fe6060f1SDimitry Andric 64480093f4SDimitry Andric void handleModuleDependency(ModuleDeps MD) override { 65480093f4SDimitry Andric // These are ignored for the make format as it can't support the full 66480093f4SDimitry Andric // set of deps, and handleFileDependency handles enough for implicitly 67480093f4SDimitry Andric // built modules to work. 68480093f4SDimitry Andric } 69480093f4SDimitry Andric 70480093f4SDimitry Andric void handleContextHash(std::string Hash) override {} 71480093f4SDimitry Andric 72*bdd1243dSDimitry Andric std::string lookupModuleOutput(const ModuleID &ID, 73*bdd1243dSDimitry Andric ModuleOutputKind Kind) override { 74*bdd1243dSDimitry Andric llvm::report_fatal_error("unexpected call to lookupModuleOutput"); 75*bdd1243dSDimitry Andric } 76*bdd1243dSDimitry Andric 77a7dea167SDimitry Andric void printDependencies(std::string &S) { 78fe6060f1SDimitry Andric assert(Opts && "Handled dependency output options."); 79a7dea167SDimitry Andric 80a7dea167SDimitry Andric class DependencyPrinter : public DependencyFileGenerator { 81a7dea167SDimitry Andric public: 82a7dea167SDimitry Andric DependencyPrinter(DependencyOutputOptions &Opts, 83a7dea167SDimitry Andric ArrayRef<std::string> Dependencies) 84a7dea167SDimitry Andric : DependencyFileGenerator(Opts) { 85a7dea167SDimitry Andric for (const auto &Dep : Dependencies) 86a7dea167SDimitry Andric addDependency(Dep); 87a7dea167SDimitry Andric } 88a7dea167SDimitry Andric 89a7dea167SDimitry Andric void printDependencies(std::string &S) { 90a7dea167SDimitry Andric llvm::raw_string_ostream OS(S); 91a7dea167SDimitry Andric outputDependencyFile(OS); 92a7dea167SDimitry Andric } 93a7dea167SDimitry Andric }; 94a7dea167SDimitry Andric 95a7dea167SDimitry Andric DependencyPrinter Generator(*Opts, Dependencies); 96a7dea167SDimitry Andric Generator.printDependencies(S); 97a7dea167SDimitry Andric } 98a7dea167SDimitry Andric 99a7dea167SDimitry Andric private: 100a7dea167SDimitry Andric std::unique_ptr<DependencyOutputOptions> Opts; 101a7dea167SDimitry Andric std::vector<std::string> Dependencies; 102a7dea167SDimitry Andric }; 103a7dea167SDimitry Andric 1045ffd83dbSDimitry Andric MakeDependencyPrinterConsumer Consumer; 105349cc55cSDimitry Andric auto Result = 106349cc55cSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 1075ffd83dbSDimitry Andric if (Result) 1085ffd83dbSDimitry Andric return std::move(Result); 1095ffd83dbSDimitry Andric std::string Output; 1105ffd83dbSDimitry Andric Consumer.printDependencies(Output); 1115ffd83dbSDimitry Andric return Output; 1125ffd83dbSDimitry Andric } 1135ffd83dbSDimitry Andric 1145ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult> 1155ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies( 116349cc55cSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 117349cc55cSDimitry Andric const llvm::StringSet<> &AlreadySeen, 118*bdd1243dSDimitry Andric LookupModuleOutputCallback LookupModuleOutput, 119*bdd1243dSDimitry Andric std::optional<StringRef> ModuleName) { 120*bdd1243dSDimitry Andric FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 121*bdd1243dSDimitry Andric Worker.shouldEagerLoadModules()); 122*bdd1243dSDimitry Andric llvm::Error Result = 123*bdd1243dSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 124*bdd1243dSDimitry Andric if (Result) 125*bdd1243dSDimitry Andric return std::move(Result); 126*bdd1243dSDimitry Andric return Consumer.takeFullDependencies(); 127480093f4SDimitry Andric } 128480093f4SDimitry Andric 129*bdd1243dSDimitry Andric llvm::Expected<FullDependenciesResult> 130*bdd1243dSDimitry Andric DependencyScanningTool::getFullDependenciesLegacyDriverCommand( 131*bdd1243dSDimitry Andric const std::vector<std::string> &CommandLine, StringRef CWD, 132*bdd1243dSDimitry Andric const llvm::StringSet<> &AlreadySeen, 133*bdd1243dSDimitry Andric LookupModuleOutputCallback LookupModuleOutput, 134*bdd1243dSDimitry Andric std::optional<StringRef> ModuleName) { 135*bdd1243dSDimitry Andric FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, 136*bdd1243dSDimitry Andric Worker.shouldEagerLoadModules()); 137*bdd1243dSDimitry Andric llvm::Error Result = 138*bdd1243dSDimitry Andric Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); 139*bdd1243dSDimitry Andric if (Result) 140*bdd1243dSDimitry Andric return std::move(Result); 141*bdd1243dSDimitry Andric return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine); 142fe6060f1SDimitry Andric } 143fe6060f1SDimitry Andric 144*bdd1243dSDimitry Andric FullDependenciesResult FullDependencyConsumer::takeFullDependencies() { 145*bdd1243dSDimitry Andric FullDependenciesResult FDR; 146*bdd1243dSDimitry Andric FullDependencies &FD = FDR.FullDeps; 14781ad6265SDimitry Andric 148fe6060f1SDimitry Andric FD.ID.ContextHash = std::move(ContextHash); 149*bdd1243dSDimitry Andric FD.FileDeps = std::move(Dependencies); 150*bdd1243dSDimitry Andric FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 151*bdd1243dSDimitry Andric FD.Commands = std::move(Commands); 152480093f4SDimitry Andric 1535ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 1545ffd83dbSDimitry Andric auto &MD = M.second; 155480093f4SDimitry Andric if (MD.ImportedByMainFile) 156fe6060f1SDimitry Andric FD.ClangModuleDeps.push_back(MD.ID); 157*bdd1243dSDimitry Andric // TODO: Avoid handleModuleDependency even being called for modules 158*bdd1243dSDimitry Andric // we've already seen. 159*bdd1243dSDimitry Andric if (AlreadySeen.count(M.first)) 160*bdd1243dSDimitry Andric continue; 161*bdd1243dSDimitry Andric FDR.DiscoveredModules.push_back(std::move(MD)); 162*bdd1243dSDimitry Andric } 163*bdd1243dSDimitry Andric 164*bdd1243dSDimitry Andric return FDR; 165*bdd1243dSDimitry Andric } 166*bdd1243dSDimitry Andric 167*bdd1243dSDimitry Andric FullDependenciesResult 168*bdd1243dSDimitry Andric FullDependencyConsumer::getFullDependenciesLegacyDriverCommand( 169*bdd1243dSDimitry Andric const std::vector<std::string> &OriginalCommandLine) const { 170*bdd1243dSDimitry Andric FullDependencies FD; 171*bdd1243dSDimitry Andric 172*bdd1243dSDimitry Andric FD.DriverCommandLine = makeTUCommandLineWithoutPaths( 173*bdd1243dSDimitry Andric ArrayRef<std::string>(OriginalCommandLine).slice(1)); 174*bdd1243dSDimitry Andric 175*bdd1243dSDimitry Andric FD.ID.ContextHash = std::move(ContextHash); 176*bdd1243dSDimitry Andric 177*bdd1243dSDimitry Andric FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); 178*bdd1243dSDimitry Andric 179*bdd1243dSDimitry Andric for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) 180*bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile); 181*bdd1243dSDimitry Andric 182*bdd1243dSDimitry Andric for (auto &&M : ClangModuleDeps) { 183*bdd1243dSDimitry Andric auto &MD = M.second; 184*bdd1243dSDimitry Andric if (MD.ImportedByMainFile) { 185*bdd1243dSDimitry Andric FD.ClangModuleDeps.push_back(MD.ID); 186*bdd1243dSDimitry Andric auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile); 187*bdd1243dSDimitry Andric if (EagerLoadModules) { 188*bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath); 189*bdd1243dSDimitry Andric } else { 190*bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-map-file=" + 191*bdd1243dSDimitry Andric MD.ClangModuleMapFile); 192*bdd1243dSDimitry Andric FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName + 193*bdd1243dSDimitry Andric "=" + PCMPath); 194*bdd1243dSDimitry Andric } 195*bdd1243dSDimitry Andric } 196480093f4SDimitry Andric } 197480093f4SDimitry Andric 198fe6060f1SDimitry Andric FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 199fe6060f1SDimitry Andric 2005ffd83dbSDimitry Andric FullDependenciesResult FDR; 2015ffd83dbSDimitry Andric 2025ffd83dbSDimitry Andric for (auto &&M : ClangModuleDeps) { 2035ffd83dbSDimitry Andric // TODO: Avoid handleModuleDependency even being called for modules 2045ffd83dbSDimitry Andric // we've already seen. 2055ffd83dbSDimitry Andric if (AlreadySeen.count(M.first)) 2065ffd83dbSDimitry Andric continue; 2075ffd83dbSDimitry Andric FDR.DiscoveredModules.push_back(std::move(M.second)); 208480093f4SDimitry Andric } 209480093f4SDimitry Andric 2105ffd83dbSDimitry Andric FDR.FullDeps = std::move(FD); 2115ffd83dbSDimitry Andric return FDR; 212480093f4SDimitry Andric } 213