]> granicus.if.org Git - clang/commitdiff
[ModuleMap][CrashReproducer] Collect headers from inner frameworks
authorBruno Cardoso Lopes <bruno.cardoso@gmail.com>
Fri, 13 May 2016 22:21:51 +0000 (22:21 +0000)
committerBruno Cardoso Lopes <bruno.cardoso@gmail.com>
Fri, 13 May 2016 22:21:51 +0000 (22:21 +0000)
(1) Collect headers under inner frameworks (frameworks inside other
other frameworks).
(2) Make sure we also collect the right header files inside them.

More info on (2):

Consider a dummy framework module B, with header Frameworks/B/B.h. Now
consider that another framework A, with header Frameworks/A/A.h, has a
layout with a inner framework Frameworks/A/Frameworks/B/B.h, where the
"B/B.h" part is a symlink for Frameworks/B/B.h. Also assume that
Frameworks/A/A.h includes <B/B.h>.

When parsing header Frameworks/A/A.h, framework module lookup is
performed in search for B, and it happens that
"Frameworks/A/Frameworks/B/B.h" path is registered in the module instead
of real "Frameworks/B/B.h". This occurs because
"Frameworks/A/Frameworks/B/B.h" is scanned first by the FileManager,
when looking for inner framework modules under Frameworks/A/Frameworks.
This makes Frameworks/A/Frameworks/B/B.h the default cached named inside
the FileManager for the B.h file UID.

This leads to modules being built without consistent paths to underlying
header files. This is usually not a problem in regular compilation flow,
but it's an issue when running the crash reproducer. The issue is that
clangs collect "Frameworks/A/Frameworks/B/B.h" but not
"Frameworks/B/B.h" into the VFS, leading to err_mmap_umbrella_clash. So
make sure we also collect the original header.

Differential Revision: http://reviews.llvm.org/D20194

rdar://problem/25880368

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@269502 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Lex/ModuleMap.h
lib/Frontend/ModuleDependencyCollector.cpp
lib/Lex/ModuleMap.cpp
test/Modules/Inputs/crash-recovery/Frameworks/A.framework/Headers/A.h [new file with mode: 0644]
test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Headers/B.h [new file with mode: 0644]
test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Modules/module.modulemap [new file with mode: 0644]
test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Headers/I.h [new file with mode: 0644]
test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Modules/module.modulemap [new file with mode: 0644]
test/Modules/Inputs/crash-recovery/Frameworks/module.modulemap [new file with mode: 0644]
test/Modules/crash-vfs-umbrella-frameworks.m [new file with mode: 0644]

index fdb617c5323c1d69eedc3a89c7ef6dc89f2039a5..db2cf3c0f2f9571feb7b6507c9b3c33d353ec199 100644 (file)
@@ -55,6 +55,13 @@ public:
   ///
   /// \param Filename The header file itself.
   virtual void moduleMapAddHeader(StringRef Filename) {}
+
+  /// \brief Called when an umbrella header is added during module map parsing.
+  ///
+  /// \param FileMgr FileManager instance
+  /// \param Filename The umbreall header to collect.
+  virtual void moduleMapAddUmbrellaHeader(FileManager *FileMgr,
+                                          const FileEntry *Header) {}
 };
   
 class ModuleMap {
index b58e1bfac67acbde28d15dfd85450be8d769b6c0..ca11f9b863bb96540f17b1cd267f8eee0bd4eb7f 100644 (file)
@@ -48,6 +48,34 @@ struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {
     if (llvm::sys::path::is_absolute(HeaderPath))
       Collector.addFile(HeaderPath);
   }
+  void moduleMapAddUmbrellaHeader(FileManager *FileMgr,
+                                  const FileEntry *Header) override {
+    StringRef HeaderFilename = Header->getName();
+    moduleMapAddHeader(HeaderFilename);
+    // The FileManager can find and cache the symbolic link for a framework
+    // header before its real path, this means a module can have some of its
+    // headers to use other paths. Although this is usually not a problem, it's
+    // inconsistent, and not collecting the original path header leads to
+    // umbrella clashes while rebuilding modules in the crash reproducer. For
+    // example:
+    //    ApplicationServices.framework/Frameworks/ImageIO.framework/ImageIO.h
+    // instead of:
+    //    ImageIO.framework/ImageIO.h
+    //
+    // FIXME: this shouldn't be necessary once we have FileName instances
+    // around instead of FileEntry ones. For now, make sure we collect all
+    // that we need for the reproducer to work correctly.
+    StringRef UmbreallDirFromHeader =
+        llvm::sys::path::parent_path(HeaderFilename);
+    StringRef UmbrellaDir = Header->getDir()->getName();
+    if (!UmbrellaDir.equals(UmbreallDirFromHeader)) {
+      SmallString<128> AltHeaderFilename;
+      llvm::sys::path::append(AltHeaderFilename, UmbrellaDir,
+                              llvm::sys::path::filename(HeaderFilename));
+      if (FileMgr->getFile(AltHeaderFilename))
+        moduleMapAddHeader(AltHeaderFilename);
+    }
+  }
 };
 
 }
