浏览代码

Merge topic 'imported-interface-libname'

09cda9d5 Allow imported INTERFACE libraries to specify a link library name
1d1f1eeb cmTarget: Refactor GetMappedConfig to choose location property up front
479932fa cmTarget: Add comment clarifying interface library special case
925e4270 cmTarget: Clarify comments in GetMappedConfig
Brad King 9 年之前
父节点
当前提交
453df662ba

+ 2 - 0
Help/manual/cmake-properties.7.rst

@@ -167,6 +167,8 @@ Properties on Targets
    /prop_tgt/IMPORTED_CONFIGURATIONS
    /prop_tgt/IMPORTED_IMPLIB_CONFIG
    /prop_tgt/IMPORTED_IMPLIB
+   /prop_tgt/IMPORTED_LIBNAME_CONFIG
+   /prop_tgt/IMPORTED_LIBNAME
    /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES_CONFIG
    /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES
    /prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES_CONFIG

+ 23 - 0
Help/prop_tgt/IMPORTED_LIBNAME.rst

@@ -0,0 +1,23 @@
+IMPORTED_LIBNAME
+----------------
+
+Specify the link library name for an :ref:`imported <Imported Targets>`
+:ref:`Interface Library <Interface Libraries>`.
+
+An interface library builds no library file itself but does specify
+usage requirements for its consumers.  The ``IMPORTED_LIBNAME``
+property may be set to specify a single library name to be placed
+on the link line in place of the interface library target name as
+a requirement for using the interface.
+
+This property is intended for use in naming libraries provided by
+a platform SDK for which the full path to a library file may not
+be known.  The value may be a plain library name such as ``foo``
+but may *not* be a path (e.g. ``/usr/lib/libfoo.so``) or a flag
+(e.g. ``-Wl,...``).  The name is never treated as a library target
+name even if it happens to name one.
+
+The ``IMPORTED_LIBNAME`` property is allowed only on
+:ref:`imported <Imported Targets>` :ref:`Interface Libraries`
+and is rejected on targets of other types (for which
+the :prop_tgt:`IMPORTED_LOCATION` target property may be used).

+ 7 - 0
Help/prop_tgt/IMPORTED_LIBNAME_CONFIG.rst

@@ -0,0 +1,7 @@
+IMPORTED_LIBNAME_<CONFIG>
+-------------------------
+
+<CONFIG>-specific version of :prop_tgt:`IMPORTED_LIBNAME` property.
+
+Configuration names correspond to those provided by the project from
+which the target is imported.

+ 7 - 0
Help/release/dev/imported-interface-libname.rst

@@ -0,0 +1,7 @@
+imported-interface-libname
+--------------------------
+
+* :ref:`Imported <Imported Targets>` :ref:`Interface Libraries` learned new
+  :prop_tgt:`IMPORTED_LIBNAME` and :prop_tgt:`IMPORTED_LIBNAME_<CONFIG>`
+  target properties to specify a link library name since interface libraries
+  do not build their own library files.

+ 6 - 0
Source/cmComputeLinkInformation.cxx

@@ -606,6 +606,12 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
       // of COMPATIBLE_INTERFACE_ enforcement.  The generators will ignore
       // this for the actual link line.
       this->Items.push_back(Item(std::string(), false, tgt));
+
+      // Also add the item the interface specifies to be used in its place.
+      std::string const& libName = tgt->GetImportedLibName(config);
+      if (!libName.empty()) {
+        this->AddItem(libName, CM_NULLPTR);
+      }
     } else {
       // Decide whether to use an import library.
       bool implib =

+ 13 - 0
Source/cmGeneratorTarget.cxx

@@ -2798,6 +2798,16 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
   }
 }
 
+std::string cmGeneratorTarget::GetImportedLibName(
+  std::string const& config) const
+{
+  if (cmGeneratorTarget::ImportInfo const* info =
+        this->GetImportInfo(config)) {
+    return info->LibName;
+  }
+  return std::string();
+}
+
 std::string cmGeneratorTarget::GetFullPath(const std::string& config,
                                            bool implib, bool realname) const
 {
@@ -4713,6 +4723,9 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
     }
   }
   if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    if (loc) {
+      info.LibName = loc;
+    }
     return;
   }
 

+ 4 - 0
Source/cmGeneratorTarget.h

@@ -147,6 +147,9 @@ public:
                                      const cmGeneratorTarget* head,
                                      bool usage_requirements_only) const;
 
