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.
This commit is contained in:
Brad King 2012-08-13 09:49:53 -04:00
parent cd3bd23266
commit ebf05abda1
17 changed files with 199 additions and 1 deletions

View File

@ -16,6 +16,8 @@
"Generator expressions are evaluted during build system generation " \
"to produce information specific to each build configuration. " \
"Valid expressions are:\n" \
" $<0:...> = empty string (ignores \"...\")\n" \
" $<1:...> = content of \"...\"\n" \
" $<CONFIGURATION> = configuration name\n" \
" $<TARGET_FILE:tgt> = main file (.exe, .so.1.2, .a)\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" \
" $<TARGET_FILE_DIR:tgt>/$<TARGET_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

View File

@ -102,6 +102,26 @@ bool cmGeneratorExpression::Evaluate()
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)
{
@ -116,6 +136,32 @@ bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
{
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
{
result = "Expression syntax not recognized.";

View File

@ -551,6 +551,16 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
FAIL_REGULAR_EXPRESSION "Unexpected: ")
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}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/CustomCommand"

View File

@ -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
)

View File

@ -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")

View File

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

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

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

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

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

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

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

View File

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

View File

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