Allow imported INTERFACE libraries to specify a link library name

Add an `IMPORTED_LIBNAME[_<CONFIG>]` target property to specify a library
name to be placed on the link line in place of an interface library
since it has no library file of its own.  Restrict use of the property
to imported `INTERFACE` libraries.

This will be particularly useful for find modules that need to provide
imported libraries from system SDKs where the full path to the library
file is not known.  Now such find modules will be able to provide an
imported interface library and set `IMPORTED_LIBNAME` to refer to the
SDK library by name.

Issue: #15267
This commit is contained in:
Brad King 2016-11-03 16:44:32 -04:00
parent 1d1f1eeb6a
commit 09cda9d5e7
24 changed files with 284 additions and 15 deletions

View File

@ -167,6 +167,8 @@ Properties on Targets
/prop_tgt/IMPORTED_CONFIGURATIONS /prop_tgt/IMPORTED_CONFIGURATIONS
/prop_tgt/IMPORTED_IMPLIB_CONFIG /prop_tgt/IMPORTED_IMPLIB_CONFIG
/prop_tgt/IMPORTED_IMPLIB /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_CONFIG
/prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES
/prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES_CONFIG /prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES_CONFIG

View File

@ -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).

View File

@ -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.

View File

@ -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.

View File

@ -606,6 +606,12 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
// of COMPATIBLE_INTERFACE_ enforcement. The generators will ignore // of COMPATIBLE_INTERFACE_ enforcement. The generators will ignore
// this for the actual link line. // this for the actual link line.
this->Items.push_back(Item(std::string(), false, tgt)); 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 { } else {
// Decide whether to use an import library. // Decide whether to use an import library.
bool implib = bool implib =

View File

@ -2796,6 +2796,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, std::string cmGeneratorTarget::GetFullPath(const std::string& config,
bool implib, bool realname) const bool implib, bool realname) const
{ {
@ -4711,6 +4721,9 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
} }
} }
if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
if (loc) {
info.LibName = loc;
}
return; return;
} }

View File

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

View File

@ -912,6 +912,9 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
this->Internal->SourceEntries.push_back(value); this->Internal->SourceEntries.push_back(value);
this->Internal->SourceBacktraces.push_back(lfbt); this->Internal->SourceBacktraces.push_back(lfbt);
} }
} else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") &&
!this->CheckImportedLibName(prop, value ? value : "")) {
/* error was reported by check method */
} else { } else {
this->Properties.SetProperty(prop, value); this->Properties.SetProperty(prop, value);
} }
@ -979,6 +982,9 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
this->Internal->SourceEntries.push_back(value); this->Internal->SourceEntries.push_back(value);
this->Internal->SourceBacktraces.push_back(lfbt); 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 { } else {
this->Properties.AppendProperty(prop, value, asString); this->Properties.AppendProperty(prop, value, asString);
} }
@ -1374,20 +1380,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, bool cmTarget::GetMappedConfig(std::string const& desired_config,
const char** loc, const char** imp, const char** loc, const char** imp,
std::string& suffix) const std::string& suffix) const
{ {
if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { std::string const locPropBase =
// This method attempts to find a config-specific LOCATION for the this->GetType() == cmStateEnums::INTERFACE_LIBRARY ? "IMPORTED_LIBNAME"
// IMPORTED library. In the case of cmStateEnums::INTERFACE_LIBRARY, there : "IMPORTED_LOCATION";
// is no
// LOCATION at all, so leaving *loc and *imp unchanged is the appropriate
// and valid response.
return true;
}
std::string const locPropBase = "IMPORTED_LOCATION";
// Track the configuration-specific property suffix. // Track the configuration-specific property suffix.
suffix = "_"; suffix = "_";
@ -1445,7 +1472,9 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
// then the target location is not found. The project does not want // then the target location is not found. The project does not want
// any other configuration. // any other configuration.
if (!mappedConfigs.empty() && !*loc && !*imp) { 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 // If we have not yet found it then there are no mapped
@ -1499,7 +1528,9 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config,
} }
// If we have not yet found it then the target location is not available. // If we have not yet found it then the target location is not available.
if (!*loc && !*imp) { 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; return true;

View File

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

View File

@ -66,7 +66,9 @@ bool cmTargetPropertyComputer::WhiteListedInterfaceProperty(
return true; 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; return true;
} }

View File

@ -25,8 +25,25 @@ target_sources(iface_objlib INTERFACE $<TARGET_OBJECTS:objlib>)
add_library(intermediate INTERFACE) add_library(intermediate INTERFACE)
target_link_libraries(intermediate INTERFACE iface_objlib) 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) 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) add_subdirectory(libsdir)

View File

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

View File

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

View File

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

View File

@ -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\)$

View File

@ -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)

View File

@ -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\)$

View File

@ -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)

View File

@ -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\)$

View File

@ -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)

View File

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