//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "clang/Frontend/Utils.h" #include using namespace clang; using namespace tooling; using namespace dependencies; static std::vector makeTUCommandLineWithoutPaths(ArrayRef OriginalCommandLine) { std::vector Args = OriginalCommandLine; Args.push_back("-fno-implicit-modules"); Args.push_back("-fno-implicit-module-maps"); // These arguments are unused in explicit compiles. llvm::erase_if(Args, [](StringRef Arg) { if (Arg.consume_front("-fmodules-")) { return Arg.startswith("cache-path=") || Arg.startswith("prune-interval=") || Arg.startswith("prune-after=") || Arg == "validate-once-per-build-session"; } return Arg.startswith("-fbuild-session-file="); }); return Args; } DependencyScanningTool::DependencyScanningTool( DependencyScanningService &Service, llvm::IntrusiveRefCntPtr FS) : Worker(Service, std::move(FS)) {} namespace { /// Prints out all of the gathered dependencies into a string. class MakeDependencyPrinterConsumer : public DependencyConsumer { public: void handleBuildCommand(Command) override {} void handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { this->Opts = std::make_unique(Opts); } void handleFileDependency(StringRef File) override { Dependencies.push_back(std::string(File)); } void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { // Same as `handleModuleDependency`. } void handleModuleDependency(ModuleDeps MD) override { // These are ignored for the make format as it can't support the full // set of deps, and handleFileDependency handles enough for implicitly // built modules to work. } void handleContextHash(std::string Hash) override {} std::string lookupModuleOutput(const ModuleID &ID, ModuleOutputKind Kind) override { llvm::report_fatal_error("unexpected call to lookupModuleOutput"); } void printDependencies(std::string &S) { assert(Opts && "Handled dependency output options."); class DependencyPrinter : public DependencyFileGenerator { public: DependencyPrinter(DependencyOutputOptions &Opts, ArrayRef Dependencies) : DependencyFileGenerator(Opts) { for (const auto &Dep : Dependencies) addDependency(Dep); } void printDependencies(std::string &S) { llvm::raw_string_ostream OS(S); outputDependencyFile(OS); } }; DependencyPrinter Generator(*Opts, Dependencies); Generator.printDependencies(S); } protected: std::unique_ptr Opts; std::vector Dependencies; }; } // anonymous namespace llvm::Expected DependencyScanningTool::getDependencyFile( const std::vector &CommandLine, StringRef CWD, std::optional ModuleName) { MakeDependencyPrinterConsumer Consumer; auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); std::string Output; Consumer.printDependencies(Output); return Output; } llvm::Expected DependencyScanningTool::getP1689ModuleDependencyFile( const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, std::string &MakeformatOutputPath) { class P1689ModuleDependencyPrinterConsumer : public MakeDependencyPrinterConsumer { public: P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, const CompileCommand &Command) : Filename(Command.Filename), Rule(Rule) { Rule.PrimaryOutput = Command.Output; } void handleProvidedAndRequiredStdCXXModules( std::optional Provided, std::vector Requires) override { Rule.Provides = Provided; if (Rule.Provides) Rule.Provides->SourcePath = Filename.str(); Rule.Requires = Requires; } StringRef getMakeFormatDependencyOutputPath() { if (Opts->OutputFormat != DependencyOutputFormat::Make) return {}; return Opts->OutputFile; } private: StringRef Filename; P1689Rule &Rule; }; P1689Rule Rule; P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer); if (Result) return std::move(Result); MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); if (!MakeformatOutputPath.empty()) Consumer.printDependencies(MakeformatOutput); return Rule; } llvm::Expected DependencyScanningTool::getFullDependencies( const std::vector &CommandLine, StringRef CWD, const llvm::StringSet<> &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput, std::optional ModuleName) { FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, Worker.shouldEagerLoadModules()); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); return Consumer.takeFullDependencies(); } llvm::Expected DependencyScanningTool::getFullDependenciesLegacyDriverCommand( const std::vector &CommandLine, StringRef CWD, const llvm::StringSet<> &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput, std::optional ModuleName) { FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput, Worker.shouldEagerLoadModules()); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine); } FullDependenciesResult FullDependencyConsumer::takeFullDependencies() { FullDependenciesResult FDR; FullDependencies &FD = FDR.FullDeps; FD.ID.ContextHash = std::move(ContextHash); FD.FileDeps = std::move(Dependencies); FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); FD.Commands = std::move(Commands); for (auto &&M : ClangModuleDeps) { auto &MD = M.second; if (MD.ImportedByMainFile) FD.ClangModuleDeps.push_back(MD.ID); // TODO: Avoid handleModuleDependency even being called for modules // we've already seen. if (AlreadySeen.count(M.first)) continue; FDR.DiscoveredModules.push_back(std::move(MD)); } return FDR; } FullDependenciesResult FullDependencyConsumer::getFullDependenciesLegacyDriverCommand( const std::vector &OriginalCommandLine) const { FullDependencies FD; FD.DriverCommandLine = makeTUCommandLineWithoutPaths( ArrayRef(OriginalCommandLine).slice(1)); FD.ID.ContextHash = std::move(ContextHash); FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile); for (auto &&M : ClangModuleDeps) { auto &MD = M.second; if (MD.ImportedByMainFile) { FD.ClangModuleDeps.push_back(MD.ID); auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile); if (EagerLoadModules) { FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath); } else { FD.DriverCommandLine.push_back("-fmodule-map-file=" + MD.ClangModuleMapFile); FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName + "=" + PCMPath); } } } FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); FullDependenciesResult FDR; for (auto &&M : ClangModuleDeps) { // TODO: Avoid handleModuleDependency even being called for modules // we've already seen. if (AlreadySeen.count(M.first)) continue; FDR.DiscoveredModules.push_back(std::move(M.second)); } FDR.FullDeps = std::move(FD); return FDR; }