[clang] 33a745e - [clang][clang-scan-deps] Add support for extracting full module dependencies.
Michael Spencer via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 30 16:03:24 PDT 2019
On Wed, Oct 30, 2019 at 3:54 PM Reid Kleckner via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
> I XFAILED the test on Windows in 52194350cfe. The [/\\] regexes might need
> to match two slashes, and the PREFIX variable ends up having inconsistent
> slash direction.
>
Hmm, fixing the slashes should be easy, but I'm not sure there's a good way
to get the PREFIX right. I feel like FileCheck needs an ignore slash
direction mode.
- Michael Spencer
>
> On Wed, Oct 30, 2019 at 3:27 PM Michael Spencer via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>>
>> Author: Michael Spencer
>> Date: 2019-10-30T15:27:27-07:00
>> New Revision: 33a745e6fe7e81d3793f7831d2832aa0785ef327
>>
>> URL:
>> https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327
>> DIFF:
>> https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327.diff
>>
>> LOG: [clang][clang-scan-deps] Add support for extracting full module
>> dependencies.
>>
>> This is a recommit of d8a4ef0e685c with the nondeterminism fixed.
>>
>> 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
>>
>> Added:
>> clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
>> clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
>> clang/test/ClangScanDeps/modules-full.cpp
>>
>> Modified:
>>
>> clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
>>
>> clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
>>
>> clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
>> clang/lib/Tooling/DependencyScanning/CMakeLists.txt
>> clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
>> clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
>> clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
>> clang/tools/clang-scan-deps/ClangScanDeps.cpp
>>
>> Removed:
>>
>>
>>
>>
>> ################################################################################
>> diff --git
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
>> index fd8ed80b143c..76edf150dbee 100644
>> ---
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
>> +++
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
>> @@ -30,15 +30,30 @@ enum class ScanningMode {
>> MinimizedSourcePreprocessing
>> };
>>
>> +/// The format that is output by the dependency scanner.
>> +enum class ScanningOutputFormat {
>> + /// This is the Makefile compatible dep format. This will include all
>> of the
>> + /// deps necessary for an implicit modules build, but won't include any
>> + /// intermodule dependency information.
>> + Make,
>> +
>> + /// This outputs the full module dependency graph suitable for use for
>> + /// explicitly building modules.
>> + Full,
>> +};
>> +
>> /// The dependency scanning service contains the shared state that is
>> used by
>> /// the invidual dependency scanning workers.
>> class DependencyScanningService {
>> public:
>> - DependencyScanningService(ScanningMode Mode, bool ReuseFileManager =
>> true,
>> + DependencyScanningService(ScanningMode Mode, ScanningOutputFormat
>> Format,
>> + bool ReuseFileManager = true,
>> bool SkipExcludedPPRanges = true);
>>
>> ScanningMode getMode() const { return Mode; }
>>
>> + ScanningOutputFormat getFormat() const { return Format; }
>> +
>> bool canReuseFileManager() const { return ReuseFileManager; }
>>
>> bool canSkipExcludedPPRanges() const { return SkipExcludedPPRanges; }
>> @@ -49,6 +64,7 @@ class DependencyScanningService {
>>
>> private:
>> const ScanningMode Mode;
>> + const ScanningOutputFormat Format;
>> const bool ReuseFileManager;
>> /// Set to true to use the preprocessor optimization that skips
>> excluded PP
>> /// ranges by bumping the buffer pointer in the lexer instead of
>> lexing the
>>
>> diff --git
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
>> index c950cbe167cd..78b49e4fa0c5 100644
>> ---
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
>> +++
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
>> @@ -40,6 +40,7 @@ class DependencyScanningTool {
>> StringRef CWD);
>>
>> private:
>> + const ScanningOutputFormat Format;
>> DependencyScanningWorker Worker;
>> const tooling::CompilationDatabase &Compilations;
>> };
>>
>> diff --git
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
>> index 45c9fb4f029d..689119330c41 100644
>> ---
>> a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
>> +++
>> b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
>> @@ -15,6 +15,8 @@
>> #include "clang/Frontend/PCHContainerOperations.h"
>> #include
>> "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"
>> #include "clang/Tooling/CompilationDatabase.h"
>> +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
>> +#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
>> #include "llvm/Support/Error.h"
>> #include "llvm/Support/FileSystem.h"
>> #include <string>
>> @@ -26,7 +28,6 @@ class DependencyOutputOptions;
>> namespace tooling {
>> namespace dependencies {
>>
>> -class DependencyScanningService;
>> class DependencyScanningWorkerFilesystem;
>>
>> class DependencyConsumer {
>> @@ -36,7 +37,9 @@ class DependencyConsumer {
>> virtual void handleFileDependency(const DependencyOutputOptions &Opts,
>> StringRef Filename) = 0;
>>
>> - // FIXME: Add support for reporting modular dependencies.
>> + virtual void handleModuleDependency(ModuleDeps MD) = 0;
>> +
>> + virtual void handleContextHash(std::string Hash) = 0;
>> };
>>
>> /// An individual dependency scanning worker that is able to run on its
>> own
>> @@ -73,6 +76,7 @@ class DependencyScanningWorker {
>> /// The file manager that is reused accross multiple invocations by
>> this
>> /// worker. If null, the file manager will not be reused.
>> llvm::IntrusiveRefCntPtr<FileManager> Files;
>> + ScanningOutputFormat Format;
>> };
>>
>> } // end namespace dependencies
>>
>> diff --git
>> a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
>> b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
>> new file mode 100644
>> index 000000000000..7a9fc276fcaa
>> --- /dev/null
>> +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
>> @@ -0,0 +1,94 @@
>> +//===- ModuleDepCollector.h - 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.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
>> +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
>> +
>> +#include "clang/Basic/LLVM.h"
>> +#include "clang/Basic/SourceManager.h"
>> +#include "clang/Frontend/Utils.h"
>> +#include "clang/Lex/HeaderSearch.h"
>> +#include "clang/Lex/PPCallbacks.h"
>> +#include "clang/Serialization/ASTReader.h"
>> +#include "llvm/ADT/DenseMap.h"
>> +#include "llvm/ADT/StringSet.h"
>> +#include "llvm/Support/raw_ostream.h"
>> +
>> +#include <string>
>> +
>> +namespace clang {
>> +namespace tooling {
>> +namespace dependencies {
>> +
>> +class DependencyConsumer;
>> +
>> +struct ModuleDeps {
>> + std::string ModuleName;
>> + std::string ClangModuleMapFile;
>> + std::string ModulePCMPath;
>> + std::string ContextHash;
>> + llvm::StringSet<> FileDeps;
>> + llvm::StringSet<> ClangModuleDeps;
>> + bool ImportedByMainFile = false;
>> +};
>> +
>> +class ModuleDepCollector;
>> +
>> +class ModuleDepCollectorPP final : public PPCallbacks {
>> +public:
>> + ModuleDepCollectorPP(CompilerInstance &I, ModuleDepCollector &MDC)
>> + : Instance(I), MDC(MDC) {}
>> +
>> + void FileChanged(SourceLocation Loc, FileChangeReason Reason,
>> + SrcMgr::CharacteristicKind FileType,
>> + FileID PrevFID) override;
>> + void 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) override;
>> +
>> + void EndOfMainFile() override;
>> +
>> +private:
>> + CompilerInstance &Instance;
>> + ModuleDepCollector &MDC;
>> + llvm::DenseSet<const Module *> DirectDeps;
>> +
>> + void handleTopLevelModule(const Module *M);
>> + void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD);
>> + void addModuleDep(const Module *M, ModuleDeps &MD);
>> +
>> + void addDirectDependencies(const Module *Mod);
>> +};
>> +
>> +class ModuleDepCollector final : public DependencyCollector {
>> +public:
>> + ModuleDepCollector(CompilerInstance &I, DependencyConsumer &C);
>> +
>> + void attachToPreprocessor(Preprocessor &PP) override;
>> + void attachToASTReader(ASTReader &R) override;
>> +
>> +private:
>> + friend ModuleDepCollectorPP;
>> +
>> + CompilerInstance &Instance;
>> + DependencyConsumer &Consumer;
>> + std::string MainFile;
>> + std::string ContextHash;
>> + std::vector<std::string> MainDeps;
>> + std::unordered_map<std::string, ModuleDeps> Deps;
>> +};
>> +
>> +} // end namespace dependencies
>> +} // end namespace tooling
>> +} // end namespace clang
>> +
>> +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
>>
>> diff --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
>> b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
>> index 05e1aa54f8d4..c6fe207ab2f2 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 e5cebe381000..93bb0cde439d 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 d2af1a9d110c..bffd7c338124 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 toJSONSorted(const llvm::StringSet<> &Set) {
>> + std::vector<llvm::StringRef> Strings;
>> + for (auto &&I : Set)
>> + Strings.push_back(I.getKey());
>> + std::sort(Strings.begin(), Strings.end());
>> + return llvm::json::Array(Strings);
>> +}
>>
>> 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", toJSONSorted(MD.FileDeps)},
>> + {"clang-module-deps", toJSONSorted(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 f382c202f8c2..edf2cf8bd70f 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 @@ class DependencyScanningAction : public
>> tooling::ToolAction {
>> 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 @@ class DependencyScanningAction : public
>> tooling::ToolAction {
>> // 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 @@ class DependencyScanningAction : public
>> tooling::ToolAction {
>> 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 000000000000..7f20ec7056c6
>> --- /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) {}
>>
>> diff --git a/clang/test/ClangScanDeps/modules-full.cpp
>> b/clang/test/ClangScanDeps/modules-full.cpp
>> new file mode 100644
>> index 000000000000..f8bff06d8f74
>> --- /dev/null
>> +++ b/clang/test/ClangScanDeps/modules-full.cpp
>> @@ -0,0 +1,74 @@
>> +// RUN: rm -rf %t.dir
>> +// RUN: rm -rf %t.cdb
>> +// RUN: rm -rf %t.module-cache
>> +// RUN: mkdir -p %t.dir
>> +// RUN: cp %s %t.dir/modules_cdb_input.cpp
>> +// RUN: cp %s %t.dir/modules_cdb_input2.cpp
>> +// RUN: mkdir %t.dir/Inputs
>> +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h
>> +// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h
>> +// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap
>> +// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb.json > %t.cdb
>> +//
>> +// RUN: echo %t.dir > %t.result
>> +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 \
>> +// RUN: -mode preprocess-minimized-sources -format experimental-full
>> >> %t.result
>> +// RUN: cat %t.result | FileCheck --check-prefixes=CHECK %s
>> +
>> +#include "header.h"
>> +
>> +// CHECK: [[PREFIX:(.*[/\\])+[a-zA-Z0-9.-]+]]
>> +// CHECK-NEXT: {
>> +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH:[A-Z0-9]+]]",
>> +// CHECK-NEXT: "clang-module-deps": [
>> +// CHECK-NEXT: "header1"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "clang-modules": [
>> +// CHECK-NEXT: {
>> +// CHECK-NEXT: "clang-module-deps": [
>> +// CHECK-NEXT: "header2"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "clang-modulemap-file":
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
>> +// CHECK-NEXT: "file-deps": [
>> +// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h",
>> +// CHECK-NEXT:
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "name": "header1"
>> +// CHECK-NEXT: },
>> +// CHECK-NEXT: {
>> +// CHECK-NEXT: "clang-module-deps": [],
>> +// CHECK-NEXT: "clang-modulemap-file":
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
>> +// CHECK-NEXT: "file-deps": [
>> +// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header2.h",
>> +// CHECK-NEXT:
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "name": "header2"
>> +// CHECK-NEXT: }
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "file-deps": [
>> +// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp"
>> +// CHECK-NEXT:},
>> +// CHECK-NEXT:{
>> +// CHECK-NOT: "clang-context-hash": "[[CONTEXT_HASH]]",
>> +// CHECK-NEXT: "clang-context-hash": "{{[A-Z0-9]+}}",
>> +// CHECK-NEXT: "clang-module-deps": [
>> +// CHECK-NEXT: "header1"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "clang-modules": [
>> +// CHECK-NEXT: {
>> +// CHECK-NEXT: "clang-module-deps": [],
>> +// CHECK-NEXT: "clang-modulemap-file":
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
>> +// CHECK-NEXT: "file-deps": [
>> +// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h",
>> +// CHECK-NEXT:
>> "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "name": "header1"
>> +// CHECK-NEXT: }
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "file-deps": [
>> +// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp"
>> +// CHECK-NEXT: ],
>> +// CHECK-NEXT: "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp"
>> +// CHECK-NEXT:},
>>
>> diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
>> b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
>> index d57983ed1664..a6abb4a9600b 100644
>> --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
>> +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
>> @@ -59,6 +59,17 @@ static llvm::cl::opt<ScanningMode> ScanMode(
>> llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing),
>> llvm::cl::cat(DependencyScannerCategory));
>>
>> +static llvm::cl::opt<ScanningOutputFormat> Format(
>> + "format", llvm::cl::desc("The output format for the dependencies"),
>> + llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make",
>> + "Makefile compatible dep file"),
>> + clEnumValN(ScanningOutputFormat::Full,
>> "experimental-full",
>> + "Full dependency graph suitable"
>> + " for explicitly building modules. This
>> format "
>> + "is experimental and will change.")),
>> + llvm::cl::init(ScanningOutputFormat::Make),
>> + llvm::cl::cat(DependencyScannerCategory));
>> +
>> llvm::cl::opt<unsigned>
>> NumThreads("j", llvm::cl::Optional,
>> llvm::cl::desc("Number of worker threads to use (default:
>> use "
>> @@ -200,7 +211,7 @@ int main(int argc, const char **argv) {
>> // Print out the dependency results to STDOUT by default.
>> SharedStream DependencyOS(llvm::outs());
>>
>> - DependencyScanningService Service(ScanMode, ReuseFileManager,
>> + DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
>> SkipExcludedPPRanges);
>> #if LLVM_ENABLE_THREADS
>> unsigned NumWorkers =
>>
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20191030/cc723bd5/attachment-0001.html>
More information about the cfe-commits
mailing list