From 30268b46f8237f25c82858693c000f5da8ede6ad Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 31 Jan 2013 11:18:49 +0100 Subject: Handle reading empty properties defined by the link interface. This was segfaulting before. --- Source/cmGeneratorExpressionEvaluator.cxx | 4 +++- Tests/CompatibleInterface/CMakeLists.txt | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index f74b69e..55f54e4 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -465,9 +465,11 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (target->IsLinkInterfaceDependentStringProperty(propertyName, context->Config)) { - return target->GetLinkInterfaceDependentStringProperty( + const char *propContent = + target->GetLinkInterfaceDependentStringProperty( propertyName, context->Config); + return propContent ? propContent : ""; } return std::string(); diff --git a/Tests/CompatibleInterface/CMakeLists.txt b/Tests/CompatibleInterface/CMakeLists.txt index 259b5a1..329510b 100644 --- a/Tests/CompatibleInterface/CMakeLists.txt +++ b/Tests/CompatibleInterface/CMakeLists.txt @@ -48,10 +48,22 @@ target_compile_definitions(CompatibleInterface add_library(iface2 SHARED iface2.cpp) generate_export_header(iface2) +set_property(TARGET iface2 APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING + Iface2_PROP +) + # For the LINK_LIBRARIES and related properties, we should not evaluate # properties defined only in the interface - they should be implicitly zero set_property(TARGET iface2 APPEND PROPERTY LINK_INTERFACE_LIBRARIES $<$>:nonexistant> ) -target_link_libraries(CompatibleInterface iface2) +target_link_libraries(CompatibleInterface iface2 + $<$>:nonexistant> +) +# Test that this does not segfault: +target_compile_definitions(CompatibleInterface + PRIVATE + $<$>:SOME_DEFINE> +) -- cgit v0.12 From 7ceeba992b4fb35ca05760b3170e68f41dfc1bb5 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 23:31:54 +0100 Subject: Advance more when preprocessing exported strings. When evaluating TARGET_PROPERTY here, we can skip to the comma location. We need to calculate it though as the string may have just been changed. --- Source/cmExportFileGenerator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index dbe64e9..e772327 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -391,7 +391,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression( { input.replace(nameStartPos, commaPos - nameStartPos, targetName); } - lastPos = pos + targetName.size(); + lastPos = nameStartPos + targetName.size() + 1; } std::string errorString; -- cgit v0.12 From df4d2b28b24a3172daf1290070199633f7c46cf0 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 17:49:47 +0100 Subject: Make it an error for INSTALL_PREFIX to be evaluated. An empty string is not likely going to produce expected results in any evaluation context. It is replaced by preprocessing currently. --- Source/cmGeneratorExpressionEvaluator.cxx | 9 ++++++--- Tests/GeneratorExpression/CMakeLists.txt | 1 - Tests/GeneratorExpression/check-part2.cmake | 1 - Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt | 1 + Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt | 9 +++++++++ Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake | 3 +++ Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake | 1 + 7 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt create mode 100644 Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt create mode 100644 Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index 55f54e4..fff7dab 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -621,14 +621,17 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode { InstallPrefixNode() {} - virtual bool GeneratesContent() const { return false; } + virtual bool GeneratesContent() const { return true; } virtual int NumExpectedParameters() const { return 0; } std::string Evaluate(const std::vector &, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, cmGeneratorExpressionDAGChecker *) const { + reportError(context, content->GetOriginalExpression(), + "INSTALL_PREFIX is a marker for install(EXPORT) only. It " + "should never be evaluated."); return std::string(); } diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index a40a541..ecbbedf 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -89,7 +89,6 @@ add_custom_target(check-part2 ALL -Dtest_install_interface=$ -Dtest_target_name_1=$ -Dtest_target_name_2=$ - -Dtest_install_prefix=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)" VERBATIM diff --git a/Tests/GeneratorExpression/check-part2.cmake b/Tests/GeneratorExpression/check-part2.cmake index 0b50204..8855a97 100644 --- a/Tests/GeneratorExpression/check-part2.cmake +++ b/Tests/GeneratorExpression/check-part2.cmake @@ -26,4 +26,3 @@ check(test_build_interface "build") check(test_install_interface "") check(test_target_name_1 "tgt,ok") check(test_target_name_2 "tgt:ok") -check(test_install_prefix "") diff --git a/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt new file mode 100644 index 0000000..271eb6e --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at BadInstallPrefix.cmake:1 \(add_custom_target\): + Error evaluating generator expression: + + \$ + + INSTALL_PREFIX is a marker for install\(EXPORT\) only. It should never be + evaluated. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake new file mode 100644 index 0000000..fcfc3eb --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake @@ -0,0 +1,3 @@ +add_custom_target(check ALL COMMAND check + $/include + VERBATIM) diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 8a69675..62bf29b 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -7,3 +7,4 @@ run_cmake(BadNOT) run_cmake(BadStrEqual) run_cmake(BadZero) run_cmake(BadTargetName) +run_cmake(BadInstallPrefix) -- cgit v0.12 From d4297d5697cd10114f8accb7a233aa1f5ebc50ab Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 08:24:59 +0100 Subject: Export targets to a targets file, not a Config file. --- Tests/ExportImport/Export/CMakeLists.txt | 4 ++-- Tests/ExportImport/Import/A/CMakeLists.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 47521d8..ae44c28 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -234,10 +234,10 @@ install(TARGETS testLibRequired testLibIncludeRequired6 testSharedLibRequired EXPORT RequiredExp DESTINATION lib ) -install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired) +install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredTargets.cmake DESTINATION lib/cmake/testLibRequired) install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib ) -install(EXPORT DependsExp FILE testLibDependsConfig.cmake DESTINATION lib/cmake/testLibDepends) +install(EXPORT DependsExp FILE testLibDependsTargets.cmake DESTINATION lib/cmake/testLibDepends) # Install and export from install tree. diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index e9bd2ba..0675594 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -5,8 +5,8 @@ include(${Import_BINARY_DIR}/../Export/ExportBuildTree.cmake) include(${CMAKE_INSTALL_PREFIX}/lib/exp/exp.cmake) # Import two exports, where the Depends one depends on an exported target from the Required one: -include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredConfig.cmake) -include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsConfig.cmake) +include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredTargets.cmake) +include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsTargets.cmake) # Try referencing an executable imported from the install tree. add_custom_command( -- cgit v0.12 From 2c3654c3de718fe822f8960063373774fc019494 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Tue, 29 Jan 2013 16:32:51 +0100 Subject: Add a way to exclude INTERFACE properties from exported targets. Projects set interface requirements upstream, and existing downstreams use of target_link_libraries will consume those interfaces. This can create a backward compatibility concern as the result may be changing the order of include directories of downstreams, or another side-effect of using the INTERFACE properties. Provide a way for them to emulate the behavior of a version-based policy in the config file. --- Source/cmExportFileGenerator.cxx | 7 ++++--- Source/cmFindPackageCommand.cxx | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index e772327..2ecac84 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -264,15 +264,16 @@ void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target, { if (!properties.empty()) { + os << "if(NOT ${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES)\n"; std::string targetName = this->Namespace; targetName += target->GetName(); - os << "set_target_properties(" << targetName << " PROPERTIES\n"; + os << " set_target_properties(" << targetName << " PROPERTIES\n"; for(ImportPropertyMap::const_iterator pi = properties.begin(); pi != properties.end(); ++pi) { - os << " " << pi->first << " \"" << pi->second << "\"\n"; + os << " " << pi->first << " \"" << pi->second << "\"\n"; } - os << ")\n\n"; + os << " )\nendif()\n\n"; } } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 470ceca..e9b6ab6 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -376,6 +376,27 @@ void cmFindPackageCommand::GenerateDocumentation() "The package configuration file may set _FOUND to false " "to tell find_package that component requirements are not satisfied." "\n" + "Targets exported by the install() command may have additional INTERFACE " + "properties set for include directories, compile definitions etc. As " + "these may create a backward compatibility concern for consumers of " + "existing projects, it is possible to set the _NO_INTERFACES " + "variable to disable populating those interfaces. It is possible to " + "emulate a version-based policy for whether the interfaces should be " + "enabled in the config file before including the imported targets file. " + "This allows consumers to decide when to enable the new interfaces when " + "upgrading. The upstream project is responsible for adding code similar " + "to this in the version which generates the INTERFACE content:\n" + " if(${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION VERSION_LESS 2.3\n" + " AND NOT ${CMAKE_FIND_PACKAGE_NAME}_INTERFACES)\n" + " set(${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES 1)\n" + " endif()\n" + " include(\"${CMAKE_CURRENT_LIST_DIR}/Targets.cmake\")\n" + "\n" + "Downstream users may then choose to use the new interfaces by finding " + "the newer version of upstream. They can also choose to not use the new " + "interfaces by setting _INTERFACES to false before finding the " + "package.\n" + "\n" "See the cmake_policy() command documentation for discussion of the " "NO_POLICY_SCOPE option." ; -- cgit v0.12 From 0fa7f69c0e2cdcd8b7ece400651ee7821b2ede4b Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 23:35:22 +0100 Subject: Add API to check if we're reading a includes or defines property. This will allow the implementation of the LINKED generator expression. --- Source/cmGeneratorExpressionDAGChecker.cxx | 16 ++++++++++++++++ Source/cmGeneratorExpressionDAGChecker.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 269211b..bcb0820 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -125,3 +125,19 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries() || strncmp(prop, "LINK_INTERFACE_LIBRARIES_", 26) == 0 || strncmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_", 35) == 0); } + +//---------------------------------------------------------------------------- +bool cmGeneratorExpressionDAGChecker::EvaluatingIncludeDirectories() +{ + const char *prop = this->Property.c_str(); + return (strcmp(prop, "INCLUDE_DIRECTORIES") == 0 + || strcmp(prop, "INTERFACE_INCLUDE_DIRECTORIES") == 0 ); +} + +//---------------------------------------------------------------------------- +bool cmGeneratorExpressionDAGChecker::EvaluatingCompileDefinitions() +{ + const char *prop = this->Property.c_str(); + return (strcmp(prop, "COMPILE_DEFINITIONS") == 0 + || strcmp(prop, "INTERFACE_COMPILE_DEFINITIONS") == 0 ); +} diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 3169291..7e9c62a 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -37,6 +37,8 @@ struct cmGeneratorExpressionDAGChecker const std::string &expr); bool EvaluatingLinkLibraries(); + bool EvaluatingIncludeDirectories(); + bool EvaluatingCompileDefinitions(); private: Result checkGraph() const; -- cgit v0.12 From 0b92602b816e2584db3781b120a1e5200da72ada Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 23:38:04 +0100 Subject: Add the $ generator expression. This is both a short form of using a TARGET_DEFINED expression together with a TARGET_PROPERTY definition, and a way to strip non-target content from interface properties when exporting. --- Source/cmDocumentGeneratorExpressions.h | 8 ++ Source/cmExportFileGenerator.cxx | 66 +++++++++++++++-- Source/cmExportFileGenerator.h | 4 +- Source/cmGeneratorExpressionEvaluator.cxx | 85 ++++++++++++++++++++++ .../target_compile_definitions/CMakeLists.txt | 6 ++ .../target_compile_definitions/consumer.cpp | 4 + .../target_include_directories/CMakeLists.txt | 11 ++- .../target_include_directories/consumer.cpp | 5 ++ .../BadLinked-result.txt | 1 + .../BadLinked-stderr.txt | 7 ++ .../BadLinked.cmake | 7 ++ .../RunCMakeTest.cmake | 1 + 12 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt create mode 100644 Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt create mode 100644 Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake diff --git a/Source/cmDocumentGeneratorExpressions.h b/Source/cmDocumentGeneratorExpressions.h index 8b80a8a..3993f7d 100644 --- a/Source/cmDocumentGeneratorExpressions.h +++ b/Source/cmDocumentGeneratorExpressions.h @@ -51,6 +51,14 @@ "on the target tgt.\n" \ "Note that tgt is not added as a dependency of the target this " \ "expression is evaluated on.\n" \ + " $ = An empty string if item is not a " \ + "target. If item is a target then the " \ + "INTERFACE_INCLUDE_DIRECTORIES or INTERFACE_COMPILE_DEFINITIONS " \ + "content is read from the target. " \ + "This generator expression can only be used in evaluation of the " \ + "INCLUDE_DIRECTORIES or COMPILE_DEFINITIONS property. Note that " \ + "this expression is for internal use and may be changed or removed " \ + "in the future.\n" \ " $ = '1' if the policy was NEW when " \ "the 'head' target was created, else '0'. If the policy was not " \ "set, the warning message for the policy will be emitted. This " \ diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 2ecac84..7e4c3df 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -25,6 +25,8 @@ #include +#include "assert.h" + //---------------------------------------------------------------------------- cmExportFileGenerator::cmExportFileGenerator() { @@ -160,7 +162,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, preprocessRule); if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target, + this->ResolveTargetsInGeneratorExpressions(prepro, target, propName, missingTargets); properties[outputName] = prepro; } @@ -324,13 +326,14 @@ static bool isGeneratorExpression(const std::string &lib) void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( std::string &input, - cmTarget* target, + cmTarget* target, const char *propName, std::vector &missingTargets, FreeTargetsReplace replace) { if (replace == NoReplaceFreeTargets) { - this->ResolveTargetsInGeneratorExpression(input, target, missingTargets); + this->ResolveTargetsInGeneratorExpression(input, target, propName, + missingTargets); return; } std::vector parts; @@ -349,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( { this->ResolveTargetsInGeneratorExpression( *li, - target, + target, propName, missingTargets); } input += sep + *li; @@ -361,7 +364,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( std::string &input, - cmTarget* target, + cmTarget* target, const char *propName, std::vector &missingTargets) { std::string::size_type pos = 0; @@ -398,6 +401,57 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression( std::string errorString; pos = 0; lastPos = pos; + while((pos = input.find("$", nameStartPos); + if (endPos == input.npos) + { + errorString = "$ expression incomplete"; + break; + } + std::string targetName = input.substr(nameStartPos, + endPos - nameStartPos); + if(targetName.find("$<") != input.npos) + { + errorString = "$ requires its parameter to be a " + "literal."; + break; + } + if (this->AddTargetNamespace(targetName, target, missingTargets)) + { + assert(propName); // The link libraries strings will + // never contain $ + std::string replacement = "$IssueMessage(cmake::FATAL_ERROR, errorString); + return; + } + + pos = 0; + lastPos = pos; while((pos = input.find("$ResolveTargetsInGeneratorExpressions(prepro, target, + this->ResolveTargetsInGeneratorExpressions(prepro, target, 0, missingTargets, ReplaceFreeTargets); properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro; diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 776be61..5ad27bf 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -119,7 +119,7 @@ protected: }; void ResolveTargetsInGeneratorExpressions(std::string &input, - cmTarget* target, + cmTarget* target, const char *propName, std::vector &missingTargets, FreeTargetsReplace replace = NoReplaceFreeTargets); @@ -150,7 +150,7 @@ private: std::vector &missingTargets); void ResolveTargetsInGeneratorExpression(std::string &input, - cmTarget* target, + cmTarget* target, const char *propName, std::vector &missingTargets); virtual void ReplaceInstallPrefix(std::string &input); diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index fff7dab..cbea1d9 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -638,6 +638,89 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode } installPrefixNode; //---------------------------------------------------------------------------- +static const struct LinkedNode : public cmGeneratorExpressionNode +{ + LinkedNode() {} + + virtual bool GeneratesContent() const { return true; } + virtual int NumExpectedParameters() const { return 1; } + virtual bool RequiresLiteralInput() const { return true; } + + std::string Evaluate(const std::vector ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (dagChecker->EvaluatingIncludeDirectories()) + { + return this->GetInterfaceProperty(parameters.front(), + "INCLUDE_DIRECTORIES", + context, content, dagChecker); + } + if (dagChecker->EvaluatingCompileDefinitions()) + { + return this->GetInterfaceProperty(parameters.front(), + "COMPILE_DEFINITIONS", + context, content, dagChecker); + } + + reportError(context, content->GetOriginalExpression(), + "$ may only be used in INCLUDE_DIRECTORIES and " + "COMPILE_DEFINITIONS properties."); + + return std::string(); + } + +private: + std::string GetInterfaceProperty(const std::string &item, + const std::string &prop, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagCheckerParent) const + { + cmTarget *target = context->CurrentTarget + ->GetMakefile()->FindTargetToUse(item.c_str()); + if (!target) + { + return std::string(); + } + std::string propertyName = "INTERFACE_" + prop; + const char *propContent = target->GetProperty(propertyName.c_str()); + if (!propContent) + { + return std::string(); + } + + cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, + target->GetName(), + propertyName, + content, + dagCheckerParent); + + switch (dagChecker.check()) + { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.reportError(context, content->GetOriginalExpression()); + return std::string(); + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + return std::string(); + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + cmGeneratorExpression ge(context->Backtrace); + return ge.Parse(propContent)->Evaluate(context->Makefile, + context->Config, + context->Quiet, + context->HeadTarget, + target, + &dagChecker); + } + +} linkedNode; + +//---------------------------------------------------------------------------- template struct TargetFilesystemArtifactResultCreator { @@ -874,6 +957,8 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier) return &targetDefinedNode; else if (identifier == "INSTALL_PREFIX") return &installPrefixNode; + else if (identifier == "LINKED") + return &linkedNode; return 0; } diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index 8a4437b..0bfcc1b 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -16,9 +16,15 @@ add_executable(consumer "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" ) +add_library(linked UNKNOWN IMPORTED) +set_property(TARGET linked PROPERTY + INTERFACE_COMPILE_DEFINITIONS "MY_LINKED_DEFINE") + + target_compile_definitions(consumer PRIVATE $ $<$:SHOULD_NOT_BE_DEFINED> $<$:SHOULD_BE_DEFINED> + $ -DDASH_D_DEFINE ) diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp index 1a46aa5..c077593 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp @@ -23,4 +23,8 @@ #error Expected DASH_D_DEFINE #endif +#ifndef MY_LINKED_DEFINE +#error Expected MY_LINKED_DEFINE +#endif + int main() { return 0; } diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt index 7529283..a564918 100644 --- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt @@ -17,6 +17,9 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/poison/common.h" "#error Should not be i file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cure") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cure/common.h" "#define CURE_DEFINE\n") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude/linkedinclude.h" "#define LINKEDINCLUDE_DEFINE\n") + add_executable(target_include_directories "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" ) @@ -42,7 +45,13 @@ add_executable(consumer "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" ) +add_library(linked UNKNOWN IMPORTED) +set_property(TARGET linked PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude") + target_include_directories(consumer - PRIVATE $ + PRIVATE + $ + $ relative_dir ) diff --git a/Tests/CMakeCommands/target_include_directories/consumer.cpp b/Tests/CMakeCommands/target_include_directories/consumer.cpp index 82b800a..ccffd9c 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.cpp +++ b/Tests/CMakeCommands/target_include_directories/consumer.cpp @@ -3,6 +3,7 @@ #include "publicinclude.h" #include "interfaceinclude.h" #include "relative_dir.h" +#include "linkedinclude.h" #ifdef PRIVATEINCLUDE_DEFINE #error Unexpected PRIVATEINCLUDE_DEFINE @@ -24,4 +25,8 @@ #error Expected RELATIVE_DIR_DEFINE #endif +#ifndef LINKEDINCLUDE_DEFINE +#error Expected LINKEDINCLUDE_DEFINE +#endif + int main() { return 0; } diff --git a/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt new file mode 100644 index 0000000..4cd9cdd --- /dev/null +++ b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt @@ -0,0 +1,7 @@ +CMake Error: + Error evaluating generator expression: + + \$ + + \$ may only be used in INCLUDE_DIRECTORIES and + COMPILE_DEFINITIONS properties.$ diff --git a/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake new file mode 100644 index 0000000..542ea76 --- /dev/null +++ b/Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake @@ -0,0 +1,7 @@ + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp" + "int main(int, char **) { return 0; }\n") + +add_executable(TargetPropertyGeneratorExpressions + "${CMAKE_CURRENT_BINARY_DIR}/main.cpp") +target_link_libraries(TargetPropertyGeneratorExpressions "$") diff --git a/Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake b/Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake index 0ee3238..ea48f61 100644 --- a/Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake +++ b/Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake @@ -15,3 +15,4 @@ run_cmake(BadInvalidName5) run_cmake(BadInvalidName6) run_cmake(BadInvalidName7) run_cmake(BadInvalidName8) +run_cmake(BadLinked) -- cgit v0.12 From 77cecb778ff1882d87401c1055ec06585462f787 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 5 Nov 2012 12:43:28 +0100 Subject: Add includes and compile definitions with target_link_libraries. This establishes that linking is used to propagate usage-requirements between targets in CMake code. The use of the target_link_libraries command as the API for this is chosen because introducing a new command would introduce confusion due to multiple commands which differ only in a subtle way. --- Source/cmTarget.h | 6 +-- Source/cmTargetLinkLibrariesCommand.cxx | 53 ++++++++++++++++++++++ Source/cmTargetLinkLibrariesCommand.h | 9 ++++ .../target_link_libraries/CMakeLists.txt | 25 ++++++++-- Tests/CMakeCommands/target_link_libraries/depG.cpp | 7 +++ Tests/CMakeCommands/target_link_libraries/depG.h | 7 +++ .../target_link_libraries/targetC.cpp | 16 +++++++ Tests/ExportImport/Export/CMakeLists.txt | 37 ++++++++------- Tests/ExportImport/Import/A/CMakeLists.txt | 14 +----- Tests/ExportImport/Import/CMakeLists.txt | 5 ++ .../Import/package_new_new/CMakeLists.txt | 23 ++++++++++ .../Import/package_new_old/CMakeLists.txt | 24 ++++++++++ .../Import/package_old_old/CMakeLists.txt | 24 ++++++++++ Tests/Qt4Targets/CMakeLists.txt | 19 -------- 14 files changed, 213 insertions(+), 56 deletions(-) create mode 100644 Tests/CMakeCommands/target_link_libraries/depG.cpp create mode 100644 Tests/CMakeCommands/target_link_libraries/depG.h create mode 100644 Tests/CMakeCommands/target_link_libraries/targetC.cpp create mode 100644 Tests/ExportImport/Import/package_new_new/CMakeLists.txt create mode 100644 Tests/ExportImport/Import/package_new_old/CMakeLists.txt create mode 100644 Tests/ExportImport/Import/package_old_old/CMakeLists.txt diff --git a/Source/cmTarget.h b/Source/cmTarget.h index cf2d4c4..7577a59 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -514,6 +514,9 @@ public: const char *GetLinkInterfaceDependentStringProperty(const std::string &p, const char *config); + + std::string GetDebugGeneratorExpressions(const std::string &value, + cmTarget::LinkLibraryType llt); private: /** * A list of direct dependencies. Use in conjunction with DependencyMap. @@ -659,9 +662,6 @@ private: void ProcessSourceExpression(std::string const& expr); - std::string GetDebugGeneratorExpressions(const std::string &value, - cmTarget::LinkLibraryType llt); - // The cmMakefile instance that owns this target. This should // always be set. cmMakefile* Makefile; diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index f42b0f6..cb913f5 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -250,10 +250,51 @@ cmTargetLinkLibrariesCommand } //---------------------------------------------------------------------------- +static std::string compileProperty(cmTarget *tgt, const std::string &lib, + bool isGenex, + const std::string &property, + cmTarget::LinkLibraryType llt) +{ + std::string value = !isGenex ? "$" + : "$<$:" + + "$" + ">"; + + return tgt->GetDebugGeneratorExpressions(value, llt); +} + +//---------------------------------------------------------------------------- +static bool isGeneratorExpression(const std::string &lib) +{ + const std::string::size_type openpos = lib.find("$<"); + return (openpos != std::string::npos) + && (lib.find(">", openpos) != std::string::npos); +} + +//---------------------------------------------------------------------------- void cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib, cmTarget::LinkLibraryType llt) { + const bool isGenex = isGeneratorExpression(lib); + + cmsys::RegularExpression targetNameValidator; + targetNameValidator.compile("^[A-Za-z0-9_.:-]+$"); + const bool potentialTargetName = targetNameValidator.find(lib); + + if (potentialTargetName || isGenex) + { + this->Target->AppendProperty("INCLUDE_DIRECTORIES", + compileProperty(this->Target, lib, + isGenex, + "INCLUDE_DIRECTORIES", llt).c_str()); + this->Target->AppendProperty("COMPILE_DEFINITIONS", + compileProperty(this->Target, lib, + isGenex, + "COMPILE_DEFINITIONS", llt).c_str()); + } + // Handle normal case first. if(this->CurrentProcessingState != ProcessingLinkInterface) { @@ -266,6 +307,18 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib, } } + if (potentialTargetName || isGenex) + { + this->Target->AppendProperty("INTERFACE_COMPILE_DEFINITIONS", + compileProperty(this->Target, lib, + isGenex, + "COMPILE_DEFINITIONS", llt).c_str()); + this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", + compileProperty(this->Target, lib, + isGenex, + "INCLUDE_DIRECTORIES", llt).c_str()); + } + // Get the list of configurations considered to be DEBUG. std::vector const& debugConfigs = this->Makefile->GetCMakeInstance()->GetDebugConfigs(); diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h index 3da3950..aaabdfa 100644 --- a/Source/cmTargetLinkLibrariesCommand.h +++ b/Source/cmTargetLinkLibrariesCommand.h @@ -97,6 +97,15 @@ public: "Calls to other signatures of this command may set the property " "making any libraries linked exclusively by this signature private." "\n" + "Target usage requirements are also consumed by this command. If the " + " is linked to another target which has " + "a populated INTERFACE_INCLUDE_DIRECTORIES, the content of it is " + "appended to the INCLUDE_DIRECTORIES of . Similarly, the " + "INTERFACE_COMPILE_DEFINITONS of a dependee are added to the " + "COMPILE_DEFINITONS of , and the " + "INTERFACE_POSITION_INDEPENDENT_CODE property is used to determine the " + "POSITION_INDEPENDENT_CODE property of ." + "\n" " target_link_libraries( LINK_INTERFACE_LIBRARIES\n" " [[debug|optimized|general] ] ...)\n" "The LINK_INTERFACE_LIBRARIES mode appends the libraries " diff --git a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt index 1d0e342..cd0fe11 100644 --- a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt +++ b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt @@ -62,10 +62,6 @@ assert_property(targetA LINK_INTERFACE_LIBRARIES "") add_subdirectory(subdir) target_link_libraries(targetA subdirlib) -set_property(TARGET targetA APPEND PROPERTY - INCLUDE_DIRECTORIES - $ -) target_link_libraries(targetA depB depC) @@ -87,3 +83,24 @@ set_property(TARGET depD APPEND PROPERTY add_executable(targetB targetB.cpp) target_link_libraries(targetB depD) + +macro(create_header _name) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_name}") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_name}/${_name}.h" "//${_name}.h\n") +endmacro() + +create_header(foo) +create_header(bar) + +add_library(depG SHARED depG.cpp) +generate_export_header(depG) +target_include_directories(depG INTERFACE + "${CMAKE_CURRENT_BINARY_DIR}/foo" + "${CMAKE_CURRENT_BINARY_DIR}/bar" +) +target_compile_definitions(depG INTERFACE + TEST_DEF +) + +add_executable(targetC targetC.cpp) +target_link_libraries(targetC depG) diff --git a/Tests/CMakeCommands/target_link_libraries/depG.cpp b/Tests/CMakeCommands/target_link_libraries/depG.cpp new file mode 100644 index 0000000..65b9655 --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/depG.cpp @@ -0,0 +1,7 @@ + +#include "depG.h" + +int DepG::foo() +{ + return 0; +} diff --git a/Tests/CMakeCommands/target_link_libraries/depG.h b/Tests/CMakeCommands/target_link_libraries/depG.h new file mode 100644 index 0000000..1a36589 --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/depG.h @@ -0,0 +1,7 @@ + +#include "depg_export.h" + +struct DEPG_EXPORT DepG +{ + int foo(); +}; diff --git a/Tests/CMakeCommands/target_link_libraries/targetC.cpp b/Tests/CMakeCommands/target_link_libraries/targetC.cpp new file mode 100644 index 0000000..ff6ba66 --- /dev/null +++ b/Tests/CMakeCommands/target_link_libraries/targetC.cpp @@ -0,0 +1,16 @@ + +#include "depG.h" + +#include "foo.h" +#include "bar.h" + +#ifndef TEST_DEF +#error Expected TEST_DEF definition +#endif + +int main(int argc, char **argv) +{ + DepG g; + + return g.foo(); +} diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index ae44c28..ae938cd 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -90,23 +90,7 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3) # Test exporting dependent libraries into different exports add_library(testLibRequired testLibRequired.c) add_library(testLibDepends testLibDepends.c) -set_property(TARGET testLibDepends APPEND PROPERTY - INCLUDE_DIRECTORIES - $ -) -set_property(TARGET testLibDepends APPEND PROPERTY - COMPILE_DEFINITIONS - $ -) -set_property(TARGET testLibDepends APPEND PROPERTY - INTERFACE_INCLUDE_DIRECTORIES - $ -) -set_property(TARGET testLibDepends APPEND PROPERTY - INTERFACE_COMPILE_DEFINITIONS - $ -) -target_link_libraries(testLibDepends testLibRequired) +target_link_libraries(testLibDepends LINK_PUBLIC testLibRequired) macro(add_include_lib _libName) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n") @@ -239,6 +223,25 @@ install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredTargets.cmake DES install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib ) install(EXPORT DependsExp FILE testLibDependsTargets.cmake DESTINATION lib/cmake/testLibDepends) +file(WRITE + "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake" + " +if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION VERSION_LESS 2.3 AND NOT \${CMAKE_FIND_PACKAGE_NAME}_INTERFACES) + set(\${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES 1) +endif() +include(\"\${CMAKE_CURRENT_LIST_DIR}/testLibRequiredTargets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_INCLUDE_DIRS \"${CMAKE_CURRENT_BINARY_DIR}\" \"${CMAKE_CURRENT_SOURCE_DIR}\" ) +" +) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( testLibRequiredConfigVersion.cmake VERSION 2.5 COMPATIBILITY AnyNewerVersion) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfigVersion.cmake" + DESTINATION lib/cmake/testLibRequired +) # Install and export from install tree. install( diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 0675594..0337130 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -159,18 +159,11 @@ endif() add_executable(deps_iface deps_iface.c) target_link_libraries(deps_iface testLibDepends) -target_include_directories(deps_iface PRIVATE $) -target_compile_definitions(deps_iface PRIVATE $) add_executable(deps_shared_iface deps_shared_iface.cpp) target_link_libraries(deps_shared_iface testSharedLibDepends) -target_include_directories(deps_shared_iface - PRIVATE - $ -) target_compile_definitions(deps_shared_iface PRIVATE - $ $<$>:PIC_PROPERTY_IS_ON> $<$>:CUSTOM_PROPERTY_IS_ON> $<$,testcontent>:CUSTOM_STRING_IS_MATCH> @@ -200,13 +193,8 @@ endif() add_executable(deps_shared_iface2 deps_shared_iface.cpp) target_link_libraries(deps_shared_iface2 bld_testSharedLibDepends bld_subdirlib) -target_include_directories(deps_shared_iface2 - PRIVATE - $ - $ -) target_compile_definitions(deps_shared_iface2 - PRIVATE $ TEST_SUBDIR_LIB + PRIVATE TEST_SUBDIR_LIB $<$>:PIC_PROPERTY_IS_ON> $<$>:CUSTOM_PROPERTY_IS_ON> $<$,testcontent>:CUSTOM_STRING_IS_MATCH> diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt index 3fc78a2..237f8fa 100644 --- a/Tests/ExportImport/Import/CMakeLists.txt +++ b/Tests/ExportImport/Import/CMakeLists.txt @@ -17,3 +17,8 @@ add_executable(imp_testTransExe1 imp_testTransExe1.c) target_link_libraries(imp_testTransExe1 imp_lib1) add_executable(imp_testTransExe1b imp_testTransExe1.c) target_link_libraries(imp_testTransExe1b imp_lib1b) + +# Test package INTERFACE controls +add_subdirectory(package_old_old) +add_subdirectory(package_new_old) +add_subdirectory(package_new_new) diff --git a/Tests/ExportImport/Import/package_new_new/CMakeLists.txt b/Tests/ExportImport/Import/package_new_new/CMakeLists.txt new file mode 100644 index 0000000..4e6f642 --- /dev/null +++ b/Tests/ExportImport/Import/package_new_new/CMakeLists.txt @@ -0,0 +1,23 @@ + +cmake_minimum_required(VERSION 2.8) + +find_package(testLibRequired 2.5 REQUIRED) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp" + " +#include \"testSharedLibRequired.h\" +int main(int argc, char **argv) +{ + TestSharedLibRequired req; + return req.foo(); +} +" +) + +get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES) +if (NOT prop) + message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty") +endif() + +add_executable(new_new_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp") +target_link_libraries(new_new_test Req::testSharedLibRequired) diff --git a/Tests/ExportImport/Import/package_new_old/CMakeLists.txt b/Tests/ExportImport/Import/package_new_old/CMakeLists.txt new file mode 100644 index 0000000..e675d64 --- /dev/null +++ b/Tests/ExportImport/Import/package_new_old/CMakeLists.txt @@ -0,0 +1,24 @@ + +cmake_minimum_required(VERSION 2.8) + +find_package(testLibRequired 2.5 REQUIRED) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp" + " +#include \"testSharedLibRequired.h\" +int main(int argc, char **argv) +{ + TestSharedLibRequired req; + return req.foo(); +} +" +) + +get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES) +if ("${prop}" STREQUAL "") + message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty") +endif() + +add_executable(new_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp") +target_link_libraries(new_old_test Req::testSharedLibRequired) +include_directories(${testLibRequired_INCLUDE_DIRS}) diff --git a/Tests/ExportImport/Import/package_old_old/CMakeLists.txt b/Tests/ExportImport/Import/package_old_old/CMakeLists.txt new file mode 100644 index 0000000..3b27330 --- /dev/null +++ b/Tests/ExportImport/Import/package_old_old/CMakeLists.txt @@ -0,0 +1,24 @@ + +cmake_minimum_required(VERSION 2.8) + +find_package(testLibRequired 2.1 REQUIRED) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp" + " +#include \"testSharedLibRequired.h\" +int main(int argc, char **argv) +{ + TestSharedLibRequired req; + return req.foo(); +} +" +) + +get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES) +if (prop) + message(SEND_ERROR "Interface of Req::testSharedLibRequired should be empty, but is ${prop}") +endif() + +add_executable(old_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp") +target_link_libraries(old_old_test Req::testSharedLibRequired) +include_directories(${testLibRequired_INCLUDE_DIRS}) diff --git a/Tests/Qt4Targets/CMakeLists.txt b/Tests/Qt4Targets/CMakeLists.txt index d3aba74..d0c9c66 100644 --- a/Tests/Qt4Targets/CMakeLists.txt +++ b/Tests/Qt4Targets/CMakeLists.txt @@ -12,29 +12,10 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) add_executable(Qt4Targets WIN32 main.cpp) target_link_libraries(Qt4Targets Qt4::QtGui) -set_property(TARGET Qt4Targets APPEND PROPERTY - INCLUDE_DIRECTORIES - $ -) -set_property(TARGET Qt4Targets APPEND PROPERTY - COMPILE_DEFINITIONS - $ -) - if (WIN32) if (TARGET Qt4::QAxServer) add_executable(activeqtexe WIN32 activeqtexe.cpp) set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON) target_link_libraries(activeqtexe Qt4::QAxServer Qt4::QtGui) - set_property(TARGET activeqtexe APPEND PROPERTY - INCLUDE_DIRECTORIES - $ - $ - ) - set_property(TARGET activeqtexe APPEND PROPERTY - COMPILE_DEFINITIONS - $ - $ - ) endif() endif() -- cgit v0.12 From e7b579bd01690f27c82ee9cbda4b7023f4a3d6c9 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 21 Dec 2012 18:28:58 +0100 Subject: Test workaround of bad interface include directories from depends. --- .../TargetIncludeDirectories/CMakeLists.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt index 21159e0..5387377 100644 --- a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt @@ -82,3 +82,44 @@ add_custom_target(test_custom_target $ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bad") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bad/common.h" "#error Should not be included\n") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/good") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/good/common.h" "#include \"othergood.h\"\n") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/othergood") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/othergood/othergood.h" "// No error\n") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp" "// No content \n") +add_library(libothergood "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp") +set_property(TARGET libothergood APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/othergood" +) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp" "// No content \n") +add_library(libgood "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp") +set_property(TARGET libgood APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + "${CMAKE_CURRENT_BINARY_DIR}/good;$" +) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp" "// No content \n") +add_library(libbad "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp") +set_property(TARGET libbad APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/bad" +) + + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp" "#include \"common.h\"\n") +add_library(lib5 "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp") + +# Assuming the link order must be: +target_link_libraries(lib5 libbad libgood) + +# Oops!. +# As include directory order and link order are the same when using target_link_libraries, we have to +# get the libgood includes in before the libbad includes. +# We do that with this command: +target_include_directories(lib5 + BEFORE PRIVATE $ +) -- cgit v0.12 From 179f49560286e2e322b9b0cf5d0a277b7634540f Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 1 Feb 2013 10:30:57 -0500 Subject: find_package: Reword _NO_INTERFACES documentation --- Source/cmFindPackageCommand.cxx | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index e9b6ab6..6e78bd7 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -376,26 +376,25 @@ void cmFindPackageCommand::GenerateDocumentation() "The package configuration file may set _FOUND to false " "to tell find_package that component requirements are not satisfied." "\n" - "Targets exported by the install() command may have additional INTERFACE " - "properties set for include directories, compile definitions etc. As " - "these may create a backward compatibility concern for consumers of " - "existing projects, it is possible to set the _NO_INTERFACES " - "variable to disable populating those interfaces. It is possible to " - "emulate a version-based policy for whether the interfaces should be " - "enabled in the config file before including the imported targets file. " - "This allows consumers to decide when to enable the new interfaces when " - "upgrading. The upstream project is responsible for adding code similar " - "to this in the version which generates the INTERFACE content:\n" - " if(${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION VERSION_LESS 2.3\n" - " AND NOT ${CMAKE_FIND_PACKAGE_NAME}_INTERFACES)\n" - " set(${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES 1)\n" + "A package configuration file may include() a Targets.cmake " + "file, created by install(EXPORT) in the upstream source, to import " + "targets into the downstream consumer. " + "When a new version of the upstream adds INTERFACE properties not " + "present in a previous version it can change behavior for existing " + "downstreams. " + "In order to remain source compatible the upstream package configuration " + "file may set _NO_INTERFACES to disable INTERFACE properties. " + "For example, code of the form:\n" + " if(_FIND_VERSION VERSION_LESS \n" + " AND NOT _INTERFACES)\n" + " set(_NO_INTERFACES 1)\n" " endif()\n" " include(\"${CMAKE_CURRENT_LIST_DIR}/Targets.cmake\")\n" - "\n" - "Downstream users may then choose to use the new interfaces by finding " - "the newer version of upstream. They can also choose to not use the new " - "interfaces by setting _INTERFACES to false before finding the " - "package.\n" + "tells Targets.cmake not to provide the INTERFACE properties " + "unless the downstream requests at least or sets " + "_INTERFACES to explicitly request them. " + "This allows consumers to decide when to enable the new interfaces when " + "upgrading." "\n" "See the cmake_policy() command documentation for discussion of the " "NO_POLICY_SCOPE option." -- cgit v0.12 From 089fe1c13d8fa73be5182162a855c17351d1f918 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 1 Feb 2013 09:38:40 +0100 Subject: Optimize genex evaluation for includes and defines. While porting boost to use these features, the generation step took too long (several minutes before I stopped it). The reason was that the boost libraries form a large interdependent mesh. The libraries list their dependencies in their INTERFACE such as: $;$;$ As boost::core already depends on the boost::config libraries, that expression has no impact on the end-content, as it is removed after the generation step. There is no DAG issue though, so the generator expression evaluation would fully evaluate them. In the case of the config library, it also depends on the core library, so all depends are followed through that again, despite the fact that they've just been evaluated. After this patch, the evaluation skips libraries if they have already been seen via depends or directly in the content. This patch keeps track of targets whose INTERFACE has been consumed already. The INCLUDE_DIRECTORIES and COMPILE_DEFINITIONS properties are whitelisted because repeated content will be stripped out later during generation. For other properties now and in the future, that may not be the case. --- Source/cmGeneratorExpressionDAGChecker.cxx | 26 ++++++++++++++++++++++++++ Source/cmGeneratorExpressionDAGChecker.h | 4 +++- Source/cmGeneratorExpressionEvaluator.cxx | 6 ++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index bcb0820..b9069ef 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -24,7 +24,33 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( : Parent(parent), Target(target), Property(property), Content(content), Backtrace(backtrace) { + const cmGeneratorExpressionDAGChecker *top = this; + const cmGeneratorExpressionDAGChecker *p = this->Parent; + while (p) + { + top = p; + p = p->Parent; + } this->CheckResult = this->checkGraph(); + + if (CheckResult == DAG && (top->Property == "INCLUDE_DIRECTORIES" + || top->Property == "COMPILE_DEFINITIONS") ) + { + std::map >::const_iterator it + = top->Seen.find(target); + if (it != top->Seen.end()) + { + const std::set &propSet = it->second; + const std::set::const_iterator i = propSet.find(property); + if (i != propSet.end()) + { + this->CheckResult = ALREADY_SEEN; + return; + } + } + const_cast(top) + ->Seen[target].insert(property); + } } //---------------------------------------------------------------------------- diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 7e9c62a..a2e5ce4 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -28,7 +28,8 @@ struct cmGeneratorExpressionDAGChecker enum Result { DAG, SELF_REFERENCE, - CYCLIC_REFERENCE + CYCLIC_REFERENCE, + ALREADY_SEEN }; Result check() const; @@ -47,6 +48,7 @@ private: const cmGeneratorExpressionDAGChecker * const Parent; const std::string Target; const std::string Property; + std::map > Seen; const GeneratorExpressionContent * const Content; const cmListFileBacktrace Backtrace; Result CheckResult; diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index cbea1d9..cd4b7d8 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -435,6 +435,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: // No error. We just skip cyclic references. return std::string(); + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + // No error. We're not going to find anything new here. + return std::string(); case cmGeneratorExpressionDAGChecker::DAG: break; } @@ -705,6 +708,9 @@ private: case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: // No error. We just skip cyclic references. return std::string(); + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + // No error. We're not going to find anything new here. + return std::string(); case cmGeneratorExpressionDAGChecker::DAG: break; } -- cgit v0.12 From e48d84209cde93b43fcfb305897b4f52cd18a55f Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Sun, 3 Feb 2013 07:33:15 +0100 Subject: Cache context-independent includes on evaluation. Generator expressions whose output depends on the configuration now record that fact. The GetIncludeDirectories method can use that result to cache the include directories for later calls. GetIncludeDirectories is called multiple times for a target for each configuration, so this should restore performance for multi-config generators. --- Source/cmGeneratorExpression.cxx | 8 +++++- Source/cmGeneratorExpression.h | 5 ++++ Source/cmGeneratorExpressionEvaluator.cxx | 25 +++++++++++++++++-- Source/cmGeneratorExpressionEvaluator.h | 1 + Source/cmTarget.cxx | 41 ++++++++++++++++++++++--------- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 78ae8f2..7add1bf 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -88,6 +88,7 @@ const char *cmCompiledGeneratorExpression::Evaluate( context.Config = config; context.Quiet = quiet; context.HadError = false; + context.HadContextSensitiveCondition = false; context.HeadTarget = headTarget; context.CurrentTarget = currentTarget ? currentTarget : headTarget; context.Backtrace = this->Backtrace; @@ -109,6 +110,10 @@ const char *cmCompiledGeneratorExpression::Evaluate( break; } } + if (!context.HadError) + { + this->HadContextSensitiveCondition = context.HadContextSensitiveCondition; + } this->Targets = context.Targets; // TODO: Return a std::string from here instead? @@ -118,7 +123,8 @@ const char *cmCompiledGeneratorExpression::Evaluate( cmCompiledGeneratorExpression::cmCompiledGeneratorExpression( cmListFileBacktrace const& backtrace, const char *input) - : Backtrace(backtrace), Input(input ? input : "") + : Backtrace(backtrace), Input(input ? input : ""), + HadContextSensitiveCondition(false) { cmGeneratorExpressionLexer l; std::vector tokens = diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 8f1aef6..700fe03 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -100,6 +100,10 @@ public: { return this->Backtrace; } + bool GetHadContextSensitiveCondition() const + { + return this->HadContextSensitiveCondition; + } private: cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace, @@ -118,6 +122,7 @@ private: mutable std::set Targets; mutable std::map SeenTargetProperties; mutable std::string Output; + mutable bool HadContextSensitiveCondition; }; #endif diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index cd4b7d8..5d94718 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -238,6 +238,7 @@ static const struct ConfigurationNode : public cmGeneratorExpressionNode const GeneratorExpressionContent *, cmGeneratorExpressionDAGChecker *) const { + context->HadContextSensitiveCondition = true; return context->Config ? context->Config : ""; } } configurationNode; @@ -262,6 +263,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode "Expression syntax not recognized."); return std::string(); } + context->HadContextSensitiveCondition = true; if (!context->Config) { return parameters.front().empty() ? "1" : "0"; @@ -455,12 +457,14 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode } if (propertyName == "POSITION_INDEPENDENT_CODE") { + context->HadContextSensitiveCondition = true; return target->GetLinkInterfaceDependentBoolProperty( "POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0"; } if (target->IsLinkInterfaceDependentBoolProperty(propertyName, context->Config)) { + context->HadContextSensitiveCondition = true; return target->GetLinkInterfaceDependentBoolProperty( propertyName, context->Config) ? "1" : "0"; @@ -468,6 +472,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (target->IsLinkInterfaceDependentStringProperty(propertyName, context->Config)) { + context->HadContextSensitiveCondition = true; const char *propContent = target->GetLinkInterfaceDependentStringProperty( propertyName, @@ -486,12 +491,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (targetPropertyTransitiveWhitelist[i] == propertyName) { cmGeneratorExpression ge(context->Backtrace); - return ge.Parse(prop)->Evaluate(context->Makefile, + cmsys::auto_ptr cge = ge.Parse(prop); + std::string result = cge->Evaluate(context->Makefile, context->Config, context->Quiet, context->HeadTarget, target, &dagChecker); + + if (cge->GetHadContextSensitiveCondition()) + { + context->HadContextSensitiveCondition = true; + } + return result; } } return prop; @@ -585,6 +597,9 @@ static const struct TargetPolicyNode : public cmGeneratorExpressionNode "be used with add_custom_command."); return std::string(); } + + context->HadContextSensitiveCondition = true; + for (size_t i = 0; i < (sizeof(targetPolicyWhitelist) / sizeof(*targetPolicyWhitelist)); @@ -716,12 +731,18 @@ private: } cmGeneratorExpression ge(context->Backtrace); - return ge.Parse(propContent)->Evaluate(context->Makefile, + cmsys::auto_ptr cge = ge.Parse(propContent); + std::string result = cge->Evaluate(context->Makefile, context->Config, context->Quiet, context->HeadTarget, target, &dagChecker); + if (cge->GetHadContextSensitiveCondition()) + { + context->HadContextSensitiveCondition = true; + } + return result; } } linkedNode; diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index fb6c7ee..37d5c86 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -32,6 +32,7 @@ struct cmGeneratorExpressionContext // directly or indirectly in the property. bool Quiet; bool HadError; + bool HadContextSensitiveCondition; }; struct cmGeneratorExpressionDAGChecker; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 5669872..1a6b7ce 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -134,6 +134,7 @@ public: : ge(cge) {} const cmsys::auto_ptr ge; + std::vector CachedIncludes; }; std::vector IncludeDirectoriesEntries; }; @@ -2778,22 +2779,36 @@ std::vector cmTarget::GetIncludeDirectories(const char *config) end = this->Internal->IncludeDirectoriesEntries.end(); it != end; ++it) { - std::vector entryIncludes; - cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile, - config, - false, - this, - &dagChecker), - entryIncludes); + + bool testIsOff = true; + bool cacheIncludes = false; + std::vector entryIncludes = (*it)->CachedIncludes; + if(!entryIncludes.empty()) + { + testIsOff = false; + } + else + { + cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile, + config, + false, + this, + &dagChecker), + entryIncludes); + if (!(*it)->ge->GetHadContextSensitiveCondition()) + { + cacheIncludes = true; + } + } std::string usedIncludes; - for(std::vector::const_iterator + for(std::vector::iterator li = entryIncludes.begin(); li != entryIncludes.end(); ++li) { - std::string inc = *li; - if (!cmSystemTools::IsOff(inc.c_str())) + if (testIsOff && !cmSystemTools::IsOff(li->c_str())) { - cmSystemTools::ConvertToUnixSlashes(inc); + cmSystemTools::ConvertToUnixSlashes(*li); } + std::string inc = *li; if(uniqueIncludes.insert(inc).second) { @@ -2804,6 +2819,10 @@ std::vector cmTarget::GetIncludeDirectories(const char *config) } } } + if (cacheIncludes) + { + (*it)->CachedIncludes = entryIncludes; + } if (!usedIncludes.empty()) { this->Makefile->GetCMakeInstance()->IssueMessage(cmake::LOG, -- cgit v0.12