From 0b92602b816e2584db3781b120a1e5200da72ada Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 30 Jan 2013 23:38:04 +0100 Subject: [PATCH] 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 8b80a8a32..3993f7d72 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 2ecac8477..7e4c3dfe5 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; @@ -396,6 +399,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 776be614d..5ad27bfaf 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 fff7dab09..cbea1d9f5 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -637,6 +637,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 8a4437ba5..0bfcc1b1b 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 1a46aa50c..c07759358 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 752928378..a56491807 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 82b800a40..ccffd9cab 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 000000000..d00491fd7 --- /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 000000000..4cd9cdd15 --- /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 000000000..542ea760e --- /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 0ee32387d..ea48f61a8 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)