Use export_as for autolinking frameworks
authorBruno Cardoso Lopes <bruno.cardoso@gmail.com>
Mon, 16 Apr 2018 19:42:32 +0000 (19:42 +0000)
committerBruno Cardoso Lopes <bruno.cardoso@gmail.com>
Mon, 16 Apr 2018 19:42:32 +0000 (19:42 +0000)
framework module SomeKitCore {
  ...
  export_as SomeKit
}

Given the module above, while generting autolink information during
codegen, clang should to emit '-framework SomeKitCore' only if SomeKit
was not imported in the relevant TU, otherwise it should use '-framework
SomeKit' instead.

rdar://problem/38269782

llvm-svn: 330152

17 files changed:
clang/include/clang/Basic/Module.h
clang/include/clang/Lex/ModuleMap.h
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Frontend/CompilerInstance.cpp
clang/lib/Lex/ModuleMap.cpp
clang/lib/Serialization/ASTReader.cpp
clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Headers/OtherKit.h [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Modules/module.modulemap [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SKWidget.h [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SomeKit.h [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Modules/module.modulemap [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKit.framework/SomeKit.tbd [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SKWidget.h [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SomeKitCore.h [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Modules/module.modulemap [new file with mode: 0644]
clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/SomeKitCore.tbd [new file with mode: 0644]
clang/test/Modules/use-exportas-for-link.m [new file with mode: 0644]

index 3725fcb..d4e2dd4 100644 (file)
@@ -331,6 +331,10 @@ public:
   /// an entity from this module is used.
   llvm::SmallVector<LinkLibrary, 2> LinkLibraries;
 
+  /// Autolinking uses the framework name for linking purposes
+  /// when this is false and the export_as name otherwise.
+  bool UseExportAsModuleLinkName = false;
+
   /// \brief The set of "configuration macros", which are macros that
   /// (intentionally) change how this module is built.
   std::vector<std::string> ConfigMacros;
index 8593fcd..a34f0d8 100644 (file)
@@ -21,6 +21,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
@@ -104,7 +105,19 @@ class ModuleMap {
   /// \brief The number of modules we have created in total.
   unsigned NumCreatedModules = 0;
 
+  /// In case a module has a export_as entry, it might have a pending link
+  /// name to be determined if that module is imported.
+  llvm::StringMap<llvm::StringSet<>> PendingLinkAsModule;
+
 public:
+  /// Use PendingLinkAsModule information to mark top level link names that
+  /// are going to be replaced by export_as aliases.
+  void resolveLinkAsDependencies(Module *Mod);
+
+  /// Make module to use export_as as the link dependency name if enough
+  /// information is available or add it to a pending list otherwise.
+  void addLinkAsDependency(Module *Mod);
+
   /// \brief Flags describing the role of a module header.
   enum ModuleHeaderRole {
     /// \brief This header is normally included in the module.
index 687a6ca..42a8cd8 100644 (file)
@@ -1556,6 +1556,12 @@ static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
   // Add linker options to link against the libraries/frameworks
   // described by this module.
   llvm::LLVMContext &Context = CGM.getLLVMContext();
+
+  // For modules that use export_as for linking, use that module
+  // name instead.
+  if (Mod->UseExportAsModuleLinkName)
+    return;
+
   for (unsigned I = Mod->LinkLibraries.size(); I > 0; --I) {
     // Link against a framework.  Frameworks are currently Darwin only, so we
     // don't to ask TargetCodeGenInfo for the spelling of the linker option.
index b8f55b6..f57246b 100644 (file)
@@ -1979,6 +1979,12 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
                      Module, ImportLoc);
   }
 
+  // Resolve any remaining module using export_as for this one.
+  getPreprocessor()
+      .getHeaderSearchInfo()
+      .getModuleMap()
+      .resolveLinkAsDependencies(TopModule);
+
   LastModuleImportLoc = ImportLoc;
   LastModuleImportResult = ModuleLoadResult(Module);
   return LastModuleImportResult;
index a711e1d..e47abd5 100644 (file)
 
 using namespace clang;
 
+void ModuleMap::resolveLinkAsDependencies(Module *Mod) {
+  auto PendingLinkAs = PendingLinkAsModule.find(Mod->Name);
+  if (PendingLinkAs != PendingLinkAsModule.end()) {
+    for (auto &Name : PendingLinkAs->second) {
+      auto *M = findModule(Name.getKey());
+      if (M)
+        M->UseExportAsModuleLinkName = true;
+    }
+  }
+}
+
+void ModuleMap::addLinkAsDependency(Module *Mod) {
+  if (findModule(Mod->ExportAsModule))
+    Mod->UseExportAsModuleLinkName = true;
+  else
+    PendingLinkAsModule[Mod->ExportAsModule].insert(Mod->Name);
+}
+
 Module::HeaderKind ModuleMap::headerRoleToKind(ModuleHeaderRole Role) {
   switch ((int)Role) {
   default: llvm_unreachable("unknown header role");
@@ -2412,6 +2430,8 @@ void ModuleMapParser::parseExportAsDecl() {
   }
   
   ActiveModule->ExportAsModule = Tok.getString();
+  Map.addLinkAsDependency(ActiveModule);
+
   consumeToken();
 }
 
index 9e83250..179f25e 100644 (file)
@@ -5171,6 +5171,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
       break;
 
     case SUBMODULE_LINK_LIBRARY:
+      ModMap.resolveLinkAsDependencies(CurrentModule);
       CurrentModule->LinkLibraries.push_back(
                                          Module::LinkLibrary(Blob, Record[0]));
       break;
@@ -5203,6 +5204,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
 
     case SUBMODULE_EXPORT_AS:
       CurrentModule->ExportAsModule = Blob.str();
+      ModMap.addLinkAsDependency(CurrentModule);
       break;
     }
   }
diff --git a/clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Headers/OtherKit.h b/clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Headers/OtherKit.h
new file mode 100644 (file)
index 0000000..18f0bd5
--- /dev/null
@@ -0,0 +1,6 @@
+#import <SomeKitCore/SomeKitCore.h>
+
+#ifdef F
+#import <SomeKit/SomeKit.h>
+#endif
+
diff --git a/clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/exportas-link/OtherKit.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..1ce7351
--- /dev/null
@@ -0,0 +1,5 @@
+
+framework module OtherKit {
+  header "OtherKit.h"
+  export *
+}
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SKWidget.h b/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SKWidget.h
new file mode 100644 (file)
index 0000000..dd99ff7
--- /dev/null
@@ -0,0 +1 @@
+#import <SomeKitCore/SKWidget.h>
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SomeKit.h b/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Headers/SomeKit.h
new file mode 100644 (file)
index 0000000..17a4cc8
--- /dev/null
@@ -0,0 +1 @@
+#import <SomeKit/SKWidget.h>
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..ae4b276
--- /dev/null
@@ -0,0 +1,6 @@
+framework module SomeKit {
+  umbrella header "SomeKit.h"
+  module * {
+    export *
+  }
+}
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/SomeKit.tbd b/clang/test/Modules/Inputs/exportas-link/SomeKit.framework/SomeKit.tbd
new file mode 100644 (file)
index 0000000..ab95795
--- /dev/null
@@ -0,0 +1 @@
+dummy tbd file
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SKWidget.h b/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SKWidget.h
new file mode 100644 (file)
index 0000000..1fb010c
--- /dev/null
@@ -0,0 +1,4 @@
+@interface SKWidget
+- (void)someObjCMethod;
+@end
+
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SomeKitCore.h b/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Headers/SomeKitCore.h
new file mode 100644 (file)
index 0000000..dd99ff7
--- /dev/null
@@ -0,0 +1 @@
+#import <SomeKitCore/SKWidget.h>
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..30f9770
--- /dev/null
@@ -0,0 +1,7 @@
+framework module SomeKitCore {
+  umbrella header "SomeKitCore.h"
+  export_as SomeKit
+  module * {
+    export *
+  }
+}
diff --git a/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/SomeKitCore.tbd b/clang/test/Modules/Inputs/exportas-link/SomeKitCore.framework/SomeKitCore.tbd
new file mode 100644 (file)
index 0000000..ab95795
--- /dev/null
@@ -0,0 +1 @@
+dummy tbd file
diff --git a/clang/test/Modules/use-exportas-for-link.m b/clang/test/Modules/use-exportas-for-link.m
new file mode 100644 (file)
index 0000000..69773e6
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DA -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_A %s
+// CHECK_A: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_A: ![[MODULE]] = !{!"-framework", !"SomeKit"}
+#ifdef A
+@import SomeKitCore;
+@import SomeKit;
+#endif
+
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DB -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_B %s
+// CHECK_B: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_B: ![[MODULE]] = !{!"-framework", !"SomeKit"}
+#ifdef B
+@import SomeKit;
+@import SomeKitCore;
+#endif
+
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DC -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_C %s
+// CHECK_C: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_C: ![[MODULE]] = !{!"-framework", !"SomeKitCore"}
+#ifdef C
+@import SomeKitCore;
+#endif
+
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DD -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_D %s
+// CHECK_D: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_D: ![[MODULE]] = !{!"-framework", !"SomeKit"}
+#ifdef D
+@import SomeKit;
+#endif
+
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DE -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_E %s
+// CHECK_E: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_E: ![[MODULE]] = !{!"-framework", !"SomeKitCore"}
+#ifdef E
+@import OtherKit;
+#endif
+
+// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -DF -fmodules -fimplicit-module-maps -F %S/Inputs/exportas-link %s | FileCheck --check-prefix=CHECK_F %s
+// CHECK_F: !llvm.linker.options = !{![[MODULE:[0-9]+]]}
+// CHECK_F: ![[MODULE]] = !{!"-framework", !"SomeKit"}
+#ifdef F
+@import OtherKit;
+#endif