summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/cmForEachCommand.cxx74
-rw-r--r--Source/cmForEachCommand.h12
-rw-r--r--Tests/StringFileTest/CMakeLists.txt19
3 files changed, 103 insertions, 2 deletions
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index cd6e1e2..24f17fa 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -106,7 +106,79 @@ bool cmForEachCommand::InitialPass(std::vector<std::string> const& args)
// create a function blocker
cmForEachFunctionBlocker *f = new cmForEachFunctionBlocker();
- f->m_Args = args;
+ if ( args.size() > 1 )
+ {
+ if ( args[1] == "RANGE" )
+ {
+ int start = 0;
+ int stop = 0;
+ int step = 0;
+ if ( args.size() == 3 )
+ {
+ stop = atoi(args[2].c_str());
+ }
+ if ( args.size() == 4 )
+ {
+ start = atoi(args[2].c_str());
+ stop = atoi(args[3].c_str());
+ }
+ if ( args.size() == 5 )
+ {
+ start = atoi(args[2].c_str());
+ stop = atoi(args[3].c_str());
+ step = atoi(args[4].c_str());
+ }
+ if ( step == 0 )
+ {
+ if ( start > stop )
+ {
+ step = -1;
+ }
+ else
+ {
+ step = 1;
+ }
+ }
+ if (
+ (start > stop && step > 0) ||
+ (start < stop && step < 0) ||
+ step == 0
+ )
+ {
+ cmOStringStream str;
+ str << "called with incorrect range specification: start ";
+ str << start << ", stop " << stop << ", step " << step;
+ this->SetError(str.str().c_str());
+ return false;
+ }
+ std::vector<std::string> range;
+ char buffer[100];
+ range.push_back(args[0]);
+ int cc;
+ for ( cc = start; ; cc += step )
+ {
+ if ( (step > 0 && cc > stop) || (step < 0 && cc < stop) )
+ {
+ break;
+ }
+ sprintf(buffer, "%d", cc);
+ range.push_back(buffer);
+ if ( cc == stop )
+ {
+ break;
+ }
+ }
+ f->m_Args = range;
+ }
+ else
+ {
+ f->m_Args = args;
+ }
+ }
+ else
+ {
+ f->m_Args = args;
+ }
m_Makefile->AddFunctionBlocker(f);
return true;
diff --git a/Source/cmForEachCommand.h b/Source/cmForEachCommand.h
index bb767e9..64602c9 100644
--- a/Source/cmForEachCommand.h
+++ b/Source/cmForEachCommand.h
@@ -98,12 +98,22 @@ public:
" COMMAND2(ARGS ...)\n"
" ...\n"
" ENDFOREACH(loop_var)\n"
+ " FOREACH(loop_var RANGE total)\n"
+ " FOREACH(loop_var RANGE start stop [step])\n"
"All commands between FOREACH and the matching ENDFOREACH are recorded "
"without being invoked. Once the ENDFOREACH is evaluated, the "
"recorded list of commands is invoked once for each argument listed "
"in the original FOREACH command. Each recorded command is modified "
"before invocation to replace any occurrence of \"${loop_var}\" with "
- "the current value in the list.";
+ "the current value in the list.\n"
+ "Foreach can also iterate over the range of numbers generated by "
+ "foreach. There are three types of this iteration:\n"
+ "* When specifying single number, the range will have elements "
+ "0 to \"total\".\n"
+ "* When specifying two numbers, the range will have elements from "
+ "the first number to the second number.\n"
+ "* The third optional number is the increment used to iterate from "
+ "the first number to the second number.";
}
cmTypeMacro(cmForEachCommand, cmCommand);
diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt
index 74b99ec..c0a904d 100644
--- a/Tests/StringFileTest/CMakeLists.txt
+++ b/Tests/StringFileTest/CMakeLists.txt
@@ -73,3 +73,22 @@ FILE(GLOB src_files "${expr}")
MESSAGE("Globbed files [${src_files}].")
ADD_EXECUTABLE(StringFileTest ${src_files})
+
+# Test FOREACH range
+MESSAGE("Cheack if FOREACH with RANGE works")
+MACRO(TEST_RANGE ARGS CHECK)
+ SET(r)
+ FOREACH(a RANGE ${ARGS})
+ SET(r ${r} ${a})
+ ENDFOREACH(a)
+ MESSAGE("FOREACH with RANGE ${ARGS} produces ${r}")
+ IF("x${r}x" MATCHES "^x${CHECK}x$")
+ ELSE("x${r}x" MATCHES "^x${CHECK}x$")
+ MESSAGE(SEND_ERROR "The range resulted in: ${r} should be ${CHECK}")
+ ENDIF("x${r}x" MATCHES "^x${CHECK}x$")
+ENDMACRO(TEST_RANGE)
+TEST_RANGE("5" "0;1;2;3;4;5")
+TEST_RANGE("3;5" "3;4;5")
+TEST_RANGE("5;3" "5;4;3")
+TEST_RANGE("3;10;2" "3;5;7;9")
+TEST_RANGE("10;0;-3" "10;7;4;1")