//===- 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" using namespace clang; using namespace tooling; using namespace dependencies; std::vector FullDependencies::getCommandLine( llvm::function_ref LookupModuleOutput) const { std::vector Ret = getCommandLineWithoutModulePaths(); for (ModuleID MID : ClangModuleDeps) { auto PCM = LookupModuleOutput(MID, ModuleOutputKind::ModuleFile); Ret.push_back("-fmodule-file=" + PCM); } return Ret; } std::vector FullDependencies::getCommandLineWithoutModulePaths() const { std::vector Args = OriginalCommandLine; Args.push_back("-fno-implicit-modules"); Args.push_back("-fno-implicit-module-maps"); for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) Args.push_back("-fmodule-file=" + PMD.PCMFile); // 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)) {} llvm::Expected DependencyScanningTool::getDependencyFile( const std::vector &CommandLine, StringRef CWD, llvm::Optional ModuleName) { /// Prints out all of the gathered dependencies into a string. class MakeDependencyPrinterConsumer : public DependencyConsumer { public: 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 {} 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); } private: std::unique_ptr Opts; std::vector Dependencies; }; 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::getFullDependencies( const std::vector &CommandLine, StringRef CWD, const llvm::StringSet<> &AlreadySeen, llvm::Optional ModuleName) { class FullDependencyPrinterConsumer : public DependencyConsumer { public: FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) : AlreadySeen(AlreadySeen) {} void handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {} void handleFileDependency(StringRef File) override { Dependencies.push_back(std::string(File)); } void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { PrebuiltModuleDeps.emplace_back(std::move(PMD)); } void handleModuleDependency(ModuleDeps MD) override { ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD); } void handleContextHash(std::string Hash) override { ContextHash = std::move(Hash); } FullDependenciesResult getFullDependencies( const std::vector &OriginalCommandLine) const { FullDependencies FD; FD.OriginalCommandLine = ArrayRef(OriginalCommandLine).slice(1); FD.ID.ContextHash = std::move(ContextHash); FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); for (auto &&M : ClangModuleDeps) { auto &MD = M.second; if (MD.ImportedByMainFile) FD.ClangModuleDeps.push_back(MD.ID); } 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; } private: std::vector Dependencies; std::vector PrebuiltModuleDeps; llvm::MapVector> ClangModuleDeps; std::string ContextHash; std::vector OutputPaths; const llvm::StringSet<> &AlreadySeen; }; FullDependencyPrinterConsumer Consumer(AlreadySeen); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); return Consumer.getFullDependencies(CommandLine); }