Handle INTERFACE properties transitively for includes and defines.

Contextually, the behavior is as if the properties content from another
target is included in the string and then the result is evaluated.
This commit is contained in:
Stephen Kelly 2012-09-23 13:45:17 +02:00
parent f5b1980fb2
commit 894f52f32d
9 changed files with 243 additions and 2 deletions

View File

@ -52,6 +52,8 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateExpectedTargetsCode(os, expectedTargets); this->GenerateExpectedTargetsCode(os, expectedTargets);
} }
std::vector<std::string> missingTargets;
// Create all the imported targets. // Create all the imported targets.
for(std::vector<cmTarget*>::const_iterator for(std::vector<cmTarget*>::const_iterator
tei = allTargets.begin(); tei = allTargets.begin();
@ -59,8 +61,21 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{ {
cmTarget* te = *tei; cmTarget* te = *tei;
this->GenerateImportTargetCode(os, te); this->GenerateImportTargetCode(os, te);
ImportPropertyMap properties;
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", te,
cmGeneratorExpression::BuildInterface,
properties, missingTargets);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", te,
cmGeneratorExpression::BuildInterface,
properties, missingTargets);
this->GenerateInterfaceProperties(te, os, properties);
} }
this->GenerateMissingTargetsCheckCode(os, missingTargets);
// Generate import file content for each configuration. // Generate import file content for each configuration.
for(std::vector<std::string>::const_iterator for(std::vector<std::string>::const_iterator
ci = this->Configurations.begin(); ci = this->Configurations.begin();

View File

@ -69,6 +69,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateExpectedTargetsCode(os, expectedTargets); this->GenerateExpectedTargetsCode(os, expectedTargets);
} }
std::vector<std::string> missingTargets;
// Create all the imported targets. // Create all the imported targets.
for(std::vector<cmTarget*>::const_iterator for(std::vector<cmTarget*>::const_iterator
tei = allTargets.begin(); tei = allTargets.begin();
@ -76,8 +78,23 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
{ {
cmTarget* te = *tei; cmTarget* te = *tei;
this->GenerateImportTargetCode(os, te); this->GenerateImportTargetCode(os, te);
ImportPropertyMap properties;
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES",
te,
cmGeneratorExpression::InstallInterface,
properties, missingTargets);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS",
te,
cmGeneratorExpression::InstallInterface,
properties, missingTargets);
this->GenerateInterfaceProperties(te, os, properties);
} }
this->GenerateMissingTargetsCheckCode(os, missingTargets);
// Now load per-configuration properties for them. // Now load per-configuration properties for them.
os << "# Load information for each installed configuration.\n" os << "# Load information for each installed configuration.\n"
<< "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n" << "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"

View File

@ -286,6 +286,13 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
} }
} configurationTestNode; } configurationTestNode;
//----------------------------------------------------------------------------
static const char* targetPropertyTransitiveWhitelist[] = {
"INTERFACE_INCLUDE_DIRECTORIES"
, "INTERFACE_COMPILE_DEFINITIONS"
};
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
static const struct TargetPropertyNode : public cmGeneratorExpressionNode static const struct TargetPropertyNode : public cmGeneratorExpressionNode
{ {
@ -394,7 +401,28 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
} }
const char *prop = target->GetProperty(propertyName.c_str()); const char *prop = target->GetProperty(propertyName.c_str());
return prop ? prop : ""; if (!prop)
{
return std::string();
}
for (size_t i = 0;
i < (sizeof(targetPropertyTransitiveWhitelist) /
sizeof(*targetPropertyTransitiveWhitelist));
++i)
{
if (targetPropertyTransitiveWhitelist[i] == propertyName)
{
cmGeneratorExpression ge(context->Backtrace);
return ge.Parse(prop)->Evaluate(context->Makefile,
context->Config,
context->Quiet,
context->HeadTarget,
target,
&dagChecker);
}
}
return prop;
} }
} targetPropertyNode; } targetPropertyNode;

View File

