瀏覽代碼

ENH: Add RANGE support to FOREACH

Andy Cedilnik 21 年之前
父節點
當前提交
8750f1c277
共有 3 個文件被更改,包括 103 次插入2 次删除
  1. 73 1
      Source/cmForEachCommand.cxx
  2. 11 1
      Source/cmForEachCommand.h
  3. 19 0
      Tests/StringFileTest/CMakeLists.txt

+ 73 - 1
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;

+ 11 - 1
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);

+ 19 - 0
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")