index 39ded6f61343e2baace2dc79c6f48a60bfebf166..be3b1d99a55157532ce74163aebe5967e1914f1d 100644 (file)
@@ -760,6 +760,10 @@ void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader,
   Mod->Umbrella = UmbrellaHeader;
   Mod->UmbrellaAsWritten = NameAsWritten.str();
   UmbrellaDirs[UmbrellaHeader->getDir()] = Mod;
+
+  // Notify callbacks that we just added a new header.
+  for (const auto &Cb : Callbacks)
+    Cb->moduleMapAddUmbrellaHeader(&SourceMgr.getFileManager(), UmbrellaHeader);
 }
 
 void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir,
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/A.framework/Headers/A.h b/test/Modules/Inputs/crash-recovery/Frameworks/A.framework/Headers/A.h
new file mode 100644 (file)
index 0000000..49c9fe0
--- /dev/null
@@ -0,0 +1 @@
+#include <B/B.h>
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Headers/B.h b/test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Headers/B.h
new file mode 100644 (file)
index 0000000..761540b
--- /dev/null
@@ -0,0 +1 @@
+// B.h
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Modules/module.modulemap b/test/Modules/Inputs/crash-recovery/Frameworks/B.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..f6c6e7b
--- /dev/null
@@ -0,0 +1,5 @@
+framework module B [extern_c] {
+  umbrella header "B.h"
+  export *
+  module * { export * }
+}
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Headers/I.h b/test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Headers/I.h
new file mode 100644 (file)
index 0000000..f98baaa
--- /dev/null
@@ -0,0 +1,2 @@
+
+#import <A/A.h>
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Modules/module.modulemap b/test/Modules/Inputs/crash-recovery/Frameworks/I.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..912d39e
--- /dev/null
@@ -0,0 +1,5 @@
+framework module I [extern_c] {
+  umbrella header "I.h"
+  export *
+  module * { export * }
+}
diff --git a/test/Modules/Inputs/crash-recovery/Frameworks/module.modulemap b/test/Modules/Inputs/crash-recovery/Frameworks/module.modulemap
new file mode 100644 (file)
index 0000000..0f6fcc0
--- /dev/null
@@ -0,0 +1,2 @@
+framework module * [extern_c] {
+}
diff --git a/test/Modules/crash-vfs-umbrella-frameworks.m b/test/Modules/crash-vfs-umbrella-frameworks.m
new file mode 100644 (file)
index 0000000..0454976
--- /dev/null
@@ -0,0 +1,42 @@
+// REQUIRES: crash-recovery, shell
+
+// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it?
+// XFAIL: mingw32
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/i %t/m %t
+// RUN: cp -a %S/Inputs/crash-recovery/Frameworks %t/i/
+// RUN: mkdir -p %t/i/Frameworks/A.framework/Frameworks
+// RUN: ln -s ../../B.framework %t/i/Frameworks/A.framework/Frameworks/B.framework
+
+// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \
+// RUN: %clang -nostdinc -fsyntax-only %s \
+// RUN:     -F %/t/i/Frameworks -fmodules \
+// RUN:     -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s
+
+// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \
+// RUN:         %t/crash-vfs-*.cache/vfs/vfs.yaml
+// RUN: find %t/crash-vfs-*.cache/vfs | \
+// RUN:   grep "B.framework/Headers/B.h" | count 1
+
+// CHECK: Preprocessed source(s) and associated run script(s) are located at:
+// CHECK-NEXT: note: diagnostic msg: {{.*}}.m
+// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache
+
+// CHECKYAML:      'type': 'directory',
+// CHECKYAML:      'name': "/[[PATH:.*]]/i/Frameworks/A.framework/Frameworks/B.framework/Headers",
+// CHECKYAML-NEXT:      'contents': [
+// CHECKYAML-NEXT:        {
+// CHECKYAML-NEXT:          'type': 'file',
+// CHECKYAML-NEXT:          'name': "B.h",
+// CHECKYAML-NEXT:          'external-contents': "/[[PATH]]/i/Frameworks/B.framework/Headers/B.h"
+
+// CHECKYAML:      'type': 'directory',
+// CHECKYAML:      'name': "/[[PATH]]/i/Frameworks/B.framework/Headers",
+// CHECKYAML-NEXT:      'contents': [
+// CHECKYAML-NEXT:        {
+// CHECKYAML-NEXT:          'type': 'file',
+// CHECKYAML-NEXT:          'name': "B.h",
+// CHECKYAML-NEXT:          'external-contents': "/[[PATH]]/i/Frameworks/B.framework/Headers/B.h"
+
+@import I;