@ -719,6 +719,28 @@ void cmTarget::DefineProperties(cmake *cm)
"If set, this property completely overrides the generic property " "If set, this property completely overrides the generic property "
"for the named configuration."); "for the named configuration.");
cm->DefineProperty
("INTERFACE_INCLUDE_DIRECTORIES", cmProperty::TARGET,
"List of public include directories for a library.",
"Targets may populate this property to publish the include directories "
"required to compile against the headers for the target. Consuming "
"targets can add entries to their own INCLUDE_DIRECTORIES property such "
"as $<TARGET_PROPERTY:foo,INTERFACE_INCLUDE_DIRECTORIES> to use the "
"include directories specified in the interface of 'foo'."
"\n"
CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS);
cm->DefineProperty
("INTERFACE_COMPILE_DEFINITIONS", cmProperty::TARGET,
"List of public compile definitions for a library.",
"Targets may populate this property to publish the compile definitions "
"required to compile against the headers for the target. Consuming "
"targets can add entries to their own COMPILE_DEFINITIONS property such "
"as $<TARGET_PROPERTY:foo,INTERFACE_COMPILE_DEFINITIONS> to use the "
"compile definitions specified in the interface of 'foo'."
"\n"
CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS);
cm->DefineProperty cm->DefineProperty
("LINK_INTERFACE_MULTIPLICITY", cmProperty::TARGET, ("LINK_INTERFACE_MULTIPLICITY", cmProperty::TARGET,
"Repetition count for STATIC libraries with cyclic dependencies.", "Repetition count for STATIC libraries with cyclic dependencies.",

View File

@ -90,9 +90,82 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3)
# Test exporting dependent libraries into different exports # Test exporting dependent libraries into different exports
add_library(testLibRequired testLibRequired.c) add_library(testLibRequired testLibRequired.c)
add_library(testLibDepends testLibDepends.c) add_library(testLibDepends testLibDepends.c)
set_property(TARGET testLibDepends APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET testLibDepends APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
)
set_property(TARGET testLibDepends APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
)
set_property(TARGET testLibDepends APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
)
target_link_libraries(testLibDepends testLibRequired) target_link_libraries(testLibDepends testLibRequired)
install(TARGETS testLibRequired EXPORT RequiredExp DESTINATION lib ) macro(add_include_lib _libName)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n")
add_library(${_libName} "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_libName}")
set_property(TARGET ${_libName} APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/${_libName}")
if (NOT "${ARGV1}" STREQUAL "NO_HEADER")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}/${_libName}.h" "// no content\n")
endif()
endmacro()
add_include_lib(testLibIncludeRequired1)
add_include_lib(testLibIncludeRequired2)
add_include_lib(testLibIncludeRequired3 NO_HEADER)
# Generate testLibIncludeRequired4 in the testLibIncludeRequired3 directory
# with an error. If the includes from testLibIncludeRequired3 appear first,
# the error will be hit.
# Below, the '3' library appears before the '4' library
# but we are testing that the INSTALL_INTERFACE causes it not to be used
# at build time.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testLibIncludeRequired3/testLibIncludeRequired4.h" "#error Should not be included\n")
add_include_lib(testLibIncludeRequired4)
add_include_lib(testLibIncludeRequired5 NO_HEADER)
# Generate testLibIncludeRequired6 in the testLibIncludeRequired5 directory
# with an error. If the includes from testLibIncludeRequired5 appear first,
# the error will be hit.
# Below, the '5' library appears before the '6' library
# but we are testing that when the installed IMPORTED target is used, from
# the Import side of this unit test, the '6' include from the '5' directory
# will not be used because it is in the BUILD_INTERFACE only.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testLibIncludeRequired5/testLibIncludeRequired6.h" "#error Should not be included\n")
add_include_lib(testLibIncludeRequired6)
set_property(TARGET testLibRequired APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibIncludeRequired1,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:$<1:$<TARGET_NAME:testLibIncludeRequired2>>,INTERFACE_INCLUDE_DIRECTORIES>
$<INSTALL_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired3,INTERFACE_INCLUDE_DIRECTORIES>>
$<BUILD_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired4,INTERFACE_INCLUDE_DIRECTORIES>>
$<BUILD_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired5,INTERFACE_INCLUDE_DIRECTORIES>>
$<INSTALL_INTERFACE:$<TARGET_PROPERTY:testLibIncludeRequired6,INTERFACE_INCLUDE_DIRECTORIES>>
)
set_property(TARGET testLibRequired APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS
testLibRequired_IFACE_DEFINE
$<BUILD_INTERFACE:BuildOnly_DEFINE>
$<INSTALL_INTERFACE:InstallOnly_DEFINE>
)
install(TARGETS testLibRequired
testLibIncludeRequired1
testLibIncludeRequired2
testLibIncludeRequired3
testLibIncludeRequired4
testLibIncludeRequired5
testLibIncludeRequired6
EXPORT RequiredExp DESTINATION lib )
install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired) install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired)
install(TARGETS testLibDepends EXPORT DependsExp DESTINATION lib ) install(TARGETS testLibDepends EXPORT DependsExp DESTINATION lib )

View File

