diff --git a/Help/command/break.rst b/Help/command/break.rst index 8f1067b1e..fc2cd3c7b 100644 --- a/Help/command/break.rst +++ b/Help/command/break.rst @@ -8,3 +8,5 @@ Break from an enclosing foreach or while loop. break() Breaks from an enclosing foreach loop or while loop + +See also the :command:`continue` command. diff --git a/Help/command/continue.rst b/Help/command/continue.rst new file mode 100644 index 000000000..1c7d6730c --- /dev/null +++ b/Help/command/continue.rst @@ -0,0 +1,12 @@ +continue +-------- + +Continue to the top of enclosing foreach or while loop. + +:: + + continue() + +The ``continue`` command allows a cmake script to abort the rest of a block +in a :command:`foreach` or :command:`while` loop, and start at the top of +the next iteration. See also the :command:`break` command. diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 9c1d3b9c9..4616dd1db 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -31,6 +31,7 @@ These commands may be used freely in CMake projects. /command/cmake_minimum_required /command/cmake_policy /command/configure_file + /command/continue /command/create_test_sourcelist /command/define_property /command/elseif diff --git a/Help/manual/cmake-language.7.rst b/Help/manual/cmake-language.7.rst index 9c511cae2..15c101f63 100644 --- a/Help/manual/cmake-language.7.rst +++ b/Help/manual/cmake-language.7.rst @@ -469,8 +469,10 @@ Loops The :command:`foreach`/:command:`endforeach` and :command:`while`/:command:`endwhile` commands delimit code -blocks to be executed in a loop. The :command:`break` command -may be used inside such blocks to terminate the loop early. +blocks to be executed in a loop. Inside such blocks the +:command:`break` command may be used to terminate the loop +early whereas the :command:`continue` command may be used +to start with the next iteration immediately. Command Definitions ------------------- diff --git a/Help/release/dev/add-continue-command.rst b/Help/release/dev/add-continue-command.rst new file mode 100644 index 000000000..4995a8ead --- /dev/null +++ b/Help/release/dev/add-continue-command.rst @@ -0,0 +1,6 @@ +add-continue-command +-------------------- + +* A new :command:`continue` command was added that can be called inside loop + contexts to end the current iteration and start the next one at the top of + the loop block. diff --git a/Source/cmBootstrapCommands1.cxx b/Source/cmBootstrapCommands1.cxx index 55026099f..4274d85b2 100644 --- a/Source/cmBootstrapCommands1.cxx +++ b/Source/cmBootstrapCommands1.cxx @@ -28,6 +28,7 @@ #include "cmCMakePolicyCommand.cxx" #include "cmCommandArgumentsHelper.cxx" #include "cmConfigureFileCommand.cxx" +#include "cmContinueCommand.cxx" #include "cmCoreTryCompile.cxx" #include "cmCreateTestSourceList.cxx" #include "cmDefinePropertyCommand.cxx" @@ -70,6 +71,7 @@ void GetBootstrapCommands1(std::list& commands) commands.push_back(new cmCMakeMinimumRequired); commands.push_back(new cmCMakePolicyCommand); commands.push_back(new cmConfigureFileCommand); + commands.push_back(new cmContinueCommand); commands.push_back(new cmCreateTestSourceList); commands.push_back(new cmDefinePropertyCommand); commands.push_back(new cmElseCommand); diff --git a/Source/cmContinueCommand.cxx b/Source/cmContinueCommand.cxx new file mode 100644 index 000000000..4a03caade --- /dev/null +++ b/Source/cmContinueCommand.cxx @@ -0,0 +1,39 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2014 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmContinueCommand.h" + +// cmContinueCommand +bool cmContinueCommand::InitialPass(std::vector const &args, + cmExecutionStatus &status) +{ + if(!this->Makefile->IsLoopBlock()) + { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, + "A CONTINUE command was found outside of a " + "proper FOREACH or WHILE loop scope."); + cmSystemTools::SetFatalErrorOccured(); + return true; + } + + status.SetContinueInvoked(true); + + if(!args.empty()) + { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, + "The CONTINUE command does not accept any " + "arguments."); + cmSystemTools::SetFatalErrorOccured(); + return true; + } + + return true; +} diff --git a/Source/cmContinueCommand.h b/Source/cmContinueCommand.h new file mode 100644 index 000000000..093b14f91 --- /dev/null +++ b/Source/cmContinueCommand.h @@ -0,0 +1,55 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2014 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmContinueCommand_h +#define cmContinueCommand_h + +#include "cmCommand.h" + +/** \class cmContinueCommand + * \brief Continue from an enclosing foreach or while loop + * + * cmContinueCommand returns from an enclosing foreach or while loop + */ +class cmContinueCommand : public cmCommand +{ +public: + /** + * This is a virtual constructor for the command. + */ + virtual cmCommand* Clone() + { + return new cmContinueCommand; + } + + /** + * This is called when the command is first encountered in + * the CMakeLists.txt file. + */ + virtual bool InitialPass(std::vector const& args, + cmExecutionStatus &status); + + /** + * This determines if the command is invoked when in script mode. + */ + virtual bool IsScriptable() const { return true; } + + /** + * The name of the command as specified in CMakeList.txt. + */ + virtual std::string GetName() const { return "continue"; } + + cmTypeMacro(cmContinueCommand, cmCommand); +}; + + + +#endif diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h index 5c94a9758..d4da5a457 100644 --- a/Source/cmExecutionStatus.h +++ b/Source/cmExecutionStatus.h @@ -36,10 +36,16 @@ public: virtual bool GetBreakInvoked() { return this->BreakInvoked; } + virtual void SetContinueInvoked(bool val) + { this->ContinueInvoked = val; } + virtual bool GetContinueInvoked() + { return this->ContinueInvoked; } + virtual void Clear() { this->ReturnInvoked = false; this->BreakInvoked = false; + this->ContinueInvoked = false; this->NestedError = false; } virtual void SetNestedError(bool val) { this->NestedError = val; } @@ -49,6 +55,7 @@ public: protected: bool ReturnInvoked; bool BreakInvoked; + bool ContinueInvoked; bool NestedError; }; diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index dec51579a..03d65907a 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -69,6 +69,10 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, mf.AddDefinition(this->Args[0],oldDef.c_str()); return true; } + if (status.GetContinueInvoked()) + { + break; + } if(cmSystemTools::GetFatalErrorOccured() ) { return true; diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index f728c15e3..b8e30b752 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -151,6 +151,11 @@ IsFunctionBlocked(const cmListFileFunction& lff, inStatus.SetBreakInvoked(true); return true; } + if (status.GetContinueInvoked()) + { + inStatus.SetContinueInvoked(true); + return true; + } } } return true; diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index d36095e6c..47edb03f0 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -83,6 +83,10 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, { return true; } + if (status.GetContinueInvoked()) + { + break; + } if(cmSystemTools::GetFatalErrorOccured() ) { return true; diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 9832a9510..54fe2d9ca 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -102,6 +102,7 @@ add_RunCMake_test(add_dependencies) add_RunCMake_test(build_command) add_RunCMake_test(export) add_RunCMake_test(cmake_minimum_required) +add_RunCMake_test(continue) add_RunCMake_test(file) add_RunCMake_test(find_package) add_RunCMake_test(get_filename_component) diff --git a/Tests/RunCMake/continue/CMakeLists.txt b/Tests/RunCMake/continue/CMakeLists.txt new file mode 100644 index 000000000..ef2163c29 --- /dev/null +++ b/Tests/RunCMake/continue/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.1) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/continue/ContinueForEachInLists.cmake b/Tests/RunCMake/continue/ContinueForEachInLists.cmake new file mode 100644 index 000000000..fbd7359fe --- /dev/null +++ b/Tests/RunCMake/continue/ContinueForEachInLists.cmake @@ -0,0 +1,10 @@ +list(APPEND foo 1 2 3 4 5) + +message(STATUS "start") +foreach(iter IN LISTS foo) + if("${iter}" EQUAL 1 OR "${iter}" EQUAL 3 OR "${iter}" EQUAL 5) + continue() + endif() + message(STATUS "${iter}") +endforeach() +message(STATUS "end") diff --git a/Tests/RunCMake/continue/ContinueForeach-stdout.txt b/Tests/RunCMake/continue/ContinueForeach-stdout.txt new file mode 100644 index 000000000..955b8595d --- /dev/null +++ b/Tests/RunCMake/continue/ContinueForeach-stdout.txt @@ -0,0 +1,4 @@ +-- start +-- 2 +-- 4 +-- end diff --git a/Tests/RunCMake/continue/ContinueForeach.cmake b/Tests/RunCMake/continue/ContinueForeach.cmake new file mode 100644 index 000000000..9b3e17f3e --- /dev/null +++ b/Tests/RunCMake/continue/ContinueForeach.cmake @@ -0,0 +1,8 @@ +message(STATUS "start") +foreach(iter RANGE 1 5) + if("${iter}" EQUAL 1 OR "${iter}" EQUAL 3 OR "${iter}" EQUAL 5) + continue() + endif() + message(STATUS "${iter}") +endforeach() +message(STATUS "end") diff --git a/Tests/RunCMake/continue/ContinueNestedForeach-stdout.txt b/Tests/RunCMake/continue/ContinueNestedForeach-stdout.txt new file mode 100644 index 000000000..adb02bccc --- /dev/null +++ b/Tests/RunCMake/continue/ContinueNestedForeach-stdout.txt @@ -0,0 +1,6 @@ +-- start +-- 7 2 +-- 7 4 +-- 9 2 +-- 9 4 +-- end diff --git a/Tests/RunCMake/continue/ContinueNestedForeach.cmake b/Tests/RunCMake/continue/ContinueNestedForeach.cmake new file mode 100644 index 000000000..de7c51b20 --- /dev/null +++ b/Tests/RunCMake/continue/ContinueNestedForeach.cmake @@ -0,0 +1,13 @@ +message(STATUS "start") +foreach(outer RANGE 7 9) + if("${outer}" EQUAL 8) + continue() + endif() + foreach(inner RANGE 1 5) + if("${inner}" EQUAL 1 OR "${inner}" EQUAL 3 OR "${inner}" EQUAL 5) + continue() + endif() + message(STATUS "${outer} ${inner}") + endforeach() +endforeach() +message(STATUS "end") diff --git a/Tests/RunCMake/continue/ContinueWhile-stdout.txt b/Tests/RunCMake/continue/ContinueWhile-stdout.txt new file mode 100644 index 000000000..f99b2a1ac --- /dev/null +++ b/Tests/RunCMake/continue/ContinueWhile-stdout.txt @@ -0,0 +1,6 @@ +-- start +-- a +-- aa +-- aaaa +-- aaaaa +-- end diff --git a/Tests/RunCMake/continue/ContinueWhile.cmake b/Tests/RunCMake/continue/ContinueWhile.cmake new file mode 100644 index 000000000..c1fa87ac2 --- /dev/null +++ b/Tests/RunCMake/continue/ContinueWhile.cmake @@ -0,0 +1,10 @@ +message(STATUS "start") +unset(iter) +while(NOT "${iter}" STREQUAL "aaaaa") + set(iter "${iter}a") + if("${iter}" STREQUAL "aaa") + continue() + endif() + message(STATUS "${iter}") +endwhile() +message(STATUS "end") diff --git a/Tests/RunCMake/continue/NoArgumentsToContinue-result.txt b/Tests/RunCMake/continue/NoArgumentsToContinue-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/continue/NoArgumentsToContinue-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/continue/NoArgumentsToContinue-stderr.txt b/Tests/RunCMake/continue/NoArgumentsToContinue-stderr.txt new file mode 100644 index 000000000..66be46295 --- /dev/null +++ b/Tests/RunCMake/continue/NoArgumentsToContinue-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at NoArgumentsToContinue.cmake:2 \(continue\): + The CONTINUE command does not accept any arguments. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/continue/NoArgumentsToContinue.cmake b/Tests/RunCMake/continue/NoArgumentsToContinue.cmake new file mode 100644 index 000000000..609804d4b --- /dev/null +++ b/Tests/RunCMake/continue/NoArgumentsToContinue.cmake @@ -0,0 +1,3 @@ +foreach(i RANGE 1 2) + continue(1) +endforeach() diff --git a/Tests/RunCMake/continue/NoEnclosingBlock-result.txt b/Tests/RunCMake/continue/NoEnclosingBlock-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlock-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/continue/NoEnclosingBlock-stderr.txt b/Tests/RunCMake/continue/NoEnclosingBlock-stderr.txt new file mode 100644 index 000000000..24caf5712 --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlock-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at NoEnclosingBlock.cmake:1 \(continue\): + A CONTINUE command was found outside of a proper FOREACH or WHILE loop + scope. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/continue/NoEnclosingBlock.cmake b/Tests/RunCMake/continue/NoEnclosingBlock.cmake new file mode 100644 index 000000000..9661e0d3e --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlock.cmake @@ -0,0 +1 @@ +continue() diff --git a/Tests/RunCMake/continue/NoEnclosingBlockInFunction-result.txt b/Tests/RunCMake/continue/NoEnclosingBlockInFunction-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlockInFunction-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/continue/NoEnclosingBlockInFunction-stderr.txt b/Tests/RunCMake/continue/NoEnclosingBlockInFunction-stderr.txt new file mode 100644 index 000000000..af4f3b680 --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlockInFunction-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at NoEnclosingBlockInFunction.cmake:2 \(continue\): + A CONTINUE command was found outside of a proper FOREACH or WHILE loop + scope. +Call Stack \(most recent call first\): + NoEnclosingBlockInFunction.cmake:6 \(foo\) + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/continue/NoEnclosingBlockInFunction.cmake b/Tests/RunCMake/continue/NoEnclosingBlockInFunction.cmake new file mode 100644 index 000000000..eb2a0984f --- /dev/null +++ b/Tests/RunCMake/continue/NoEnclosingBlockInFunction.cmake @@ -0,0 +1,8 @@ +function(foo) + continue() +endfunction(foo) + +foreach(i RANGE 1 2) + foo() + message(STATUS "Hello World") +endforeach() diff --git a/Tests/RunCMake/continue/RunCMakeTest.cmake b/Tests/RunCMake/continue/RunCMakeTest.cmake new file mode 100644 index 000000000..37caf9c3b --- /dev/null +++ b/Tests/RunCMake/continue/RunCMakeTest.cmake @@ -0,0 +1,9 @@ +include(RunCMake) + +run_cmake(ContinueForeach) +run_cmake(ContinueForEachInLists) +run_cmake(ContinueNestedForeach) +run_cmake(ContinueWhile) +run_cmake(NoArgumentsToContinue) +run_cmake(NoEnclosingBlock) +run_cmake(NoEnclosingBlockInFunction)