+  /** Get the library name for an imported interface library.  */
+  std::string GetImportedLibName(std::string const& config) const;
+
   /** Get the full path to the target according to the settings in its
       makefile and the configuration type.  */
   std::string GetFullPath(const std::string& config = "", bool implib = false,
@@ -643,6 +646,7 @@ private:
     std::string Location;
     std::string SOName;
     std::string ImportLibrary;
+    std::string LibName;
     std::string Languages;
     std::string Libraries;
     std::string LibrariesProp;

+ 53 - 18
Source/cmTarget.cxx

@@ -291,6 +291,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
          ci != configNames.end(); ++ci) {
       std::string configUpper = cmSystemTools::UpperCase(*ci);
       for (const char** p = configProps; *p; ++p) {
+        // Interface libraries have no output locations, so honor only
+        // the configuration map.
         if (this->TargetTypeValue == cmStateEnums::INTERFACE_LIBRARY &&
             strcmp(*p, "MAP_IMPORTED_CONFIG_") != 0) {
           continue;
@@ -912,6 +914,9 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       this->Internal->SourceEntries.push_back(value);
       this->Internal->SourceBacktraces.push_back(lfbt);
     }
+  } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") &&
+             !this->CheckImportedLibName(prop, value ? value : "")) {
+    /* error was reported by check method */
   } else {
     this->Properties.SetProperty(prop, value);
   }
@@ -979,6 +984,9 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
     cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
     this->Internal->SourceEntries.push_back(value);
     this->Internal->SourceBacktraces.push_back(lfbt);
+  } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) {
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR,
+                                 prop + " property may not be APPENDed.");
   } else {
     this->Properties.AppendProperty(prop, value, asString);
   }
@@ -1374,18 +1382,41 @@ void cmTarget::SetPropertyDefault(const std::string& property,
   }
 }
 
+bool cmTarget::CheckImportedLibName(std::string const& prop,
+                                    std::string const& value) const
+{
+  if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY ||
+      !this->IsImported()) {
+    this->Makefile->IssueMessage(
+      cmake::FATAL_ERROR, prop +
+        " property may be set only on imported INTERFACE library targets.");
+    return false;
+  }
+  if (!value.empty()) {
+    if (value[0] == '-') {
+      this->Makefile->IssueMessage(cmake::FATAL_ERROR, prop +
+                                     " property value\n  " + value +
+                                     "\nmay not start with '-'.");
+      return false;
+    }
+    std::string::size_type bad = value.find_first_of(":/\\;");
+    if (bad != value.npos) {
+      this->Makefile->IssueMessage(
+        cmake::FATAL_ERROR, prop + " property value\n  " + value +
+          "\nmay not contain '" + value.substr(bad, 1) + "'.");
+      return false;
+    }
+  }
+  return true;
+}
+
 bool cmTarget::GetMappedConfig(std::string const& desired_config,
                                const char** loc, const char** imp,
                                std::string& suffix) const
 {
-  if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-    // This method attempts to find a config-specific LOCATION for the
-    // IMPORTED library. In the case of cmStateEnums::INTERFACE_LIBRARY, there
-    // is no
-    // LOCATION at all, so leaving *loc and *imp unchanged is the appropriate
-    // and valid response.
-    return true;
-  }
+  std::string const locPropBase =
+    this->GetType() == cmStateEnums::INTERFACE_LIBRARY ? "IMPORTED_LIBNAME"
+                                                       : "IMPORTED_LOCATION";
 
   // Track the configuration-specific property suffix.
   suffix = "_";
