//===- 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; 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)); } // 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 handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {} void handleModuleDependency(ModuleDeps MD) override {} void handleDirectModuleDependency(ModuleID ID) override {} void handleContextHash(std::string Hash) override {} 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) { MakeDependencyPrinterConsumer Consumer; CallbackActionController Controller(nullptr); auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); 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; }; class P1689ActionController : public DependencyActionController { public: // The lookupModuleOutput is for clang modules. P1689 format don't need it. std::string lookupModuleOutput(const ModuleID &, ModuleOutputKind Kind) override { return ""; } }; P1689Rule Rule; P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); P1689ActionController Controller; auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer, Controller); if (Result) return std::move(Result); MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); if (!MakeformatOutputPath.empty()) Consumer.printDependencies(MakeformatOutput); return Rule; } llvm::Expected DependencyScanningTool::getTranslationUnitDependencies( const std::vector &CommandLine, StringRef CWD, const llvm::DenseSet &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput) { FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); if (Result) return std::move(Result); return Consumer.takeTranslationUnitDeps(); } llvm::Expected DependencyScanningTool::getModuleDependencies( StringRef ModuleName, const std::vector &CommandLine, StringRef CWD, const llvm::DenseSet &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput) { FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, Controller, ModuleName); if (Result) return std::move(Result); return Consumer.takeModuleGraphDeps(); } TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() { TranslationUnitDeps TU; TU.ID.ContextHash = std::move(ContextHash); TU.FileDeps = std::move(Dependencies); TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); TU.Commands = std::move(Commands); for (auto &&M : ClangModuleDeps) { auto &MD = M.second; // TODO: Avoid handleModuleDependency even being called for modules // we've already seen. if (AlreadySeen.count(M.first)) continue; TU.ModuleGraph.push_back(std::move(MD)); } TU.ClangModuleDeps = std::move(DirectModuleDeps); return TU; } ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() { ModuleDepsGraph ModuleGraph; for (auto &&M : ClangModuleDeps) { auto &MD = M.second; // TODO: Avoid handleModuleDependency even being called for modules // we've already seen. if (AlreadySeen.count(M.first)) continue; ModuleGraph.push_back(std::move(MD)); } return ModuleGraph; } CallbackActionController::~CallbackActionController() {}