Przeglądaj źródła

Add boolean generator expressions

Add generator expressions that combine and use boolean test results:

 $<0:...>         = empty string (ignores "...")
 $<1:...>         = content of "..."
 $<AND:?[,?]...>  = '1' if all '?' are '1', else '0'
 $<OR:?[,?]...>   = '0' if all '?' are '0', else '1'
 $<NOT:?>         = '0' if '?' is '1', else '1'

These will be useful to evaluate (future) boolean query expressions and
condition content on the results.  Include tests and documentation.
Brad King 13 lat temu
rodzic
commit
ebf05abda1

+ 9 - 1
Source/cmDocumentGeneratorExpressions.h

@@ -16,6 +16,8 @@
   "Generator expressions are evaluted during build system generation "  \
   "Generator expressions are evaluted during build system generation "  \
   "to produce information specific to each build configuration.  "      \
   "to produce information specific to each build configuration.  "      \
   "Valid expressions are:\n"                                            \
   "Valid expressions are:\n"                                            \
+  "  $<0:...>                  = empty string (ignores \"...\")\n"      \
+  "  $<1:...>                  = content of \"...\"\n"                  \
   "  $<CONFIGURATION>          = configuration name\n"                  \
   "  $<CONFIGURATION>          = configuration name\n"                  \
   "  $<TARGET_FILE:tgt>        = main file (.exe, .so.1.2, .a)\n"       \
   "  $<TARGET_FILE:tgt>        = main file (.exe, .so.1.2, .a)\n"       \
   "  $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n"   \
   "  $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n"   \
@@ -25,6 +27,12 @@
   "versions can produce the directory and file name components:\n"      \
   "versions can produce the directory and file name components:\n"      \
   "  $<TARGET_FILE_DIR:tgt>/$<TARGET_FILE_NAME:tgt>\n"                  \
   "  $<TARGET_FILE_DIR:tgt>/$<TARGET_FILE_NAME:tgt>\n"                  \
   "  $<TARGET_LINKER_FILE_DIR:tgt>/$<TARGET_LINKER_FILE_NAME:tgt>\n"    \
   "  $<TARGET_LINKER_FILE_DIR:tgt>/$<TARGET_LINKER_FILE_NAME:tgt>\n"    \
-  "  $<TARGET_SONAME_FILE_DIR:tgt>/$<TARGET_SONAME_FILE_NAME:tgt>\n"
+  "  $<TARGET_SONAME_FILE_DIR:tgt>/$<TARGET_SONAME_FILE_NAME:tgt>\n"    \
+  "Boolean expressions:\n"                                              \
+  "  $<AND:?[,?]...>           = '1' if all '?' are '1', else '0'\n"    \
+  "  $<OR:?[,?]...>            = '0' if all '?' are '0', else '1'\n"    \
+  "  $<NOT:?>                  = '0' if '?' is '1', else '1'\n"         \
+  "where '?' is always either '0' or '1'.\n"                            \
+  ""
 
 
 #endif
 #endif

+ 46 - 0
Source/cmGeneratorExpression.cxx

@@ -102,6 +102,26 @@ bool cmGeneratorExpression::Evaluate()
   return true;
   return true;
 }
 }
 
 