@@ -1412,7 +1443,7 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
     if (mci->empty()) {
       // An empty string in the mapping has a special meaning:
       // look up the config-less properties.
-      *loc = this->GetProperty("IMPORTED_LOCATION");
+      *loc = this->GetProperty(locPropBase);
       if (allowImp) {
         *imp = this->GetProperty("IMPORTED_IMPLIB");
       }
@@ -1422,7 +1453,7 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
       }
     } else {
       std::string mcUpper = cmSystemTools::UpperCase(*mci);
-      std::string locProp = "IMPORTED_LOCATION_";
+      std::string locProp = locPropBase + "_";
       locProp += mcUpper;
       *loc = this->GetProperty(locProp);
       if (allowImp) {
@@ -1440,16 +1471,18 @@ bool cmTarget::GetMappedConfig(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.
+  // then the target location is not found.  The project does not want
+  // any other configuration.
   if (!mappedConfigs.empty() && !*loc && !*imp) {
-    return false;
+    // Interface libraries are always available because their
+    // library name is optional so it is okay to leave *loc empty.
+    return this->GetType() == cmStateEnums::INTERFACE_LIBRARY;
   }
 
   // If we have not yet found it then there are no mapped
   // configurations.  Look for an exact-match.
   if (!*loc && !*imp) {
-    std::string locProp = "IMPORTED_LOCATION";
+    std::string locProp = locPropBase;
     locProp += suffix;
     *loc = this->GetProperty(locProp);
     if (allowImp) {
@@ -1467,7 +1500,7 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
 
     // Look for a configuration-less location.  This may be set by
     // manually-written code.
-    *loc = this->GetProperty("IMPORTED_LOCATION");
+    *loc = this->GetProperty(locPropBase);
     if (allowImp) {
       *imp = this->GetProperty("IMPORTED_IMPLIB");
     }
@@ -1485,7 +1518,7 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
          !*loc && !*imp && aci != availableConfigs.end(); ++aci) {
       suffix = "_";
       suffix += cmSystemTools::UpperCase(*aci);
-      std::string locProp = "IMPORTED_LOCATION";
+      std::string locProp = locPropBase;
       locProp += suffix;
       *loc = this->GetProperty(locProp);
       if (allowImp) {
@@ -1495,9 +1528,11 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
       }
     }
   }
-  // If we have not yet found it then the target is not available.
+  // If we have not yet found it then the target location is not available.
   if (!*loc && !*imp) {
-    return false;
+    // Interface libraries are always available because their
+    // library name is optional so it is okay to leave *loc empty.
+    return this->GetType() == cmStateEnums::INTERFACE_LIBRARY;
   }
 
   return true;

+ 3 - 0
Source/cmTarget.h

@@ -278,6 +278,9 @@ private:
   void SetPropertyDefault(const std::string& property,
                           const char* default_value);
 
+  bool CheckImportedLibName(std::string const& prop,
+                            std::string const& value) const;
+
 private:
   cmPropertyMap Properties;
   std::set<std::string> SystemIncludeDirectories;

+ 3 - 1
Source/cmTargetPropertyComputer.cxx

@@ -65,7 +65,9 @@ bool cmTargetPropertyComputer::WhiteListedInterfaceProperty(
     return true;
   }
 
-  if (cmHasLiteralPrefix(prop, "MAP_IMPORTED_CONFIG_")) {
+  if (prop == "IMPORTED_CONFIGURATIONS" || prop == "IMPORTED_LIBNAME" ||
+      cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME_") ||
+      cmHasLiteralPrefix(prop, "MAP_IMPORTED_CONFIG_")) {
     return true;
   }
 

+ 18 - 1
Tests/InterfaceLibrary/CMakeLists.txt

@@ -25,8 +25,25 @@ target_sources(iface_objlib INTERFACE $<TARGET_OBJECTS:objlib>)
 add_library(intermediate INTERFACE)
 target_link_libraries(intermediate INTERFACE iface_objlib)
 
+add_library(item_fake_tgt STATIC item_fake.cpp)
+set_property(TARGET item_fake_tgt PROPERTY OUTPUT_NAME item_fake)
+add_library(item_real STATIC item.cpp)
+add_library(item_iface INTERFACE IMPORTED)
+set_property(TARGET item_iface PROPERTY IMPORTED_LIBNAME item_real)
+add_dependencies(item_iface item_real)
+link_directories(${CMAKE_CURRENT_BINARY_DIR})
+
 add_executable(InterfaceLibrary definetestexe.cpp)
-target_link_libraries(InterfaceLibrary iface_nodepends headeriface subiface intermediate)
+target_link_libraries(InterfaceLibrary
+  iface_nodepends
+  headeriface
+  subiface
+  intermediate
+
+  item_iface
+  item_fake # ensure that 'item_real' is ordered in place of item_iface
+  )
+add_dependencies(InterfaceLibrary item_fake_tgt)
 
 add_subdirectory(libsdir)
 

+ 2 - 1
Tests/InterfaceLibrary/definetestexe.cpp

@@ -17,8 +17,9 @@
 
 extern int obj();
 extern int sub();
+extern int item();
 
 int main(int, char**)
 {
-  return obj() + sub();
+  return obj() + sub() + item();
 }

+ 4 - 0
Tests/InterfaceLibrary/item.cpp

@@ -0,0 +1,4 @@
+int item()
+{
+  return 0;
+}

+ 5 - 0
Tests/InterfaceLibrary/item_fake.cpp

@@ -0,0 +1,5 @@
+extern int item_undefined();
+int item()
+{
+  return item_undefined();
+}

+ 1 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-bad-value-result.txt

@@ -0,0 +1 @@
+1

+ 44 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-bad-value-stderr.txt

@@ -0,0 +1,44 @@
+^CMake Error at IMPORTED_LIBNAME-bad-value.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property value
+
+    -flag
+
+  may not start with '-'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-bad-value.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property value
+
+    item1;item2
+
+  may not contain ';'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-bad-value.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property value
+
+    /path/to/item1
+
+  may not contain '/'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-bad-value.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property value
+
+    \\path\\to\\item1
+
+  may not contain '\\'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-bad-value.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property value
+
+    c:\\path\\to\\item1
+
+  may not contain ':'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$

+ 6 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-bad-value.cmake

@@ -0,0 +1,6 @@
+add_library(MyTarget INTERFACE IMPORTED)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME -flag)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME item1 item2)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME /path/to/item1)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME \\path\\to\\item1)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME c:\\path\\to\\item1)

