r211303 - Frontend: Add a CC1 flag to dump module dependencies to a directory

Justin Bogner mail at justinbogner.com
Thu Jun 19 12:36:03 PDT 2014


Author: bogner
Date: Thu Jun 19 14:36:03 2014
New Revision: 211303

URL: http://llvm.org/viewvc/llvm-project?rev=211303&view=rev
Log:
Frontend: Add a CC1 flag to dump module dependencies to a directory

This adds the -module-dependency-dir to clang -cc1, which specifies a
directory to copy all of a module's dependencies into in a form
suitable to be used as a VFS using -ivfsoverlay with the generated
vfs.yaml.

This is useful for crashdumps that involve modules, so that the module
dependencies will be intact when a crash report script is used to
reproduce a problem on another machine.

We currently encode the absolute path to the dump directory, due to
limitations in the VFS system. Until we can handle relative paths in
the VFS, users of the VFS map may need to run a simple search and
replace in the file.

Added:
    cfe/trunk/lib/Frontend/ModuleDependencyCollector.cpp
    cfe/trunk/test/Modules/dependency-dump-dependent-module.m
    cfe/trunk/test/Modules/dependency-dump.m
Modified:
    cfe/trunk/include/clang/Driver/Options.td
    cfe/trunk/include/clang/Frontend/CompilerInstance.h
    cfe/trunk/include/clang/Frontend/DependencyOutputOptions.h
    cfe/trunk/include/clang/Frontend/Utils.h
    cfe/trunk/lib/Frontend/CMakeLists.txt
    cfe/trunk/lib/Frontend/CompilerInstance.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp

Modified: cfe/trunk/include/clang/Driver/Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Options.td (original)
+++ cfe/trunk/include/clang/Driver/Options.td Thu Jun 19 14:36:03 2014
@@ -342,6 +342,8 @@ def dependency_file : Separate<["-"], "d
   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">;

Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
+++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Thu Jun 19 14:36:03 2014
@@ -105,6 +105,9 @@ class CompilerInstance : public ModuleLo
   /// \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
   /// {

Modified: cfe/trunk/include/clang/Frontend/DependencyOutputOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/DependencyOutputOptions.h?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/DependencyOutputOptions.h (original)
+++ cfe/trunk/include/clang/Frontend/DependencyOutputOptions.h Thu Jun 19 14:36:03 2014
@@ -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;

Modified: cfe/trunk/include/clang/Frontend/Utils.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/Utils.h?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/Utils.h (original)
+++ cfe/trunk/include/clang/Frontend/Utils.h Thu Jun 19 14:36:03 2014
@@ -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,

Modified: cfe/trunk/lib/Frontend/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CMakeLists.txt?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CMakeLists.txt (original)
+++ cfe/trunk/lib/Frontend/CMakeLists.txt Thu Jun 19 14:36:03 2014
@@ -25,6 +25,7 @@ add_clang_library(clangFrontend
   LangStandards.cpp
   LayoutOverrideSource.cpp
   LogDiagnosticPrinter.cpp
+  ModuleDependencyCollector.cpp
   MultiplexConsumer.cpp
   PrintPreprocessedOutput.cpp
   SerializedDiagnosticPrinter.cpp

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Thu Jun 19 14:36:03 2014
@@ -116,6 +116,16 @@ void CompilerInstance::setModuleManager(
   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::createPreprocesso
     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(CompilerIn
   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(SourceLocat
     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,

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=211303&r1=211302&r2=211303&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Thu Jun 19 14:36:03 2014
@@ -578,6 +578,8 @@ static void ParseDependencyOutputArgs(De
   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,

Added: cfe/trunk/lib/Frontend/ModuleDependencyCollector.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ModuleDependencyCollector.cpp?rev=211303&view=auto
==============================================================================
--- cfe/trunk/lib/Frontend/ModuleDependencyCollector.cpp (added)
+++ cfe/trunk/lib/Frontend/ModuleDependencyCollector.cpp Thu Jun 19 14:36:03 2014
@@ -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;
+}

Added: cfe/trunk/test/Modules/dependency-dump-dependent-module.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/dependency-dump-dependent-module.m?rev=211303&view=auto
==============================================================================
--- cfe/trunk/test/Modules/dependency-dump-dependent-module.m (added)
+++ cfe/trunk/test/Modules/dependency-dump-dependent-module.m Thu Jun 19 14:36:03 2014
@@ -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
+
+ at import AlsoDependsOnModule;

Added: cfe/trunk/test/Modules/dependency-dump.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/dependency-dump.m?rev=211303&view=auto
==============================================================================
--- cfe/trunk/test/Modules/dependency-dump.m (added)
+++ cfe/trunk/test/Modules/dependency-dump.m Thu Jun 19 14:36:03 2014
@@ -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
+
+ at import Module;





More information about the cfe-commits mailing list