diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index 7a826c631..7a035237f 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -16,6 +16,8 @@ =========================================================================*/ #include "cmForEachCommand.h" +#include + bool cmForEachFunctionBlocker:: IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, cmExecutionStatus &inStatus) @@ -116,6 +118,10 @@ bool cmForEachCommand this->SetError("called with incorrect number of arguments"); return false; } + if(args.size() > 1 && args[1] == "IN") + { + return this->HandleInMode(args); + } // create a function blocker cmForEachFunctionBlocker *f = new cmForEachFunctionBlocker(); @@ -197,3 +203,45 @@ bool cmForEachCommand return true; } +//---------------------------------------------------------------------------- +bool cmForEachCommand::HandleInMode(std::vector const& args) +{ + cmsys::auto_ptr f(new cmForEachFunctionBlocker()); + f->Args.push_back(args[0]); + + enum Doing { DoingNone, DoingLists, DoingItems }; + Doing doing = DoingNone; + for(unsigned int i=2; i < args.size(); ++i) + { + if(doing == DoingItems) + { + f->Args.push_back(args[i]); + } + else if(args[i] == "LISTS") + { + doing = DoingLists; + } + else if(args[i] == "ITEMS") + { + doing = DoingItems; + } + else if(doing == DoingLists) + { + const char* value = this->Makefile->GetDefinition(args[i].c_str()); + if(value && *value) + { + cmSystemTools::ExpandListArgument(value, f->Args, true); + } + } + else + { + cmOStringStream e; + e << "Unknown argument:\n" << " " << args[i] << "\n"; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); + return true; + } + } + + this->Makefile->AddFunctionBlocker(f.release()); // TODO: pass auto_ptr + return true; +} diff --git a/Source/cmForEachCommand.h b/Source/cmForEachCommand.h index 3e0371119..ea7faa592 100644 --- a/Source/cmForEachCommand.h +++ b/Source/cmForEachCommand.h @@ -94,14 +94,14 @@ 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. Before each iteration of the loop " "\"${loop_var}\" will be set as a variable with " "the current value in the list.\n" + " foreach(loop_var RANGE total)\n" + " foreach(loop_var RANGE start stop [step])\n" "Foreach can also iterate over a generated range of numbers. " "There are three types of this iteration:\n" "* When specifying single number, the range will have elements " @@ -109,10 +109,21 @@ public: "* 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."; + "the first number to the second number." + "\n" + " foreach(loop_var IN [LISTS [list1 [...]]]\n" + " [ITEMS [item1 [...]]])\n" + "Iterates over a precise list of items. " + "The LISTS option names list-valued variables to be traversed, " + "including empty elements (an empty string is a zero-length list). " + "The ITEMS option ends argument parsing and includes all arguments " + "following it in the iteration." + ; } cmTypeMacro(cmForEachCommand, cmCommand); +private: + bool HandleInMode(std::vector const& args); }; diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index f1598d72b..f2789e623 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt @@ -217,3 +217,22 @@ 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") + +# Test FOREACH IN signature +set(list1 "" a "") +set(list2 a "" b) +set(var_) +set(var_a) +set(var_b) +foreach(item IN LISTS list1 list2 ITEMS "" a "") + set(var_${item} "${var_${item}}x") +endforeach(item) +if(NOT "${var_}" STREQUAL "xxxxx") + message(FATAL_ERROR "count incorrect for \"\": [${var_}]") +endif() +if(NOT "${var_a}" STREQUAL "xxx") + message(FATAL_ERROR "count incorrect for \"a\": [${var_a}]") +endif() +if(NOT "${var_b}" STREQUAL "x") + message(FATAL_ERROR "count incorrect \"b\": [${var_b}]") +endif()