浏览代码

ENH: Allow IMPORTED_IMPLIB w/o IMPORTED_LOCATION

Linking to a Windows shared library (.dll) requires only its import
library (.lib).  This teaches CMake to recognize SHARED IMPORTED library
targets that set only IMPORTED_IMPLIB and not IMPORTED_LOCATION.
Brad King 16 年之前
父节点
当前提交
d05e98f8d7

+ 68 - 23
Source/cmTarget.cxx

@@ -2527,23 +2527,17 @@ std::string cmTarget::NormalGetFullPath(const char* config, bool implib,
 //----------------------------------------------------------------------------
 std::string cmTarget::ImportedGetFullPath(const char* config, bool implib)
 {
+  std::string result;
   if(cmTarget::ImportInfo const* info = this->GetImportInfo(config))
     {
-    if(implib)
-      {
-      return info->ImportLibrary;
-      }
-    else
-      {
-      return info->Location;
-      }
+    result = implib? info->ImportLibrary : info->Location;
     }
-  else
+  if(result.empty())
     {
-    std::string result = this->GetName();
+    result = this->GetName();
     result += "-NOTFOUND";
-    return result;
     }
+  return result;
 }
 
 //----------------------------------------------------------------------------
@@ -3467,7 +3461,7 @@ cmTarget::GetImportInfo(const char* config)
 
   // If the location is empty then the target is not available for
   // this configuration.
-  if(i->second.Location.empty())
+  if(i->second.Location.empty() && i->second.ImportLibrary.empty())
     {
     return 0;
     }
@@ -3491,6 +3485,12 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
   std::string suffix = "_";
   suffix += desired_config;
 
+  // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
+  // library or an executable with exports.
+  bool allowImp =
+    this->DLLPlatform && (this->GetType() == cmTarget::SHARED_LIBRARY ||
+                          this->IsExecutableWithExports());
+
   // Look for a mapping from the current project's configuration to
   // the imported project's configuration.
   std::vector<std::string> mappedConfigs;
@@ -3505,17 +3505,24 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
 
   // If a mapping was found, check its configurations.
   const char* loc = 0;
+  const char* imp = 0;
   for(std::vector<std::string>::const_iterator mci = mappedConfigs.begin();
-      !loc && mci != mappedConfigs.end(); ++mci)
+      !loc && !imp && mci != mappedConfigs.end(); ++mci)
     {
     // Look for this configuration.
     std::string mcUpper = cmSystemTools::UpperCase(mci->c_str());
     std::string locProp = "IMPORTED_LOCATION_";
     locProp += mcUpper;
     loc = this->GetProperty(locProp.c_str());
+    if(allowImp)
+      {
+      std::string impProp = "IMPORTED_IMPLIB_";
+      impProp += mcUpper;
+      imp = this->GetProperty(impProp.c_str());
+      }
 
     // If it was found, use it for all properties below.
-    if(loc)
+    if(loc || imp)
       {
       suffix = "_";
       suffix += mcUpper;
@@ -3525,23 +3532,29 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
   // If we needed to find one of the mapped configurations but did not
   // then the target is not found.  The project does not want any
   // other configuration.
-  if(!mappedConfigs.empty() && !loc)
+  if(!mappedConfigs.empty() && !loc && !imp)
     {
     return;
     }
 
   // If we have not yet found it then there are no mapped
   // configurations.  Look for an exact-match.
-  if(!loc)
+  if(!loc && !imp)
     {
     std::string locProp = "IMPORTED_LOCATION";
     locProp += suffix;
     loc = this->GetProperty(locProp.c_str());
+    if(allowImp)
+      {
+      std::string impProp = "IMPORTED_IMPLIB";
+      impProp += suffix;
+      imp = this->GetProperty(impProp.c_str());
+      }
     }
 
   // If we have not yet found it then there are no mapped
   // configurations and no exact match.
-  if(!loc)
+  if(!loc && !imp)
     {
     // The suffix computed above is not useful.
     suffix = "";
@@ -3549,11 +3562,15 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
     // Look for a configuration-less location.  This may be set by
     // manually-written code.
     loc = this->GetProperty("IMPORTED_LOCATION");
+    if(allowImp)
+      {
+      imp = this->GetProperty("IMPORTED_IMPLIB");
+      }
     }
 
   // If we have not yet found it then the project is willing to try
   // any available configuration.
-  if(!loc)
+  if(!loc && !imp)
     {
     std::vector<std::string> availableConfigs;
     if(const char* iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS"))
@@ -3562,25 +3579,49 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
       }
     for(std::vector<std::string>::const_iterator
           aci = availableConfigs.begin();
-        !loc && aci != availableConfigs.end(); ++aci)
+        !loc && !imp && aci != availableConfigs.end(); ++aci)
       {
       suffix = "_";
       suffix += cmSystemTools::UpperCase(*aci);
       std::string locProp = "IMPORTED_LOCATION";
       locProp += suffix;
       loc = this->GetProperty(locProp.c_str());
+      if(allowImp)
+        {
+        std::string impProp = "IMPORTED_IMPLIB";
+        impProp += suffix;
+        imp = this->GetProperty(impProp.c_str());
+        }
       }
     }
 
   // If we have not yet found it then the target is not available.
-  if(!loc)
+  if(!loc && !imp)
     {
     return;
     }
 
   // A provided configuration has been chosen.  Load the
   // configuration's properties.
-  info.Location = loc;
+
+  // Get the location.
+  if(loc)
+    {
+    info.Location = loc;
+    }
+  else
+    {
+    std::string impProp = "IMPORTED_LOCATION";
+    impProp += suffix;
+    if(const char* config_location = this->GetProperty(impProp.c_str()))
+      {
+      info.Location = config_location;
+      }
+    else if(const char* location = this->GetProperty("IMPORTED_LOCATION"))
+      {
+      info.Location = location;
+      }
+    }
 
   // Get the soname.
   if(this->GetType() == cmTarget::SHARED_LIBRARY)
@@ -3613,8 +3654,12 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
     }
 
   // Get the import library.
