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">;
/// \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;
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
/// {
/// \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;
#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 {
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,
LangStandards.cpp
LayoutOverrideSource.cpp
LogDiagnosticPrinter.cpp
+ ModuleDependencyCollector.cpp
MultiplexConsumer.cpp
PrintPreprocessedOutput.cpp
SerializedDiagnosticPrinter.cpp
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,
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)
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 =
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,
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,
--- /dev/null
+//===--- 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;
+}
--- /dev/null
+// 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;
--- /dev/null
+// 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;