Export: Generate INTERFACE_LINK_LIBRARIES property on targets.

This property is generated only for targets which have recorded
policy CMP0022 as NEW, and a compatibility mode is added to
additionally export the old interfaces in that case too.

If the old interfaces are not exported, the generated export files
require CMake 2.8.12. Because the unit tests use a version which
is not yet called 2.8.12, temporarily require a lower version.
This commit is contained in:
Stephen Kelly 2013-06-04 16:47:57 +02:00
parent d0a76ea07c
commit 574fec97fd
31 changed files with 265 additions and 8 deletions

View File

@ -77,6 +77,15 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
properties, missingTargets);
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
te, properties);
const bool newCMP0022Behavior =
te->GetPolicyStatusCMP0022() != cmPolicies::WARN
&& te->GetPolicyStatusCMP0022() != cmPolicies::OLD;
if (newCMP0022Behavior)
{
this->PopulateInterfaceLinkLibrariesProperty(te,
cmGeneratorExpression::BuildInterface,
properties, missingTargets);
}
this->PopulateCompatibleInterfaceProperties(te, properties);
this->GenerateInterfaceProperties(te, os, properties);

View File

@ -30,6 +30,7 @@ cmExportCommand::cmExportCommand()
,Append(&Helper, "APPEND", &ArgumentGroup)
,Namespace(&Helper, "NAMESPACE", &ArgumentGroup)
,Filename(&Helper, "FILE", &ArgumentGroup)
,ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
{
// at first TARGETS
this->Targets.Follows(0);
@ -158,6 +159,7 @@ bool cmExportCommand
ebfg.SetAppendMode(this->Append.IsEnabled());
ebfg.SetExports(&targets);
ebfg.SetCommand(this);
ebfg.SetExportOld(this->ExportOld.IsEnabled());
// Compute the set of configurations exported.
std::vector<std::string> configurationTypes;

View File

@ -63,7 +63,7 @@ public:
{
return
" export(TARGETS [target1 [target2 [...]]] [NAMESPACE <namespace>]\n"
" [APPEND] FILE <filename>)\n"
" [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES])\n"
"Create a file <filename> that may be included by outside projects to "
"import targets from the current project's build tree. "
"This is useful during cross-compiling to build utility executables "
@ -73,6 +73,10 @@ public:
"prepended to all target names written to the file. "
"If the APPEND option is given the generated code will be appended "
"to the file instead of overwriting it. "
"The EXPORT_LINK_INTERFACE_LIBRARIES keyword, if present, causes the "
"contents of the properties matching "
"(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)? to be exported, when "
"policy CMP0022 is NEW. "
"If a library target is included in the export but "
"a target to which it links is not included the behavior is "
"unspecified."
@ -104,6 +108,7 @@ private:
cmCAEnabler Append;
cmCAString Namespace;
cmCAString Filename;
cmCAEnabler ExportOld;
friend class cmExportBuildFileGenerator;
std::string ErrorMessage;

View File

@ -30,6 +30,7 @@
cmExportFileGenerator::cmExportFileGenerator()
{
this->AppendMode = false;
this->ExportOld = false;
}
//----------------------------------------------------------------------------
@ -168,6 +169,39 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
}
}
void cmExportFileGenerator::GenerateRequiredCMakeVersion(std::ostream& os,
const char *versionString)
{
os << "if(CMAKE_VERSION VERSION_LESS " << versionString << ")\n"
" message(FATAL_ERROR \"This file relies on consumers using "
"CMake " << versionString << " or greater.\")\n"
"endif()\n\n";
}
//----------------------------------------------------------------------------
bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
cmTarget *target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap &properties,
std::vector<std::string> &missingTargets)
{
const char *input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
if (input)
{
std::string prepro = cmGeneratorExpression::Preprocess(input,
preprocessRule);
if (!prepro.empty())
{
this->ResolveTargetsInGeneratorExpressions(prepro, target,
missingTargets,
ReplaceFreeTargets);
properties["INTERFACE_LINK_LIBRARIES"] = prepro;
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
static bool isSubDirectory(const char* a, const char* b)
{
@ -583,6 +617,22 @@ cmExportFileGenerator
return;
}
const bool newCMP0022Behavior =
target->GetPolicyStatusCMP0022() != cmPolicies::WARN
&& target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
if(newCMP0022Behavior && !this->ExportOld)
{
cmMakefile *mf = target->GetMakefile();
cmOStringStream e;
e << "Target \"" << target->GetName() << "\" has policy CMP0022 enabled, "
"but also has old-style INTERFACE_LINK_LIBRARIES properties "
"populated, but it was exported without the "
"EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return;
}
if (!*propContent)
{
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = "";

View File

@ -35,6 +35,8 @@ public:
/** Set the namespace in which to place exported target names. */
void SetNamespace(const char* ns) { this->Namespace = ns; }
void SetExportOld(bool exportOld) { this->ExportOld = exportOld; }
/** Add a configuration to be exported. */
void AddConfiguration(const char* config);
@ -101,6 +103,10 @@ protected:
cmGeneratorExpression::PreprocessContext,
ImportPropertyMap &properties,
std::vector<std::string> &missingTargets);
bool PopulateInterfaceLinkLibrariesProperty(cmTarget *target,
cmGeneratorExpression::PreprocessContext,
ImportPropertyMap &properties,
std::vector<std::string> &missingTargets);
void PopulateInterfaceProperty(const char *propName, cmTarget *target,
ImportPropertyMap &properties);
void PopulateCompatibleInterfaceProperties(cmTarget *target,
@ -128,9 +134,14 @@ protected:
std::vector<std::string> &missingTargets,
FreeTargetsReplace replace = NoReplaceFreeTargets);
void GenerateRequiredCMakeVersion(std::ostream& os,
const char *versionString);
// The namespace in which the exports are placed in the generated file.
std::string Namespace;
bool ExportOld;
// The set of configurations to export.
std::vector<std::string> Configurations;

View File

@ -113,6 +113,7 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
std::vector<std::string> missingTargets;
bool require2_8_12 = false;
// Create all the imported targets.
for(std::vector<cmTarget*>::const_iterator
tei = allTargets.begin();
@ -134,6 +135,20 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
te,
cmGeneratorExpression::InstallInterface,
properties, missingTargets);
const bool newCMP0022Behavior =
te->GetPolicyStatusCMP0022() != cmPolicies::WARN
&& te->GetPolicyStatusCMP0022() != cmPolicies::OLD;
if (newCMP0022Behavior)
{
if (this->PopulateInterfaceLinkLibrariesProperty(te,
cmGeneratorExpression::InstallInterface,
properties, missingTargets)
&& !this->ExportOld)
{
require2_8_12 = true;
}
}
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
te, properties);
this->PopulateCompatibleInterfaceProperties(te, properties);
@ -141,6 +156,10 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateInterfaceProperties(te, os, properties);
}
if (require2_8_12)
{
this->GenerateRequiredCMakeVersion(os, "2.8.11.20130626");
}
// Now load per-configuration properties for them.
os << "# Load information for each installed configuration.\n"

View File

@ -1196,6 +1196,8 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
cmInstallCommandArguments ica(this->DefaultComponentName);
cmCAString exp(&ica.Parser, "EXPORT");
cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup);
cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES",
&ica.ArgumentGroup);
cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup);
exp.Follows(0);
@ -1268,15 +1270,40 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
}
}
cmExportSet *exportSet = this->Makefile->GetLocalGenerator()
->GetGlobalGenerator()->GetExportSets()[exp.GetString()];
if (exportOld.IsEnabled())
{
for(std::vector<cmTargetExport*>::const_iterator
tei = exportSet->GetTargetExports()->begin();
tei != exportSet->GetTargetExports()->end(); ++tei)
{
cmTargetExport const* te = *tei;
const bool newCMP0022Behavior =
te->Target->GetPolicyStatusCMP0022() != cmPolicies::WARN
&& te->Target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
if(!newCMP0022Behavior)
{
cmOStringStream e;
e << "INSTALL(EXPORT) given keyword \""
<< "EXPORT_LINK_INTERFACE_LIBRARIES" << "\", but target \""
<< te->Target->GetName()
<< "\" does not have policy CMP0022 set to NEW.";
this->SetError(e.str().c_str());
return false;
}
}
}
// Create the export install generator.
cmInstallExportGenerator* exportGenerator =
new cmInstallExportGenerator(
this->Makefile->GetLocalGenerator()
->GetGlobalGenerator()->GetExportSets()[exp.GetString()],
exportSet,
ica.GetDestination().c_str(),
ica.GetPermissions().c_str(), ica.GetConfigurations(),
ica.GetComponent().c_str(), fname.c_str(),
name_space.GetCString(), this->Makefile);
name_space.GetCString(), exportOld.IsEnabled(), this->Makefile);
this->Makefile->AddInstallGenerator(exportGenerator);
return true;