@ -1,4 +1,20 @@
#include "testLibIncludeRequired1.h"
#include "testLibIncludeRequired2.h"
#include "testLibIncludeRequired4.h"
#ifndef testLibRequired_IFACE_DEFINE
#error Expected testLibRequired_IFACE_DEFINE
#endif
#ifndef BuildOnly_DEFINE
#error Expected BuildOnly_DEFINE
#endif
#ifdef InstallOnly_DEFINE
#error Unexpected InstallOnly_DEFINE
#endif
extern int testLibRequired(void); extern int testLibRequired(void);
int testLibDepends(void) { return testLibRequired(); } int testLibDepends(void) { return testLibRequired(); }

View File

@ -152,3 +152,18 @@ check_function_exists(testLib1 HAVE_TESTLIB1_FUNCTION)
if (NOT HAVE_TESTLIB1_FUNCTION) if (NOT HAVE_TESTLIB1_FUNCTION)
message(SEND_ERROR "Using imported target testLib2 in check_function_exists() failed !") message(SEND_ERROR "Using imported target testLib2 in check_function_exists() failed !")
endif() endif()
#-----------------------------------------------------------------------------
# Test that dependent imported targets have usable
# INTERFACE_COMPILE_DEFINITIONS and INTERFACE_INCLUDE_DIRECTORIES
add_library(deps_iface deps_iface.cpp)
target_link_libraries(deps_iface testLibsDepends)
set_property(TARGET deps_iface APPEND PROPERTY
COMPILE_DEFINITIONS
$<TARGET_PROPERTY:testLibDepends,INTERFACE_COMPILE_DEFINITIONS>
)
set_property(TARGET deps_iface APPEND PROPERTY
INCLUDE_DIRECTORIES
$<TARGET_PROPERTY:testLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
)

View File

@ -0,0 +1,24 @@
#include "testLibIncludeRequired1.h"
#include "testLibIncludeRequired2.h"
#include "testLibIncludeRequired6.h"
#ifndef testLibRequired_IFACE_DEFINE
#error Expected testLibRequired_IFACE_DEFINE
#endif
#ifdef BuildOnly_DEFINE
#error Unexpected BuildOnly_DEFINE
#endif
#ifndef InstallOnly_DEFINE
#error Expected InstallOnly_DEFINE
#endif
extern int testLibDepends(void);
int main(int,char **)
{
return testLibDepends();
}

View File

@ -37,6 +37,37 @@ include_directories("sing$<1:/ting>")
include_directories("$<1:${CMAKE_CURRENT_BINARY_DIR}/arguments;${CMAKE_CURRENT_BINARY_DIR}/list>") include_directories("$<1:${CMAKE_CURRENT_BINARY_DIR}/arguments;${CMAKE_CURRENT_BINARY_DIR}/list>")
create_header(fee)
create_header(fiy)
create_header(foh)
create_header(fum)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib1.cpp" "#include \"fee.h\"\n")
add_library(lib1 "${CMAKE_CURRENT_BINARY_DIR}/lib1.cpp")
set_property(TARGET lib1 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fee")
set_property(TARGET lib1 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy")
set_property(TARGET lib1 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:${CMAKE_CURRENT_BINARY_DIR}/foh>")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib2.cpp" "#include \"fiy.h\"\n")
add_library(lib2 "${CMAKE_CURRENT_BINARY_DIR}/lib2.cpp")
set_property(TARGET lib2 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fum;$<TARGET_PROPERTY:lib1,INTERFACE_INCLUDE_DIRECTORIES>")
set_property(TARGET lib2 APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:lib1,INTERFACE_INCLUDE_DIRECTORIES>")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main3.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n#include \"fum.h\"\nint main(int,char**) { return 0; }\n")
add_executable(exe3 "${CMAKE_CURRENT_BINARY_DIR}/main3.cpp")
set_property(TARGET exe3 APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:lib2,INTERFACE_INCLUDE_DIRECTORIES>")
# Test cycles
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib3.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n")
add_library(lib3 "${CMAKE_CURRENT_BINARY_DIR}/lib3.cpp")
set_property(TARGET lib3 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy;$<TARGET_PROPERTY:lib4,INTERFACE_INCLUDE_DIRECTORIES>")
set_property(TARGET lib3 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/fiy;$<TARGET_PROPERTY:lib4,INTERFACE_INCLUDE_DIRECTORIES>")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib4.cpp" "#include \"fiy.h\"\n#include \"foh.h\"\n")
add_library(lib4 "${CMAKE_CURRENT_BINARY_DIR}/lib4.cpp")
set_property(TARGET lib4 APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/foh;$<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>")
set_property(TARGET lib4 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/foh;$<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>")
add_library(somelib::withcolons UNKNOWN IMPORTED) add_library(somelib::withcolons UNKNOWN IMPORTED)
set_property(TARGET somelib::withcolons PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/target") set_property(TARGET somelib::withcolons PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/target")
set_property(TARGET somelib::withcolons PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/target") set_property(TARGET somelib::withcolons PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/target")