//===- 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" namespace clang{ namespace tooling{ namespace dependencies{ std::vector FullDependencies::getAdditionalArgs( std::function LookupPCMPath, std::function LookupModuleDeps) const { std::vector Ret{ "-fno-implicit-modules", "-fno-implicit-module-maps", }; std::vector PCMPaths; std::vector ModMapPaths; dependencies::detail::collectPCMAndModuleMapPaths( ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths); for (const std::string &PCMPath : PCMPaths) Ret.push_back("-fmodule-file=" + PCMPath); for (const std::string &ModMapPath : ModMapPaths) Ret.push_back("-fmodule-map-file=" + ModMapPath); return Ret; } std::vector FullDependencies::getAdditionalArgsWithoutModulePaths() const { return { "-fno-implicit-modules", "-fno-implicit-module-maps", }; } DependencyScanningTool::DependencyScanningTool( DependencyScanningService &Service) : Worker(Service) {} llvm::Expected DependencyScanningTool::getDependencyFile( const tooling::CompilationDatabase &Compilations, StringRef CWD) { /// Prints out all of the gathered dependencies into a string. class MakeDependencyPrinterConsumer : public DependencyConsumer { public: void handleFileDependency(const DependencyOutputOptions &Opts, StringRef File) override { if (!this->Opts) this->Opts = std::make_unique(Opts); Dependencies.push_back(std::string(File)); } 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) { if (!Opts) return; 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; }; // We expect a single command here because if a source file occurs multiple // times in the original CDB, then `computeDependencies` would run the // `DependencyScanningAction` once for every time the input occured in the // CDB. Instead we split up the CDB into single command chunks to avoid this // behavior. assert(Compilations.getAllCompileCommands().size() == 1 && "Expected a compilation database with a single command!"); std::string Input = Compilations.getAllCompileCommands().front().Filename; MakeDependencyPrinterConsumer Consumer; auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); if (Result) return std::move(Result); std::string Output; Consumer.printDependencies(Output); return Output; } llvm::Expected DependencyScanningTool::getFullDependencies( const tooling::CompilationDatabase &Compilations, StringRef CWD, const llvm::StringSet<> &AlreadySeen) { class FullDependencyPrinterConsumer : public DependencyConsumer { public: FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) : AlreadySeen(AlreadySeen) {} void handleFileDependency(const DependencyOutputOptions &Opts, StringRef File) override { Dependencies.push_back(std::string(File)); } 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 { FullDependencies FD; 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); } 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::unordered_map ClangModuleDeps; std::string ContextHash; std::vector OutputPaths; const llvm::StringSet<> &AlreadySeen; }; // We expect a single command here because if a source file occurs multiple // times in the original CDB, then `computeDependencies` would run the // `DependencyScanningAction` once for every time the input occured in the // CDB. Instead we split up the CDB into single command chunks to avoid this // behavior. assert(Compilations.getAllCompileCommands().size() == 1 && "Expected a compilation database with a single command!"); std::string Input = Compilations.getAllCompileCommands().front().Filename; FullDependencyPrinterConsumer Consumer(AlreadySeen); llvm::Error Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); if (Result) return std::move(Result); return Consumer.getFullDependencies(); } } // end namespace dependencies } // end namespace tooling } // end namespace clang