View File

@ -291,6 +291,7 @@ public:
" [NAMESPACE <namespace>] [FILE <name>.cmake]\n"
" [PERMISSIONS permissions...]\n"
" [CONFIGURATIONS [Debug|Release|...]]\n"
" [EXPORT_LINK_INTERFACE_LIBRARIES]\n"
" [COMPONENT <component>])\n"
"The EXPORT form generates and installs a CMake file containing code "
"to import targets from the installation tree into another project. "
@ -306,6 +307,10 @@ public:
"installed when one of the named configurations is installed. "
"Additionally, the generated import file will reference only the "
"matching target configurations. "
"The EXPORT_LINK_INTERFACE_LIBRARIES keyword, if present, causes the "
"contents of the properties matching "
"(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)? to be exported, when "
"policy CMP0022 is NEW. "
"If a COMPONENT option is specified that does not match that given "
"to the targets associated with <export-name> the behavior is "
"undefined. "

View File

@ -33,12 +33,14 @@ cmInstallExportGenerator::cmInstallExportGenerator(
std::vector<std::string> const& configurations,
const char* component,
const char* filename, const char* name_space,
bool exportOld,
cmMakefile* mf)
:cmInstallGenerator(destination, configurations, component)
,ExportSet(exportSet)
,FilePermissions(file_permissions)
,FileName(filename)
,Namespace(name_space)
,ExportOld(exportOld)
,Makefile(mf)
{
this->EFGen = new cmExportInstallFileGenerator(this);
@ -137,6 +139,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os)
// Generate the import file for this export set.
this->EFGen->SetExportFile(this->MainImportFile.c_str());
this->EFGen->SetNamespace(this->Namespace.c_str());
this->EFGen->SetExportOld(this->ExportOld);
if(this->ConfigurationTypes->empty())
{
if(this->ConfigurationName && *this->ConfigurationName)

View File

@ -31,7 +31,7 @@ public:
const std::vector<std::string>& configurations,
const char* component,
const char* filename, const char* name_space,
cmMakefile* mf);
bool exportOld, cmMakefile* mf);
~cmInstallExportGenerator();
cmExportSet* GetExportSet() {return this->ExportSet;}
@ -52,6 +52,7 @@ protected:
std::string FilePermissions;
std::string FileName;
std::string Namespace;
bool ExportOld;
cmMakefile* Makefile;
std::string TempDir;