+//----------------------------------------------------------------------------
+static bool cmGeneratorExpressionBool(const char* c, std::string& result,
+                                      const char* name,
+                                      const char* a, const char* b)
+{
+  result = a;
+  while((c[0] == '0' || c[0] == '1') && (c[1] == ',' || c[1] == '>'))
+    {
+    if(c[0] == b[0]) { result = b; }
+    c += 2;
+    }
+  if(c[0])
+    {
+    result = name;
+    result += " requires one or more comma-separated '0' or '1' values.";
+    return false;
+    }
+  return true;
+}
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
 bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
 {
 {
@@ -116,6 +136,32 @@ bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
     {
     {
     result = this->Config? this->Config : "";
     result = this->Config? this->Config : "";
     }
     }
+  else if(strncmp(expr, "$<0:",4) == 0)
+    {
+    result = "";
+    }
+  else if(strncmp(expr, "$<1:",4) == 0)
+    {
+    result = std::string(expr+4, strlen(expr)-5);
+    }
+  else if(strncmp(expr, "$<NOT:",6) == 0)
+    {
+    const char* c = expr+6;
+    if((c[0] != '0' && c[0] != '1') || c[1] != '>' || c[2])
+      {
+      result = "NOT requires exactly one '0' or '1' value.";
+      return false;
+      }
+    result = c[0] == '1'? "0" : "1";
+    }
+  else if(strncmp(expr, "$<AND:",6) == 0)
+    {
+    return cmGeneratorExpressionBool(expr+6, result, "AND", "1", "0");
+    }
+  else if(strncmp(expr, "$<OR:",5) == 0)
+    {
+    return cmGeneratorExpressionBool(expr+5, result, "OR", "0", "1");
+    }
   else
   else
     {
     {
     result = "Expression syntax not recognized.";
     result = "Expression syntax not recognized.";

+ 10 - 0
Tests/CMakeLists.txt

@@ -551,6 +551,16 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
     FAIL_REGULAR_EXPRESSION "Unexpected: ")
     FAIL_REGULAR_EXPRESSION "Unexpected: ")
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ArgumentExpansion")
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ArgumentExpansion")
 
 
+  add_test(GeneratorExpression  ${CMAKE_CTEST_COMMAND}
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/GeneratorExpression"
+    "${CMake_BINARY_DIR}/Tests/GeneratorExpression"
+    --build-generator ${CMAKE_TEST_GENERATOR}
+    --build-project GeneratorExpression
+    --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+    )
+  list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/GeneratorExpression")
+
   add_test(CustomCommand  ${CMAKE_CTEST_COMMAND}
   add_test(CustomCommand  ${CMAKE_CTEST_COMMAND}
     --build-and-test
     --build-and-test
     "${CMake_SOURCE_DIR}/Tests/CustomCommand"
     "${CMake_SOURCE_DIR}/Tests/CustomCommand"

+ 26 - 0
Tests/GeneratorExpression/CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required (VERSION 2.8.8)
+project(GeneratorExpression NONE)
+
+add_custom_target(check ALL
+  COMMAND ${CMAKE_COMMAND}
+    -Dtest_0=$<0:nothing>
+    -Dtest_1=$<1:content>
+    -Dconfig=$<CONFIGURATION>
+    -Dtest_and_0=$<AND:0>
+    -Dtest_and_0_0=$<AND:0,0>
+    -Dtest_and_0_1=$<AND:0,1>
+    -Dtest_and_1=$<AND:1>
+    -Dtest_and_1_0=$<AND:1,0>
+    -Dtest_and_1_1=$<AND:1,1>
+    -Dtest_not_0=$<NOT:0>
+    -Dtest_not_1=$<NOT:1>
+    -Dtest_or_0=$<OR:0>
+    -Dtest_or_0_0=$<OR:0,0>
+    -Dtest_or_0_1=$<OR:0,1>
+    -Dtest_or_1=$<OR:1>
+    -Dtest_or_1_0=$<OR:1,0>
+    -Dtest_or_1_1=$<OR:1,1>
+    -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake
+  COMMAND ${CMAKE_COMMAND} -E echo "check done"
+  VERBATIM
+  )

+ 23 - 0
Tests/GeneratorExpression/check.cmake

@@ -0,0 +1,23 @@
+macro(check var val)
+  if(NOT "${${var}}" STREQUAL "${val}")
+    message(SEND_ERROR "${var} is \"${${var}}\", not \"${val}\"")
+  endif()
+endmacro()
+
+message(STATUS "config=[${config}]")
+check(test_0 "")
+check(test_1 "content")
+check(test_and_0 "0")
+check(test_and_0_0 "0")
+check(test_and_0_1 "0")
+check(test_and_1 "1")
+check(test_and_1_0 "0")
+check(test_and_1_1 "1")
+check(test_not_0 "1")
+check(test_not_1 "0")
+check(test_or_0 "0")
+check(test_or_0_0 "0")
+check(test_or_0_1 "1")
+check(test_or_1 "1")
+check(test_or_1_0 "1")
+check(test_or_1_1 "1")

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -45,6 +45,7 @@ macro(add_RunCMake_test test)
     )
     )
 endmacro()
 endmacro()
 
 
+add_RunCMake_test(GeneratorExpression)
 add_RunCMake_test(Languages)
 add_RunCMake_test(Languages)
 add_RunCMake_test(ObjectLibrary)
 add_RunCMake_test(ObjectLibrary)
 
 

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadAND-result.txt

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

+ 17 - 0
Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt

@@ -0,0 +1,17 @@
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:>
+
+  AND requires one or more comma-separated '0' or '1' values.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:,>
+
+  AND requires one or more comma-separated '0' or '1' values.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

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

@@ -0,0 +1,4 @@
+add_custom_target(check ALL COMMAND check
+  $<AND:>
+  $<AND:,>
+  VERBATIM)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadNOT-result.txt

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

+ 26 - 0
Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt

@@ -0,0 +1,26 @@
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:>
+
+  NOT requires exactly one '0' or '1' value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:,>
+
+  NOT requires exactly one '0' or '1' value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:0,1>
+
+  NOT requires exactly one '0' or '1' value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 5 - 0
Tests/RunCMake/GeneratorExpression/BadNOT.cmake

@@ -0,0 +1,5 @@
+add_custom_target(check ALL COMMAND check
+  $<NOT:>
+  $<NOT:,>
+  $<NOT:0,1>
+  VERBATIM)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadOR-result.txt

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

+ 17 - 0
Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt

@@ -0,0 +1,17 @@
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:>
+
+  OR requires one or more comma-separated '0' or '1' values.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:,>
+
+  OR requires one or more comma-separated '0' or '1' values.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

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

@@ -0,0 +1,4 @@
+add_custom_target(check ALL COMMAND check
+  $<OR:>
+  $<OR:,>
+  VERBATIM)

+ 3 - 0
Tests/RunCMake/GeneratorExpression/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

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

@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(BadOR)
+run_cmake(BadAND)
+run_cmake(BadNOT)