diff options
| author | Michael Spencer <bigcheesegs@gmail.com> | 2019-10-16 12:28:35 -0700 |
|---|---|---|
| committer | Michael Spencer <bigcheesegs@gmail.com> | 2019-10-30 14:46:17 -0700 |
| commit | d8a4ef0e685cec1fc73d4953b48220b649d05b40 (patch) | |
| tree | c03716a6caf9dd61b8bbd337589974c860cd2a5c /clang/lib/Tooling/DependencyScanning | |
| parent | edca9ac0de3a5c10a21ef0c725501ea35791006a (diff) | |
| download | bcm5719-llvm-d8a4ef0e685cec1fc73d4953b48220b649d05b40.tar.gz bcm5719-llvm-d8a4ef0e685cec1fc73d4953b48220b649d05b40.zip | |
[clang][clang-scan-deps] Add support for extracting full module dependencies.
This adds experimental support for extracting a Clang module dependency graph
from a compilation database. The output format is experimental and will change.
It is currently a concatenation of JSON outputs for each compilation. Future
patches will change this to deduplicate modules between compilations.
Differential Revision: https://reviews.llvm.org/D69420
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning')
5 files changed, 266 insertions, 21 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt index 05e1aa54f8d..c6fe207ab2f 100644 --- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt +++ b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt @@ -8,6 +8,7 @@ add_clang_library(clangDependencyScanning DependencyScanningService.cpp DependencyScanningWorker.cpp DependencyScanningTool.cpp + ModuleDepCollector.cpp DEPENDS ClangDriverOptions diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp index e5cebe38100..93bb0cde439 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp @@ -12,8 +12,8 @@ using namespace clang; using namespace tooling; using namespace dependencies; -DependencyScanningService::DependencyScanningService(ScanningMode Mode, - bool ReuseFileManager, - bool SkipExcludedPPRanges) - : Mode(Mode), ReuseFileManager(ReuseFileManager), +DependencyScanningService::DependencyScanningService( + ScanningMode Mode, ScanningOutputFormat Format, bool ReuseFileManager, + bool SkipExcludedPPRanges) + : Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager), SkipExcludedPPRanges(SkipExcludedPPRanges) {} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index d2af1a9d110..927a04c7cc1 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -8,6 +8,15 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "clang/Frontend/Utils.h" +#include "llvm/Support/JSON.h" + +static llvm::json::Array toJSON(const llvm::StringSet<> &Set) { + llvm::json::Array Ret; + for (auto &&I : Set) { + Ret.push_back(std::string(I.getKey())); + } + return Ret; +} namespace clang{ namespace tooling{ @@ -16,13 +25,14 @@ namespace dependencies{ DependencyScanningTool::DependencyScanningTool( DependencyScanningService &Service, const tooling::CompilationDatabase &Compilations) - : Worker(Service), Compilations(Compilations) {} + : Format(Service.getFormat()), Worker(Service), Compilations(Compilations) { +} llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(const std::string &Input, StringRef CWD) { /// Prints out all of the gathered dependencies into a string. - class DependencyPrinterConsumer : public DependencyConsumer { + class MakeDependencyPrinterConsumer : public DependencyConsumer { public: void handleFileDependency(const DependencyOutputOptions &Opts, StringRef File) override { @@ -31,6 +41,14 @@ DependencyScanningTool::getDependencyFile(const std::string &Input, Dependencies.push_back(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; @@ -59,14 +77,88 @@ DependencyScanningTool::getDependencyFile(const std::string &Input, std::vector<std::string> Dependencies; }; - DependencyPrinterConsumer Consumer; - auto Result = - Worker.computeDependencies(Input, CWD, Compilations, Consumer); - if (Result) - return std::move(Result); - std::string Output; - Consumer.printDependencies(Output); - return Output; + class FullDependencyPrinterConsumer : public DependencyConsumer { + public: + void handleFileDependency(const DependencyOutputOptions &Opts, + StringRef File) override { + Dependencies.push_back(File); + } + + void handleModuleDependency(ModuleDeps MD) override { + ModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD); + } + + void handleContextHash(std::string Hash) override { + ContextHash = std::move(Hash); + } + + void printDependencies(std::string &S, StringRef MainFile) { + // Sort the modules by name to get a deterministic order. + std::vector<StringRef> Modules; + for (auto &&Dep : ModuleDeps) + Modules.push_back(Dep.first); + std::sort(Modules.begin(), Modules.end()); + + llvm::raw_string_ostream OS(S); + + using namespace llvm::json; + + Array Imports; + for (auto &&ModName : Modules) { + auto &MD = ModuleDeps[ModName]; + if (MD.ImportedByMainFile) + Imports.push_back(MD.ModuleName); + } + + Array Mods; + for (auto &&ModName : Modules) { + auto &MD = ModuleDeps[ModName]; + Object Mod{ + {"name", MD.ModuleName}, + {"file-deps", toJSON(MD.FileDeps)}, + {"clang-module-deps", toJSON(MD.ClangModuleDeps)}, + {"clang-modulemap-file", MD.ClangModuleMapFile}, + }; + Mods.push_back(std::move(Mod)); + } + + Object O{ + {"input-file", MainFile}, + {"clang-context-hash", ContextHash}, + {"file-deps", Dependencies}, + {"clang-module-deps", std::move(Imports)}, + {"clang-modules", std::move(Mods)}, + }; + + S = llvm::formatv("{0:2},\n", Value(std::move(O))).str(); + return; + } + + private: + std::vector<std::string> Dependencies; + std::unordered_map<std::string, ModuleDeps> ModuleDeps; + std::string ContextHash; + }; + + if (Format == ScanningOutputFormat::Make) { + MakeDependencyPrinterConsumer Consumer; + auto Result = + Worker.computeDependencies(Input, CWD, Compilations, Consumer); + if (Result) + return std::move(Result); + std::string Output; + Consumer.printDependencies(Output); + return Output; + } else { + FullDependencyPrinterConsumer Consumer; + auto Result = + Worker.computeDependencies(Input, CWD, Compilations, Consumer); + if (Result) + return std::move(Result); + std::string Output; + Consumer.printDependencies(Output, Input); + return Output; + } } } // end namespace dependencies diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index f382c202f8c..edf2cf8bd70 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -14,6 +14,7 @@ #include "clang/Frontend/Utils.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" #include "clang/Tooling/Tooling.h" using namespace clang; @@ -72,9 +73,11 @@ public: DependencyScanningAction( StringRef WorkingDirectory, DependencyConsumer &Consumer, llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS, - ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) + ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings, + ScanningOutputFormat Format) : WorkingDirectory(WorkingDirectory), Consumer(Consumer), - DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings) {} + DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), + Format(Format) {} bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, FileManager *FileMgr, @@ -131,9 +134,20 @@ public: // We need at least one -MT equivalent for the generator to work. if (Opts->Targets.empty()) Opts->Targets = {"clang-scan-deps dependency"}; - Compiler.addDependencyCollector( - std::make_shared<DependencyConsumerForwarder>(std::move(Opts), - Consumer)); + + switch (Format) { + case ScanningOutputFormat::Make: + Compiler.addDependencyCollector( + std::make_shared<DependencyConsumerForwarder>(std::move(Opts), + Consumer)); + break; + case ScanningOutputFormat::Full: + Compiler.addDependencyCollector( + std::make_shared<ModuleDepCollector>(Compiler, Consumer)); + break; + } + + Consumer.handleContextHash(Compiler.getInvocation().getModuleHash()); auto Action = std::make_unique<PreprocessOnlyAction>(); const bool Result = Compiler.ExecuteAction(*Action); @@ -147,12 +161,14 @@ private: DependencyConsumer &Consumer; llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings; + ScanningOutputFormat Format; }; } // end anonymous namespace DependencyScanningWorker::DependencyScanningWorker( - DependencyScanningService &Service) { + DependencyScanningService &Service) + : Format(Service.getFormat()) { DiagOpts = new DiagnosticOptions(); PCHContainerOps = std::make_shared<PCHContainerOperations>(); RealFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem()); @@ -195,7 +211,7 @@ llvm::Error DependencyScanningWorker::computeDependencies( Tool.setPrintErrorMessage(false); Tool.setDiagnosticConsumer(&DC); DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, - PPSkipMappings.get()); + PPSkipMappings.get(), Format); return !Tool.run(&Action); }); } diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp new file mode 100644 index 00000000000..7f20ec7056c --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -0,0 +1,136 @@ +//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" + +using namespace clang; +using namespace tooling; +using namespace dependencies; + +void ModuleDepCollectorPP::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + if (Reason != PPCallbacks::EnterFile) + return; + + SourceManager &SM = Instance.getSourceManager(); + + // Dependency generation really does want to go all the way to the + // file entry for a source location to find out what is depended on. + // We do not want #line markers to affect dependency generation! + Optional<FileEntryRef> File = + SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(Loc))); + if (!File) + return; + + StringRef FileName = + llvm::sys::path::remove_leading_dotslash(File->getName()); + + MDC.MainDeps.push_back(FileName); +} + +void ModuleDepCollectorPP::InclusionDirective( + SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) { + if (!File && !Imported) { + // This is a non-modular include that HeaderSearch failed to find. Add it + // here as `FileChanged` will never see it. + MDC.MainDeps.push_back(FileName); + } + + if (!Imported) + return; + + MDC.Deps[MDC.ContextHash + Imported->getTopLevelModule()->getFullModuleName()] + .ImportedByMainFile = true; + DirectDeps.insert(Imported->getTopLevelModule()); +} + +void ModuleDepCollectorPP::EndOfMainFile() { + FileID MainFileID = Instance.getSourceManager().getMainFileID(); + MDC.MainFile = + Instance.getSourceManager().getFileEntryForID(MainFileID)->getName(); + + for (const Module *M : DirectDeps) { + handleTopLevelModule(M); + } + + for (auto &&I : MDC.Deps) + MDC.Consumer.handleModuleDependency(I.second); + + DependencyOutputOptions Opts; + for (auto &&I : MDC.MainDeps) + MDC.Consumer.handleFileDependency(Opts, I); +} + +void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { + assert(M == M->getTopLevelModule() && "Expected top level module!"); + + auto ModI = MDC.Deps.insert( + std::make_pair(MDC.ContextHash + M->getFullModuleName(), ModuleDeps{})); + + if (!ModI.first->second.ModuleName.empty()) + return; + + ModuleDeps &MD = ModI.first->second; + + const FileEntry *ModuleMap = Instance.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .getContainingModuleMapFile(M); + + MD.ClangModuleMapFile = ModuleMap ? ModuleMap->getName() : ""; + MD.ModuleName = M->getFullModuleName(); + MD.ModulePCMPath = M->getASTFile()->getName(); + MD.ContextHash = MDC.ContextHash; + serialization::ModuleFile *MF = + MDC.Instance.getModuleManager()->getModuleManager().lookup( + M->getASTFile()); + MDC.Instance.getModuleManager()->visitInputFiles( + *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) { + MD.FileDeps.insert(IF.getFile()->getName()); + }); + + addAllSubmoduleDeps(M, MD); +} + +void ModuleDepCollectorPP::addAllSubmoduleDeps(const Module *M, + ModuleDeps &MD) { + addModuleDep(M, MD); + + for (const Module *SubM : M->submodules()) + addAllSubmoduleDeps(SubM, MD); +} + +void ModuleDepCollectorPP::addModuleDep(const Module *M, ModuleDeps &MD) { + for (const Module *Import : M->Imports) { + if (Import->getTopLevelModule() != M->getTopLevelModule()) { + MD.ClangModuleDeps.insert(Import->getTopLevelModuleName()); + handleTopLevelModule(Import->getTopLevelModule()); + } + } +} + +ModuleDepCollector::ModuleDepCollector(CompilerInstance &I, + DependencyConsumer &C) + : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash()) { +} + +void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { + PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this)); +} + +void ModuleDepCollector::attachToASTReader(ASTReader &R) {} |