+ 1 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-iface-result.txt

@@ -0,0 +1 @@
+1

+ 45 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-iface-stderr.txt

@@ -0,0 +1,45 @@
+^CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may not be APPENDed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME_DEBUG property may be set only on imported INTERFACE
+  library targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME_DEBUG property may not be APPENDed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-iface.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$

+ 17 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-iface.cmake

@@ -0,0 +1,17 @@
+add_custom_target(MyCustom)
+set_property(TARGET MyCustom PROPERTY IMPORTED_LIBNAME item1)
+set_property(TARGET MyCustom APPEND PROPERTY IMPORTED_LIBNAME item2)
+set_property(TARGET MyCustom PROPERTY IMPORTED_LIBNAME_DEBUG item1)
+set_property(TARGET MyCustom APPEND PROPERTY IMPORTED_LIBNAME_DEBUG item2)
+
+add_library(MyStatic STATIC IMPORTED)
+set_property(TARGET MyStatic PROPERTY IMPORTED_LIBNAME item1)
+
+add_library(MyShared SHARED IMPORTED)
+set_property(TARGET MyShared PROPERTY IMPORTED_LIBNAME item1)
+
+add_library(MyModule MODULE IMPORTED)
+set_property(TARGET MyModule PROPERTY IMPORTED_LIBNAME item1)
+
+add_executable(MyExe IMPORTED)
+set_property(TARGET MyExe PROPERTY IMPORTED_LIBNAME item1)

+ 1 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-imported-result.txt

@@ -0,0 +1 @@
+1

+ 21 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-imported-stderr.txt

@@ -0,0 +1,21 @@
+^CMake Error at IMPORTED_LIBNAME-non-imported.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may be set only on imported INTERFACE library
+  targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-imported.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME property may not be APPENDed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-imported.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME_DEBUG property may be set only on imported INTERFACE
+  library targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at IMPORTED_LIBNAME-non-imported.cmake:[0-9]+ \(set_property\):
+  IMPORTED_LIBNAME_DEBUG property may not be APPENDed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$

+ 5 - 0
Tests/RunCMake/interface_library/IMPORTED_LIBNAME-non-imported.cmake

@@ -0,0 +1,5 @@
+add_library(MyTarget INTERFACE)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME item1)
+set_property(TARGET MyTarget APPEND PROPERTY IMPORTED_LIBNAME item2)
+set_property(TARGET MyTarget PROPERTY IMPORTED_LIBNAME_DEBUG item1)
+set_property(TARGET MyTarget APPEND PROPERTY IMPORTED_LIBNAME_DEBUG item2)

+ 3 - 0
Tests/RunCMake/interface_library/RunCMakeTest.cmake

@@ -8,3 +8,6 @@ run_cmake(invalid_signature)
 run_cmake(global-interface)
 run_cmake(genex_link)
 run_cmake(add_custom_command-TARGET)
+run_cmake(IMPORTED_LIBNAME-bad-value)
+run_cmake(IMPORTED_LIBNAME-non-iface)
+run_cmake(IMPORTED_LIBNAME-non-imported)