summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Driver/Options.td2
-rw-r--r--clang/include/clang/Frontend/CompilerInstance.h7
-rw-r--r--clang/include/clang/Frontend/DependencyOutputOptions.h5
-rw-r--r--clang/include/clang/Frontend/Utils.h26
-rw-r--r--clang/lib/Frontend/CMakeLists.txt1
-rw-r--r--clang/lib/Frontend/CompilerInstance.cpp22
-rw-r--r--clang/lib/Frontend/CompilerInvocation.cpp2
-rw-r--r--clang/lib/Frontend/ModuleDependencyCollector.cpp109
-rw-r--r--clang/test/Modules/dependency-dump-dependent-module.m27
-rw-r--r--clang/test/Modules/dependency-dump.m25
10 files changed, 225 insertions, 1 deletions
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 85f2e0c740b..dd8722e1b4e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -342,6 +342,8 @@ def dependency_file : Separate<["-"], "dependency-file">, Flags<[CC1Option]>,
HelpText<"Filename (or -) to write dependency output to">;
def dependency_dot : Separate<["-"], "dependency-dot">, Flags<[CC1Option]>,
HelpText<"Filename to write DOT-formatted header dependencies to">;
+def module_dependency_dir : Separate<["-"], "module-dependency-dir">,
+ Flags<[CC1Option]>, HelpText<"Directory to dump module dependencies to">;
def dumpmachine : Flag<["-"], "dumpmachine">;
def dumpspecs : Flag<["-"], "dumpspecs">, Flags<[Unsupported]>;
def dumpversion : Flag<["-"], "dumpversion">;
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index b144069b975..d72f9046cfa 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -105,6 +105,9 @@ class CompilerInstance : public ModuleLoader {
/// \brief The ASTReader, if one exists.
IntrusiveRefCntPtr<ASTReader> ModuleManager;
+ /// \brief The module dependency collector for crashdumps
+ std::shared_ptr<ModuleDependencyCollector> ModuleDepCollector;
+
/// \brief The dependency file generator.
std::unique_ptr<DependencyFileGenerator> TheDependencyFileGenerator;
@@ -464,6 +467,10 @@ public:
IntrusiveRefCntPtr<ASTReader> getModuleManager() const;
void setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader);
+ std::shared_ptr<ModuleDependencyCollector> getModuleDepCollector() const;
+ void setModuleDepCollector(
+ std::shared_ptr<ModuleDependencyCollector> Collector);
+
/// }
/// @name Code Completion
/// {
diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h
index d275249987f..5da14597b64 100644
--- a/clang/include/clang/Frontend/DependencyOutputOptions.h
+++ b/clang/include/clang/Frontend/DependencyOutputOptions.h
@@ -43,7 +43,10 @@ public:
/// \brief The file to write GraphViz-formatted header dependencies to.
std::string DOTOutputFile;
-
+
+ /// \brief The directory to copy module dependencies to when collecting them.
+ std::string ModuleDependencyOutputDir;
+
public:
DependencyOutputOptions() {
IncludeSystemHeaders = 0;
diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h
index 8fb536ff574..a791cd55dc4 100644
--- a/clang/include/clang/Frontend/Utils.h
+++ b/clang/include/clang/Frontend/Utils.h
@@ -15,8 +15,10 @@
#define LLVM_CLANG_FRONTEND_UTILS_H
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/VirtualFileSystem.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Option/OptSpecifier.h"
namespace llvm {
@@ -80,6 +82,30 @@ public:
void AttachToASTReader(ASTReader &R);
};
+/// Collects the dependencies for imported modules into a directory. Users
+/// should attach to the AST reader whenever a module is loaded.
+class ModuleDependencyCollector {
+ std::string DestDir;
+ bool HasErrors;
+ llvm::StringSet<> Seen;
+ vfs::YAMLVFSWriter VFSWriter;
+
+public:
+ StringRef getDest() { return DestDir; }
+ bool insertSeen(StringRef Filename) { return Seen.insert(Filename); }
+ void setHasErrors() { HasErrors = true; }
+ void addFileMapping(StringRef VPath, StringRef RPath) {
+ VFSWriter.addFileMapping(VPath, RPath);
+ }
+
+ void attachToASTReader(ASTReader &R);
+ void writeFileMap();
+ bool hasErrors() { return HasErrors; }
+ ModuleDependencyCollector(std::string DestDir)
+ : DestDir(DestDir), HasErrors(false) {}
+ ~ModuleDependencyCollector() { writeFileMap(); }
+};
+
/// AttachDependencyGraphGen - Create a dependency graph generator, and attach
/// it to the given preprocessor.
void AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt
index b67e0aed24c..3fa7a2cf01b 100644
--- a/clang/lib/Frontend/CMakeLists.txt
+++ b/clang/lib/Frontend/CMakeLists.txt
@@ -25,6 +25,7 @@ add_clang_library(clangFrontend
LangStandards.cpp
LayoutOverrideSource.cpp
LogDiagnosticPrinter.cpp
+ ModuleDependencyCollector.cpp
MultiplexConsumer.cpp
PrintPreprocessedOutput.cpp
SerializedDiagnosticPrinter.cpp
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index e8ca0804090..03a2c229d0b 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -116,6 +116,16 @@ void CompilerInstance::setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader) {
ModuleManager = Reader;
}
+std::shared_ptr<ModuleDependencyCollector>
+CompilerInstance::getModuleDepCollector() const {
+ return ModuleDepCollector;
+}
+
+void CompilerInstance::setModuleDepCollector(
+ std::shared_ptr<ModuleDependencyCollector> Collector) {
+ ModuleDepCollector = Collector;
+}
+
// Diagnostics
static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts,
const CodeGenOptions *CodeGenOpts,
@@ -278,6 +288,11 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile,
getHeaderSearchOpts().Sysroot);
+ // If we don't have a collector, but we are collecting module dependencies,
+ // then we're the top level compiler instance and need to create one.
+ if (!ModuleDepCollector && !DepOpts.ModuleDependencyOutputDir.empty())
+ ModuleDepCollector = std::make_shared<ModuleDependencyCollector>(
+ DepOpts.ModuleDependencyOutputDir);
// Handle generating header include information, if requested.
if (DepOpts.ShowHeaderIncludes)
@@ -851,6 +866,10 @@ static void compileModuleImpl(CompilerInstance &ImportingInstance,
SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(),
FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
+ // If we're collecting module dependencies, we need to share a collector
+ // between all of the module CompilerInstances.
+ Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector());
+
// Get or create the module map that we'll use to build this module.
std::string InferredModuleMapContent;
if (const FileEntry *ModuleMapFile =
@@ -1211,6 +1230,9 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
if (TheDependencyFileGenerator)
TheDependencyFileGenerator->AttachToASTReader(*ModuleManager);
+ if (ModuleDepCollector)
+ ModuleDepCollector->attachToASTReader(*ModuleManager);
+
// Try to load the module file.
unsigned ARRFlags = ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing;
switch (ModuleManager->ReadAST(ModuleFileName, serialization::MK_Module,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 6d5faf935e9..beaa092c1e5 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -578,6 +578,8 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG);
Opts.PrintShowIncludes = Args.hasArg(OPT_show_includes);
Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot);
+ Opts.ModuleDependencyOutputDir =
+ Args.getLastArgValue(OPT_module_dependency_dir);
}
bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
diff --git a/clang/lib/Frontend/ModuleDependencyCollector.cpp b/clang/lib/Frontend/ModuleDependencyCollector.cpp
new file mode 100644
index 00000000000..8e4402b8eda
--- /dev/null
+++ b/clang/lib/Frontend/ModuleDependencyCollector.cpp
@@ -0,0 +1,109 @@
+//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Collect the dependencies of a set of modules.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/Utils.h"
+#include "clang/Serialization/ASTReader.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Filesystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace {
+/// Private implementation for ModuleDependencyCollector
+class ModuleDependencyListener : public ASTReaderListener {
+ ModuleDependencyCollector &Collector;
+
+ std::error_code copyToRoot(StringRef Src);
+public:
+ ModuleDependencyListener(ModuleDependencyCollector &Collector)
+ : Collector(Collector) {}
+ bool needsInputFileVisitation() override { return true; }
+ bool needsSystemInputFileVisitation() override { return true; }
+ bool visitInputFile(StringRef Filename, bool IsSystem,
+ bool IsOverridden) override;
+};
+}
+
+void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
+ R.addListener(new ModuleDependencyListener(*this));
+}
+
+void ModuleDependencyCollector::writeFileMap() {
+ if (Seen.empty())
+ return;
+
+ SmallString<256> Dest = getDest();
+ llvm::sys::path::append(Dest, "vfs.yaml");
+
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream OS(Dest.c_str(), ErrorInfo, llvm::sys::fs::F_Text);
+ if (!ErrorInfo.empty()) {
+ setHasErrors();
+ return;
+ }
+ VFSWriter.write(OS);
+}
+
+/// Append the absolute path in Nested to the path given by Root. This will
+/// remove directory traversal from the resulting nested path.
+static void appendNestedPath(SmallVectorImpl<char> &Root, StringRef Nested) {
+ using namespace llvm::sys;
+ SmallVector<StringRef, 16> ComponentStack;
+
+ StringRef Rel = path::relative_path(Nested);
+ for (StringRef C : llvm::make_range(path::begin(Rel), path::end(Rel))) {
+ if (C == ".")
+ continue;
+ if (C == "..") {
+ assert(ComponentStack.size() && "Path traverses out of parent");
+ ComponentStack.pop_back();
+ } else
+ ComponentStack.push_back(C);
+ }
+ // The stack is now the path without any directory traversal.
+ for (StringRef C : ComponentStack)
+ path::append(Root, C);
+}
+
+std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) {
+ using namespace llvm::sys;
+
+ // We need an absolute path to append to the root.
+ SmallString<256> AbsoluteSrc = Src;
+ fs::make_absolute(AbsoluteSrc);
+ // Build the destination path.
+ SmallString<256> Dest = Collector.getDest();
+ size_t RootLen = Dest.size();
+ appendNestedPath(Dest, AbsoluteSrc);
+
+ // Copy the file into place.
+ if (std::error_code EC = fs::create_directories(path::parent_path(Dest),
+ /*IgnoreExisting=*/true))
+ return EC;
+ if (std::error_code EC = fs::copy_file(AbsoluteSrc.str(), Dest.str()))
+ return EC;
+ // Use the absolute path under the root for the file mapping.
+ Collector.addFileMapping(Dest.substr(RootLen), Dest.str());
+ return std::error_code();
+}
+
+bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem,
+ bool IsOverridden) {
+ if (Collector.insertSeen(Filename))
+ if (copyToRoot(Filename))
+ Collector.setHasErrors();
+ return true;
+}
diff --git a/clang/test/Modules/dependency-dump-dependent-module.m b/clang/test/Modules/dependency-dump-dependent-module.m
new file mode 100644
index 00000000000..5308f7efba3
--- /dev/null
+++ b/clang/test/Modules/dependency-dump-dependent-module.m
@@ -0,0 +1,27 @@
+// When a module depends on another, check that we dump the dependency header
+// files for both.
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache -module-dependency-dir %t/vfs -F %S/Inputs -I %S/Inputs -verify %s
+// expected-no-diagnostics
+
+// RUN: FileCheck %s -check-prefix=VFS < %t/vfs/vfs.yaml
+// VFS: 'name': "AlsoDependsOnModule.h"
+// VFS: 'name': "SubFramework.h"
+// VFS: 'name': "Treasure.h"
+// VFS: 'name': "Module.h"
+// VFS: 'name': "Sub.h"
+// VFS: 'name': "Sub2.h"
+
+// TODO: We need shell to use find here. Is there a simpler way?
+// REQUIRES: shell
+
+// RUN: find %t/vfs -type f | FileCheck %s -check-prefix=DUMP
+// DUMP: AlsoDependsOnModule.framework/Headers/AlsoDependsOnModule.h
+// DUMP: Module.framework/Frameworks/SubFramework.framework/Headers/SubFramework.h
+// DUMP: Module.framework/Headers/Buried/Treasure.h
+// DUMP: Module.framework/Headers/Module.h
+// DUMP: Module.framework/Headers/Sub.h
+// DUMP: Module.framework/Headers/Sub2.h
+
+@import AlsoDependsOnModule;
diff --git a/clang/test/Modules/dependency-dump.m b/clang/test/Modules/dependency-dump.m
new file mode 100644
index 00000000000..58d6c1572da
--- /dev/null
+++ b/clang/test/Modules/dependency-dump.m
@@ -0,0 +1,25 @@
+// Check that we can dump all of the headers a module depends on, and a VFS map
+// for the same.
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache -module-dependency-dir %t/vfs -F %S/Inputs -I %S/Inputs -verify %s
+// expected-no-diagnostics
+
+// RUN: FileCheck %s -check-prefix=VFS -input-file %t/vfs/vfs.yaml
+// VFS: 'name': "SubFramework.h"
+// VFS: 'name': "Treasure.h"
+// VFS: 'name': "Module.h"
+// VFS: 'name': "Sub.h"
+// VFS: 'name': "Sub2.h"
+
+// TODO: We need shell to use find here. Is there a simpler way?
+// REQUIRES: shell
+
+// RUN: find %t/vfs -type f | FileCheck %s -check-prefix=DUMP
+// DUMP: Module.framework/Frameworks/SubFramework.framework/Headers/SubFramework.h
+// DUMP: Module.framework/Headers/Buried/Treasure.h
+// DUMP: Module.framework/Headers/Module.h
+// DUMP: Module.framework/Headers/Sub.h
+// DUMP: Module.framework/Headers/Sub2.h
+
+@import Module;
OpenPOWER on IntegriCloud