-  if(this->GetType() == cmTarget::SHARED_LIBRARY ||
-     this->IsExecutableWithExports())
+  if(imp)
+    {
+    info.ImportLibrary = imp;
+    }
+  else if(this->GetType() == cmTarget::SHARED_LIBRARY ||
+          this->IsExecutableWithExports())
     {
     std::string impProp = "IMPORTED_IMPLIB";
     impProp += suffix;

+ 15 - 1
Tests/ExportImport/Export/CMakeLists.txt

@@ -39,6 +39,8 @@ set_property(TARGET testLib3 PROPERTY SOVERSION 3)
 add_library(testLib4 SHARED testLib4.c)
 set_property(TARGET testLib4 PROPERTY FRAMEWORK 1)
 
+add_library(testLib5 SHARED testLib5.c)
+
 # Work-around: Visual Studio 6 does not support per-target object files.
 set(VS6)
 if("${CMAKE_GENERATOR}" MATCHES "Visual Studio 6")
@@ -85,15 +87,27 @@ install(
   LIBRARY DESTINATION lib/impl
   ARCHIVE DESTINATION lib/impl
   )
+install(
+  TARGETS testLib5
+  EXPORT exp
+  # Leave out RUNTIME DESTINATION to test implib-only export.
+  LIBRARY DESTINATION lib
+  ARCHIVE DESTINATION lib
+  )
 install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp)
 
+# Install testLib5.dll outside the export.
+if(WIN32)
+  install(TARGETS testLib5 RUNTIME DESTINATION bin)
+endif(WIN32)
+
 # Export from build tree.
 export(TARGETS testExe1 testLib1 testLib2 testLib3
   testExe2libImp testLib3Imp
   NAMESPACE bld_
   FILE ExportBuildTree.cmake
   )
-export(TARGETS testExe2 testLib4 testExe3 testExe2lib
+export(TARGETS testExe2 testLib4 testLib5 testExe3 testExe2lib
   testLib4lib testLib4libdbg testLib4libopt
   NAMESPACE bld_
   APPEND FILE ExportBuildTree.cmake

+ 7 - 0
Tests/ExportImport/Export/testLib5.c

@@ -0,0 +1,7 @@
+#if defined(_WIN32) || defined(__CYGWIN__)
+# define testLib5_EXPORT __declspec(dllexport)
+#else
+# define testLib5_EXPORT
+#endif
+
+testLib5_EXPORT int testLib5(void) { return 0; }

+ 12 - 2
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -23,7 +23,12 @@ add_executable(imp_testExe1
   )
 
 # Try linking to a library imported from the install tree.
-target_link_libraries(imp_testExe1 exp_testLib2 exp_testLib3 exp_testLib4)
+target_link_libraries(imp_testExe1
+  exp_testLib2
+  exp_testLib3
+  exp_testLib4
+  exp_testLib5
+  )
 
 # Try building a plugin to an executable imported from the install tree.
 add_library(imp_mod1 MODULE imp_mod1.c)
@@ -48,7 +53,12 @@ add_executable(imp_testExe1b
   )
 
 # Try linking to a library imported from the build tree.
-target_link_libraries(imp_testExe1b bld_testLib2 bld_testLib3 bld_testLib4)
+target_link_libraries(imp_testExe1b
+  bld_testLib2
+  bld_testLib3
+  bld_testLib4
+  bld_testLib5
+  )
 
 # Try building a plugin to an executable imported from the build tree.
 add_library(imp_mod1b MODULE imp_mod1.c)

+ 2 - 0
Tests/ExportImport/Import/A/imp_testExe1.c

@@ -4,6 +4,7 @@ extern int testLib2();
 extern int testLib3();
 extern int testLib4();
 extern int testLib4lib();
+extern int testLib5();
 
 /* Switch a symbol between debug and optimized builds to make sure the
    proper library is found from the testLib4 link interface.  */
@@ -17,5 +18,6 @@ extern testLib4libcfg(void);
 int main()
 {
   return (testLib2() + generated_by_testExe1() + testLib3() + testLib4()
+          + testLib5()
           + generated_by_testExe3() + testLib4lib() + testLib4libcfg());
 }