Browse Source

genex: Add TARGET_EXISTS to check for target existence

Define `$<TARGET_EXISTS:a>` to `1` if `a` is an existed target name,
else `0`.
Alex Turbov 7 years ago
parent
commit
7fec336bf7

+ 2 - 0
Help/manual/cmake-generator-expressions.7.rst

@@ -59,6 +59,8 @@ Available logical expressions are:
   ``1`` if ``a`` is EQUAL ``b`` in a numeric comparison, else ``0``
 ``$<IN_LIST:a,b>``
   ``1`` if ``a`` is IN_LIST ``b``, else ``0``
+``$<TARGET_EXISTS:tgt>``
+  ``1`` if ``tgt`` is an existed target name, else ``0``.
 ``$<CONFIG:cfg>``
   ``1`` if config is ``cfg``, else ``0``. This is a case-insensitive comparison.
   The mapping in :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by

+ 6 - 0
Help/release/dev/genex-TARGET_EXISTS.rst

@@ -0,0 +1,6 @@
+genex-TARGET_EXISTS
+-------------------
+
+* A new ``$<TARGET_EXISTS:...>``
+  :manual:`generator expression <cmake-generator-expressions(7)>`
+  has been added.

+ 32 - 0
Source/cmGeneratorExpressionNode.cxx

@@ -300,6 +300,37 @@ static const struct InListNode : public cmGeneratorExpressionNode
   }
 } inListNode;
 
+static const struct TargetExistsNode : public cmGeneratorExpressionNode
+{
+  TargetExistsNode() {}
+
+  int NumExpectedParameters() const override { return 1; }
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+  {
+    if (parameters.size() != 1) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<TARGET_EXISTS:...> expression requires one parameter");
+      return std::string();
+    }
+
+    std::string targetName = parameters.front();
+    if (targetName.empty() ||
+        !cmGeneratorExpression::IsValidTargetName(targetName)) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<TARGET_EXISTS:tgt> expression requires a non-empty "
+                  "valid target name.");
+      return std::string();
+    }
+
+    return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0";
+  }
+} targetExistsNode;
+
 static const struct LowerCaseNode : public cmGeneratorExpressionNode
 {
   LowerCaseNode() {}
@@ -1865,6 +1896,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     nodeMap["TARGET_NAME"] = &targetNameNode;
     nodeMap["TARGET_OBJECTS"] = &targetObjectsNode;
     nodeMap["TARGET_POLICY"] = &targetPolicyNode;
+    nodeMap["TARGET_EXISTS"] = &targetExistsNode;
     nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode;
     nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode;
     nodeMap["INSTALL_PREFIX"] = &installPrefixNode;

+ 4 - 0
Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake

@@ -34,6 +34,10 @@ run_cmake(OUTPUT_NAME-recursion)
 run_cmake(TARGET_PROPERTY-LOCATION)
 run_cmake(TARGET_PROPERTY-SOURCES)
 run_cmake(LINK_ONLY-not-linking)
+run_cmake(TARGET_EXISTS-no-arg)
+run_cmake(TARGET_EXISTS-empty-arg)
+run_cmake(TARGET_EXISTS)
+run_cmake(TARGET_EXISTS-not-a-target)
 
 run_cmake(ImportedTarget-TARGET_BUNDLE_DIR)
 run_cmake(ImportedTarget-TARGET_BUNDLE_CONTENT_DIR)

+ 6 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-check.cmake

@@ -0,0 +1,6 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/TARGET_EXISTS-generated.txt" content)
+
+set(expected "1")
+if(NOT content STREQUAL expected)
+  set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
+endif()

+ 1 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-empty-arg-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-empty-arg-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at TARGET_EXISTS-empty-arg.cmake:2 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_EXISTS:>
+
+  \$<TARGET_EXISTS:tgt> expression requires a non-empty valid target name.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-empty-arg.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0070 NEW)
+file(GENERATE OUTPUT TARGET_EXISTS-generated.txt CONTENT "$<TARGET_EXISTS:${empty}>")

+ 1 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-no-arg-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-no-arg-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at TARGET_EXISTS-no-arg.cmake:2 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_EXISTS>
+
+  \$<TARGET_EXISTS> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-no-arg.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0070 NEW)
+file(GENERATE OUTPUT TARGET_EXISTS-generated.txt CONTENT "$<TARGET_EXISTS>")

+ 6 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-not-a-target-check.cmake

@@ -0,0 +1,6 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/TARGET_EXISTS-not-a-target-generated.txt" content)
+
+set(expected "0")
+if(NOT content STREQUAL expected)
+  set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
+endif()

+ 2 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS-not-a-target.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0070 NEW)
+file(GENERATE OUTPUT TARGET_EXISTS-not-a-target-generated.txt CONTENT "$<TARGET_EXISTS:just-random-string>")

+ 3 - 0
Tests/RunCMake/GeneratorExpression/TARGET_EXISTS.cmake

@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0070 NEW)
+add_custom_target(foo)
+file(GENERATE OUTPUT TARGET_EXISTS-generated.txt CONTENT "$<TARGET_EXISTS:foo>")