View File

@ -559,6 +559,11 @@ cmPolicies::cmPolicies()
"and INTERFACE_COMPILE_OPTIONS properties. For in-build targets, CMake "
"will use the INTERFACE_LINK_LIBRARIES property as the source of the "
"link interface only if policy CMP0022 is NEW. "
"When exporting a target which has this policy set to NEW, only the "
"INTERFACE_LINK_LIBRARIES property will be processed and generated for "
"the IMPORTED target by default. A new option to the install(EXPORT) "
"and export commands allows export of the old-style properties for "
"compatibility with downstream users of CMake versions older than 2.8.12."
"\n"
"The OLD behavior for this policy is to ignore the "
"INTERFACE_LINK_LIBRARIES property for in-build targets. "

View File

@ -5938,11 +5938,16 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
// Get the link interface.
{
std::string linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
linkProp += suffix;
std::string linkProp = "INTERFACE_LINK_LIBRARIES";
const char *propertyLibs = this->GetProperty(linkProp.c_str());
if (!propertyLibs)
{
linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
linkProp += suffix;
propertyLibs = this->GetProperty(linkProp.c_str());
}
if(!propertyLibs)
{
linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";

View File

@ -232,6 +232,33 @@ target_link_libraries(testSharedLibDepends LINK_PUBLIC renamed_on_export)
target_link_libraries(testSharedLibDepends LINK_INTERFACE_LIBRARIES
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:$<TARGET_NAME:testSharedLibRequired>>)
add_library(cmp0022OLD SHARED cmp0022_vs6_1.cpp)
generate_export_header(cmp0022OLD BASE_NAME cmp0022)
target_include_directories(cmp0022OLD PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/cmp0022>"
)
cmake_policy(SET CMP0022 NEW)
add_library(cmp0022NEW SHARED cmp0022_vs6_1.cpp)
target_include_directories(cmp0022NEW PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/cmp0022>"
)
cmake_policy(SET CMP0022 OLD)
install(FILES
"${CMAKE_CURRENT_SOURCE_DIR}/cmp0022.h"
"${CMAKE_CURRENT_BINARY_DIR}/cmp0022_export.h"
DESTINATION include/cmp0022
)
set_property(TARGET testLib2 APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USING_TESTLIB2)
set_property(TARGET testLib3 APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USING_TESTLIB3)
set_property(TARGET cmp0022NEW APPEND PROPERTY INTERFACE_LINK_LIBRARIES testLib2)
# set_property(TARGET cmp0022NEW APPEND PROPERTY LINK_INTERFACE_LIBRARIES testLibIncludeRequired2) # TODO: Test for error
set_property(TARGET cmp0022OLD APPEND PROPERTY INTERFACE_LINK_LIBRARIES testLib2)
set_property(TARGET cmp0022OLD APPEND PROPERTY LINK_INTERFACE_LIBRARIES testLib3)
install(TARGETS testLibRequired
testLibIncludeRequired1
testLibIncludeRequired2
@ -273,6 +300,7 @@ install(
testExe2lib testLib4lib testLib4libdbg testLib4libopt
testLib6
testLibCycleA testLibCycleB
cmp0022NEW cmp0022OLD
EXPORT exp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib NAMELINK_SKIP
@ -316,6 +344,7 @@ add_subdirectory(sublib) # For CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE test.
export(TARGETS testExe1 testLib1 testLib2 testLib3
testExe2libImp testLib3Imp testLib3ImpDep subdirlib
testSharedLibRequired testSharedLibDepends renamed_on_export
cmp0022NEW cmp0022OLD
NAMESPACE bld_
FILE ExportBuildTree.cmake
)

View File

@ -0,0 +1,7 @@
#include "cmp0022.h"
int cmp0022()
{
return 0;
}

View File

@ -0,0 +1,4 @@
#include "cmp0022_export.h"
int CMP0022_EXPORT cmp0022();

View File

@ -0,0 +1 @@
#include "cmp0022.cpp"

View File

@ -0,0 +1 @@
#include "cmp0022.cpp"

View File

@ -68,6 +68,16 @@ target_link_libraries(imp_testExe1b
bld_testLibCycleA
)
add_executable(cmp0022OLD_test cmp0022OLD_test_vs6_1.cpp)
target_link_libraries(cmp0022OLD_test bld_cmp0022OLD)
add_executable(cmp0022NEW_test cmp0022NEW_test_vs6_1.cpp)
target_link_libraries(cmp0022NEW_test bld_cmp0022NEW)
add_executable(cmp0022OLD_exp_test cmp0022OLD_test_vs6_2.cpp)
target_link_libraries(cmp0022OLD_exp_test exp_cmp0022OLD)
add_executable(cmp0022NEW_exp_test cmp0022NEW_test_vs6_2.cpp)
target_link_libraries(cmp0022NEW_exp_test exp_cmp0022NEW)
# Try building a plugin to an executable imported from the build tree.
add_library(imp_mod1b MODULE imp_mod1.c)
target_link_libraries(imp_mod1b bld_testExe2)

View File

@ -0,0 +1,12 @@
#ifndef USING_TESTLIB2
#error Expected USING_TESTLIB2
#endif
#ifdef USING_TESTLIB3
#error Unexpected USING_TESTLIB3
#endif
int main(void)
{
return 0;
}

View File

@ -0,0 +1 @@
#include "cmp0022NEW_test.cpp"

View File

@ -0,0 +1 @@
#include "cmp0022NEW_test.cpp"

View File

@ -0,0 +1,12 @@
#ifdef USING_TESTLIB2
#error Unexpected USING_TESTLIB2
#endif
#ifndef USING_TESTLIB3
#error Expected USING_TESTLIB3
#endif
int main(void)
{
return 0;
}

View File

@ -0,0 +1 @@
#include "cmp0022OLD_test.cpp"

View File

@ -0,0 +1 @@
#include "cmp0022OLD_test.cpp"

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at CMP0022-export.cmake:11 \(export\):
Target "cmp0022NEW" has policy CMP0022 enabled, but also has old-style
INTERFACE_LINK_LIBRARIES properties populated, but it was exported without
the EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties

View File

@ -0,0 +1,11 @@
project(cmp0022NEW)
cmake_policy(SET CMP0022 NEW)
add_library(cmp0022NEW SHARED empty_vs6_1.cpp)
add_library(testLib SHARED empty_vs6_2.cpp)
set_property(TARGET cmp0022NEW APPEND PROPERTY LINK_INTERFACE_LIBRARIES testLib)
export(TARGETS cmp0022NEW testLib FILE "${CMAKE_CURRENT_BINARY_DIR}/cmp0022NEW.cmake")

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error in CMakeLists.txt:
Target "cmp0022NEW" has policy CMP0022 enabled, but also has old-style
INTERFACE_LINK_LIBRARIES properties populated, but it was exported without
the EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties

View File

@ -0,0 +1,12 @@
project(cmp0022NEW)
cmake_policy(SET CMP0022 NEW)
add_library(cmp0022NEW SHARED empty_vs6_1.cpp)
add_library(testLib SHARED empty_vs6_2.cpp)
set_property(TARGET cmp0022NEW APPEND PROPERTY LINK_INTERFACE_LIBRARIES testLib)
install(TARGETS cmp0022NEW testLib EXPORT exp DESTINATION lib)
install(EXPORT exp FILE expTargets.cmake DESTINATION lib/cmake/exp)

View File

@ -3,3 +3,5 @@ include(RunCMake)
run_cmake(CMP0022-WARN)
run_cmake(CMP0022-WARN-static)
run_cmake(CMP0022-NOWARN-static)
run_cmake(CMP0022-export)
run_cmake(CMP0022-install-export)