Add new flow-control commands for variables and policies scopes management

Add block() and endblock() commands offering the capability to create
new scopes for variables and/or policies.

Fixes: #20171
This commit is contained in:
Marc Chevrier 2022-08-05 10:55:32 +02:00
parent 604993248f
commit 44a2f3f332
66 changed files with 750 additions and 12 deletions

View File

@ -41,8 +41,8 @@ set the path with these commands:
:group 'cmake)
;; Keywords
(defconst cmake-keywords-block-open '("IF" "MACRO" "FOREACH" "ELSE" "ELSEIF" "WHILE" "FUNCTION"))
(defconst cmake-keywords-block-close '("ENDIF" "ENDFOREACH" "ENDMACRO" "ELSE" "ELSEIF" "ENDWHILE" "ENDFUNCTION"))
(defconst cmake-keywords-block-open '("BLOCK" "IF" "MACRO" "FOREACH" "ELSE" "ELSEIF" "WHILE" "FUNCTION"))
(defconst cmake-keywords-block-close '("ENDBLOCK" "ENDIF" "ENDFOREACH" "ENDMACRO" "ELSE" "ELSEIF" "ENDWHILE" "ENDFUNCTION"))
(defconst cmake-keywords
(let ((kwds (append cmake-keywords-block-open cmake-keywords-block-close nil)))
(delete-dups kwds)))

View File

@ -59,8 +59,8 @@ fun! CMakeGetIndent(lnum)
let cmake_closing_parens_line = '^\s*\()\+\)\s*$'
let cmake_indent_begin_regex = '^\s*\(IF\|MACRO\|FOREACH\|ELSE\|ELSEIF\|WHILE\|FUNCTION\)\s*('
let cmake_indent_end_regex = '^\s*\(ENDIF\|ENDFOREACH\|ENDMACRO\|ELSE\|ELSEIF\|ENDWHILE\|ENDFUNCTION\)\s*('
let cmake_indent_begin_regex = '^\s*\(BLOCK\|IF\|MACRO\|FOREACH\|ELSE\|ELSEIF\|WHILE\|FUNCTION\)\s*('
let cmake_indent_end_regex = '^\s*\(ENDBLOCK\|ENDIF\|ENDFOREACH\|ENDMACRO\|ELSE\|ELSEIF\|ENDWHILE\|ENDFUNCTION\)\s*('
if this_line =~? cmake_closing_parens_line
if previous_line !~? cmake_indent_open_regex

View File

@ -3832,6 +3832,7 @@ syn keyword cmakeCommand
\ add_subdirectory
\ add_test
\ aux_source_directory
\ block
\ break
\ build_command
\ cmake_host_system_information
@ -3859,6 +3860,7 @@ syn keyword cmakeCommand
\ define_property
\ enable_language
\ enable_testing
\ endblock
\ endfunction
\ endmacro
\ execute_process

74
Help/command/block.rst Normal file
View File

@ -0,0 +1,74 @@
block
-----
.. versionadded:: 3.25
Evaluate a group of commands with a dedicated variable and/or policy scope.
.. code-block:: cmake
block([SCOPE_FOR (POLICIES|VARIABLES)] [PROPAGATE <var-name>...])
<commands>
endblock()
All commands between ``block()`` and the matching :command:`endblock` are
recorded without being invoked. Once the :command:`endblock` is evaluated, the
recorded list of commands is invoked inside the requested scopes, and, finally,
the scopes created by ``block()`` command are removed.
``SCOPE_FOR``
Specify which scopes must be created.
``POLICIES``
Create a new policy scope. This is equivalent to
:command:`cmake_policy(PUSH)`.
``VARIABLES``
Create a new variable scope.
If ``SCOPE_FOR`` is not specified, this is equivalent to:
.. code-block:: cmake
block(SCOPE_FOR VARIABLES POLICIES)
``PROPAGATE``
When a variable scope is created by :command:`block` command, this option
set or unset the specified variables in the parent scope. This is equivalent
to :command:`set(PARENT_SCOPE)` or :command:`unset(PARENT_SCOPE)` commands.
.. code-block:: cmake
set(VAR1 "INIT1")
set(VAR2 "INIT2")
block(PROPAGATE VAR1 VAR2)
set(VAR1 "VALUE1")
unset(VAR2)
endblock()
# here, VAR1 holds value VALUE1 and VAR2 is unset
This option is only allowed when a variable scope is created. An error will
be raised in the other cases.
When the ``block`` is local to a :command:`foreach` or :command:`while`
command, the commands :command:`break` and :command:`continue` can be used
inside this block.
.. code-block:: cmake
while(TRUE)
block()
...
# the break() command will terminate the while() command
break()
endblock()
endwhile()
See Also
^^^^^^^^
* :command:`endblock`
* :command:`cmake_policy`

View File

@ -51,6 +51,7 @@ is equivalent to
To ensure consistency of the code, the following commands are not allowed:
* ``if`` / ``elseif`` / ``else`` / ``endif``
* ``block`` / ``endblock``
* ``while`` / ``endwhile``
* ``foreach`` / ``endforeach``
* ``function`` / ``endfunction``

View File

@ -103,6 +103,47 @@ Calls to the :command:`cmake_minimum_required(VERSION)`,
``cmake_policy(VERSION)``, or ``cmake_policy(SET)`` commands
influence only the current top of the policy stack.
.. versionadded:: 3.25
The :command:`block` and :command:`endblock` commands offer a more flexible
and more secure way to manage the policy stack. The pop action is done
automatically when the :command:`endblock` command is executed, so it avoid
to call the :command:`cmake_policy(POP)` command before each
:command:`return` command.
.. code-block:: cmake
# stack management with cmake_policy()
function(my_func)
cmake_policy(PUSH)
cmake_policy(SET ...)
if (<cond1>)
...
cmake_policy(POP)
return()
elseif(<cond2>)
...
cmake_policy(POP)
return()
endif()
...
cmake_policy(POP)
endfunction()
# stack management with block()/endblock()
function(my_func)
block(SCOPE_FOR POLICIES)
cmake_policy(SET ...)
if (<cond1>)
...
return()
elseif(<cond2>)
...
return()
endif()
...
endblock()
endfunction()
Commands created by the :command:`function` and :command:`macro`
commands record policy settings when they are created and
use the pre-record policies when they are invoked. If the function or

11
Help/command/endblock.rst Normal file
View File

@ -0,0 +1,11 @@
endblock
--------
.. versionadded:: 3.25
Ends a list of commands in a :command:`block` and removes the scopes
created by the :command:`block` command.
.. code-block:: cmake
endblock()

View File

@ -22,12 +22,17 @@ Set Normal Variable
Sets the given ``<variable>`` in the current function or directory scope.
If the ``PARENT_SCOPE`` option is given the variable will be set in
the scope above the current scope. Each new directory or function
creates a new scope. This command will set the value of a variable
into the parent directory or calling function (whichever is applicable
to the case at hand). The previous state of the variable's value stays the
same in the current scope (e.g., if it was undefined before, it is still
undefined and if it had a value, it is still that value).
the scope above the current scope. Each new directory or :command:`function`
command creates a new scope. A scope can also be created with the
:command:`block` command. This command will set the value of a variable into
the parent directory, calling function or encompassing scope (whichever is
applicable to the case at hand). The previous state of the variable's value
stays the same in the current scope (e.g., if it was undefined before, it is
still undefined and if it had a value, it is still that value).
The :command:`block(PROPAGATE)` command can be used as an alternate method to
:command:`set(PARENT_SCOPE)` and :command:`unset(PARENT_SCOPE)` commands to
update the parent scope.
Set Cache Entry
^^^^^^^^^^^^^^^

View File

@ -15,6 +15,7 @@ These commands are always available.
.. toctree::
:maxdepth: 1
/command/block
/command/break
/command/cmake_host_system_information
/command/cmake_language
@ -26,6 +27,7 @@ These commands are always available.
/command/continue
/command/else
/command/elseif
/command/endblock
/command/endforeach
/command/endfunction
/command/endif

View File

@ -0,0 +1,5 @@
block-command
-------------
* CMake language gains the commands :command:`block` and :command:`endblock` to
manage specific scopes (policy or variable) for group of commands.

View File

@ -571,6 +571,8 @@ set(SRCS
cmFindProgramCommand.h
cmForEachCommand.cxx
cmForEachCommand.h
cmBlockCommand.cxx
cmBlockCommand.h
cmFunctionBlocker.cxx
cmFunctionBlocker.h
cmFunctionCommand.cxx

200
Source/cmBlockCommand.cxx Normal file
View File

@ -0,0 +1,200 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmBlockCommand.h"
#include <cstdint> // IWYU pragma: keep
#include <utility>
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/enum_set>
#include <cmext/string_view>
#include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmFunctionBlocker.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
namespace {
enum class ScopeType : std::uint8_t
{
VARIABLES,
POLICIES
};
using ScopeSet = cm::enum_set<ScopeType>;
class BlockScopePushPop
{
public:
BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes);
~BlockScopePushPop() = default;
BlockScopePushPop(const BlockScopePushPop&) = delete;
BlockScopePushPop& operator=(const BlockScopePushPop&) = delete;
private:
std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
};
BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes)
{
if (scopes.contains(ScopeType::POLICIES)) {
this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
}
if (scopes.contains(ScopeType::VARIABLES)) {
this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
}
}
class cmBlockFunctionBlocker : public cmFunctionBlocker
{
public:
cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
std::vector<std::string> variableNames);
~cmBlockFunctionBlocker() override;
cm::string_view StartCommandName() const override { return "block"_s; }
cm::string_view EndCommandName() const override { return "endblock"_s; }
bool EndCommandSupportsArguments() const override { return false; }
bool ArgumentsMatch(cmListFileFunction const& lff,
cmMakefile& mf) const override;
bool Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus) override;
private:
cmMakefile* Makefile;
BlockScopePushPop BlockScope;
std::vector<std::string> VariableNames;
};
cmBlockFunctionBlocker::cmBlockFunctionBlocker(
cmMakefile* const mf, const ScopeSet& scopes,
std::vector<std::string> variableNames)
: Makefile{ mf }
, BlockScope{ mf, scopes }
, VariableNames{ std::move(variableNames) }
{
}
cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
{
for (auto const& varName : this->VariableNames) {
if (this->Makefile->IsNormalDefinitionSet(varName)) {
this->Makefile->RaiseScope(varName,
this->Makefile->GetDefinition(varName));
} else {
// unset variable in parent scope
this->Makefile->RaiseScope(varName, nullptr);
}
}
}
bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
cmMakefile&) const
{
// no arguments expected for endblock()
// but this method should not be called because EndCommandHasArguments()
// returns false.
return lff.Arguments().empty();
}
bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus)
{
auto& mf = inStatus.GetMakefile();
// Invoke all the functions that were collected in the block.
for (cmListFileFunction const& fn : functions) {
cmExecutionStatus status(mf);
mf.ExecuteCommand(fn, status);
if (status.GetReturnInvoked()) {
inStatus.SetReturnInvoked();
return true;
}
if (status.GetBreakInvoked()) {
inStatus.SetBreakInvoked();
return true;
}
if (status.GetContinueInvoked()) {
inStatus.SetContinueInvoked();
return true;
}
if (cmSystemTools::GetFatalErrorOccurred()) {
return true;
}
}
return true;
}
} // anonymous namespace
bool cmBlockCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
struct Arguments : public ArgumentParser::ParseResult
{
cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
};
static auto const parser = cmArgumentParser<Arguments>{}
.Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
.Bind("PROPAGATE"_s, &Arguments::Propagate);
std::vector<std::string> unrecognizedArguments;
auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
if (!unrecognizedArguments.empty()) {
status.SetError(cmStrCat("called with unsupported argument \"",
unrecognizedArguments[0], '"'));
cmSystemTools::SetFatalErrorOccurred();
return false;
}
if (parsedArgs.MaybeReportError(status.GetMakefile())) {
cmSystemTools::SetFatalErrorOccurred();
return true;
}
ScopeSet scopes;
if (parsedArgs.ScopeFor) {
for (auto const& scope : *parsedArgs.ScopeFor) {
if (scope == "VARIABLES"_s) {
scopes.insert(ScopeType::VARIABLES);
continue;
}
if (scope == "POLICIES"_s) {
scopes.insert(ScopeType::POLICIES);
continue;
}
status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
cmSystemTools::SetFatalErrorOccurred();
return false;
}
} else {
scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
}
if (!scopes.contains(ScopeType::VARIABLES) &&
!parsedArgs.Propagate.empty()) {
status.SetError(
"PROPAGATE cannot be specified without a new scope for VARIABLES");
cmSystemTools::SetFatalErrorOccurred();
return false;
}
// create a function blocker
auto fb = cm::make_unique<cmBlockFunctionBlocker>(
&status.GetMakefile(), scopes, parsedArgs.Propagate);
status.GetMakefile().AddFunctionBlocker(std::move(fb));
return true;
}

14
Source/cmBlockCommand.h Normal file
View File

@ -0,0 +1,14 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <string>
#include <vector>
class cmExecutionStatus;
/// Starts block() ... endblock() block
bool cmBlockCommand(std::vector<std::string> const& args,
cmExecutionStatus& status);

View File

@ -36,13 +36,14 @@ bool FatalError(cmExecutionStatus& status, std::string const& error)
return false;
}
std::array<cm::static_string_view, 12> InvalidCommands{
std::array<cm::static_string_view, 14> InvalidCommands{
{ // clang-format off
"function"_s, "endfunction"_s,
"macro"_s, "endmacro"_s,
"if"_s, "elseif"_s, "else"_s, "endif"_s,
"while"_s, "endwhile"_s,
"foreach"_s, "endforeach"_s
"foreach"_s, "endforeach"_s,
"block"_s, "endblock"_s
} // clang-format on
};

View File

@ -14,6 +14,7 @@
#include "cmAddLibraryCommand.h"
#include "cmAddSubDirectoryCommand.h"
#include "cmAddTestCommand.h"
#include "cmBlockCommand.h"
#include "cmBreakCommand.h"
#include "cmBuildCommand.h"
#include "cmCMakeLanguageCommand.h"
@ -126,6 +127,7 @@ void GetScriptingCommands(cmState* state)
state->AddFlowControlCommand("macro", cmMacroCommand);
state->AddFlowControlCommand("return", cmReturnCommand);
state->AddFlowControlCommand("while", cmWhileCommand);
state->AddFlowControlCommand("block", cmBlockCommand);
state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand);
state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired);
@ -198,6 +200,10 @@ void GetScriptingCommands(cmState* state)
"An ENDWHILE command was found outside of a proper "
"WHILE ENDWHILE structure. Or its arguments did not "
"match the opening WHILE command.");
state->AddUnexpectedFlowControlCommand(
"endblock",
"An ENDBLOCK command was found outside of a proper "
"BLOCK ENDBLOCK structure.");
#if !defined(CMAKE_BOOTSTRAP)
state->AddBuiltinCommand("cmake_host_system_information",

View File

@ -347,6 +347,7 @@ enum class NestingStateEnum
Foreach,
Function,
Macro,
Block
};
struct NestingState
@ -434,6 +435,16 @@ cm::optional<cmListFileContext> cmListFileParser::CheckNesting() const
return cmListFileContext::FromListFileFunction(func, this->FileName);
}
stack.pop_back();
} else if (name == "block") {
stack.push_back({
NestingStateEnum::Block,
cmListFileContext::FromListFileFunction(func, this->FileName),
});
} else if (name == "endblock") {
if (!TopIs(stack, NestingStateEnum::Block)) {
return cmListFileContext::FromListFileFunction(func, this->FileName);
}
stack.pop_back();
}
}

View File

@ -456,6 +456,7 @@ add_RunCMake_test(find_path)
add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
add_RunCMake_test(foreach)
add_RunCMake_test(function)
add_RunCMake_test(block)
add_RunCMake_test(get_filename_component)
add_RunCMake_test(get_property)
add_RunCMake_test(if)

View File

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

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at EndAlone.cmake:[0-9]+ \(endblock\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1 @@
endblock()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at EndAloneWithArgument.cmake:[0-9]+ \(endblock\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1 @@
endblock(WRONG_ARG)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at EndMissing.cmake:[0-9]+ \(block\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1 @@
block()

View File

@ -0,0 +1,9 @@
CMake Warning \(dev\) in EndWithArgument.cmake:
A logical block closing on the line
.+/Tests/RunCMake/block/EndWithArgument.cmake:[0-9]+ \(endblock\)
has unexpected arguments.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.

View File

@ -0,0 +1,2 @@
block()
endblock(END_ARG)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidArgument.cmake:[0-9]+ \(block\):
block PROPAGATE cannot be specified without a new scope for VARIABLES
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,2 @@
block(SCOPE_FOR POLICIES PROPAGATE VAR1)
endblock()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting1.cmake:[0-9]+ \(else\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,6 @@
if (TRUE)
block()
else()
endblock()
endif()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting2.cmake:[0-9]+ \(endblock\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,6 @@
block()
if (TRUE)
elseif(FALSE)
endblock()
endif()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting3.cmake:[0-9]+ \(endwhile\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,5 @@
while(TRUE)
block()
endwhile()
endblock()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting4.cmake:[0-9]+ \(endblock\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,5 @@
block()
foreach(item IN ITEMS A B)
endblock()
endforeach()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting5.cmake:[0-9]+ \(endfunction\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,5 @@
function(FUNC)
block()
endfunction()
endblock()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at InvalidNesting6.cmake:[0-9]+ \(endblock\):
Flow control statements are not properly nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,5 @@
block()
macro(FUNC)
endblock()
endmacro()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,7 @@
CMake Error at MissingArgument.cmake:[0-9]+ \(block\):
Error after keyword "SCOPE_FOR":
missing required value
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,2 @@
block(SCOPE_FOR)
endblock()

View File

@ -0,0 +1,22 @@
include(RunCMake)
run_cmake(WrongArgument)
run_cmake(InvalidArgument)
run_cmake(MissingArgument)
run_cmake(WrongScope)
run_cmake(EndMissing)
run_cmake(EndWithArgument)
run_cmake(EndAlone)
run_cmake(EndAloneWithArgument)
run_cmake(InvalidNesting1)
run_cmake(InvalidNesting2)
run_cmake(InvalidNesting3)
run_cmake(InvalidNesting4)
run_cmake(InvalidNesting5)
run_cmake(InvalidNesting6)
run_cmake(Scope)
run_cmake(Scope-VARIABLES)
run_cmake(Scope-POLICIES)
run_cmake(Workflows)

View File

@ -0,0 +1,30 @@
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
cmake_policy(SET CMP0139 NEW)
# create a block with a new scope for policies
block(SCOPE_FOR POLICIES)
set(VAR1 "INNER1")
unset(VAR2)
set(VAR3 "INNER3")
cmake_policy(SET CMP0139 OLD)
endblock()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "INNER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(DEFINED VAR2)
message(SEND_ERROR "block/endblock: VAR2 is unexpectedly defined: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()
cmake_policy(GET CMP0139 CMP0139_STATUS)
if(NOT CMP0139_STATUS STREQUAL "NEW")
message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
endif()

View File

@ -0,0 +1,52 @@
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
set(VAR3 "OUTER3")
set(VAR4 "OUTER4")
set(VAR5 "OUTER5")
set(VAR6 "CACHE6" CACHE STRING "")
set(VAR6 "OUTER6")
cmake_policy(SET CMP0139 NEW)
# create a block with a new scope for variables
block(SCOPE_FOR VARIABLES PROPAGATE VAR3 VAR4 VAR5 VAR6 VAR7)
set(VAR1 "INNER1")
set(VAR2 "INNER2" PARENT_SCOPE)
set(VAR3 "INNER3")
unset(VAR4)
unset(VAR6)
set(VAR7 "INNER7")
cmake_policy(SET CMP0139 OLD)
endblock()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()
if(DEFINED VAR4)
message(SEND_ERROR "block/endblock: VAR4 is unexpectedly defined: ${VAR4}")
endif()
if(NOT DEFINED VAR5 OR NOT VAR5 STREQUAL "OUTER5")
message(SEND_ERROR "block/endblock: VAR5 has unexpected value: ${VAR5}")
endif()
unset(VAR6 CACHE)
if (DEFINED VAR6)
message(SEND_ERROR "block/endblock: VAR6 is unexpectedly defined: ${VAR6}")
endif()
if(NOT DEFINED VAR7 OR NOT VAR7 STREQUAL "INNER7")
message(SEND_ERROR "block/endblock: VAR7 has unexpected value: ${VAR7}")
endif()
cmake_policy(GET CMP0139 CMP0139_STATUS)
if(NOT CMP0139_STATUS STREQUAL "OLD")
message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
endif()

View File

@ -0,0 +1,52 @@
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
set(VAR3 "OUTER3")
set(VAR4 "OUTER4")
set(VAR5 "OUTER5")
set(VAR6 "CACHE6" CACHE STRING "")
set(VAR6 "OUTER6")
cmake_policy(SET CMP0139 NEW)
# create a block with a new scope for variables and policies
block(PROPAGATE VAR3 VAR4 VAR5 VAR6 VAR7)
set(VAR1 "INNER1")
set(VAR2 "INNER2" PARENT_SCOPE)
set(VAR3 "INNER3")
unset(VAR4)
unset(VAR6)
set(VAR7 "INNER7")
cmake_policy(SET CMP0139 OLD)
endblock()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()
if(DEFINED VAR4)
message(SEND_ERROR "block/endblock: VAR4 is unexpectedly defined: ${VAR4}")
endif()
if(NOT DEFINED VAR5 OR NOT VAR5 STREQUAL "OUTER5")
message(SEND_ERROR "block/endblock: VAR5 has unexpected value: ${VAR5}")
endif()
unset(VAR6 CACHE)
if (DEFINED VAR6)
message(SEND_ERROR "block/endblock: VAR6 is unexpectedly defined: ${VAR6}")
endif()
if(NOT DEFINED VAR7 OR NOT VAR7 STREQUAL "INNER7")
message(SEND_ERROR "block/endblock: VAR6 has unexpected value: ${VAR7}")
endif()
cmake_policy(GET CMP0139 CMP0139_STATUS)
if(NOT CMP0139_STATUS STREQUAL "NEW")
message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
endif()

View File

@ -0,0 +1,78 @@
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
set(VAR3 "OUTER3")
while (TRUE)
# create a block with a new scope for variables
block(SCOPE_FOR VARIABLES PROPAGATE VAR3)
set(VAR2 "INNER2" PARENT_SCOPE)
set(VAR3 "INNER3")
break()
endblock()
endwhile()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
set(VAR3 "OUTER3")
function (OUTER)
# create a block with a new scope for variables
block(SCOPE_FOR VARIABLES PROPAGATE VAR3)
set(VAR2 "INNER2" PARENT_SCOPE)
set(VAR3 "INNER3")
return()
endblock()
set(VAR1 "INNER1" PARENT_SCOPE)
endfunction()
outer()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "OUTER2")
message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "OUTER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()
set(VAR1 "OUTER1")
set(VAR2 "OUTER2")
set(VAR3 "OUTER3")
foreach (id IN ITEMS 1 2 3)
# create a block with a new scope for variables
block(SCOPE_FOR VARIABLES PROPAGATE VAR${id})
set(VAR${id} "INNER${id}")
continue()
set(VAR${id} "BAD${id}")
endblock()
endforeach()
# check final values for variables
if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "INNER1")
message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
endif()
if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
endif()
if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
endif()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at WrongArgument.cmake:[0-9]+ \(block\):
block called with unsupported argument "WRONG_ARG"
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,2 @@
block(WRONG_ARG)
endblock()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at WrongScope.cmake:[0-9]+ \(block\):
block SCOPE_FOR unsupported scope "WRONG_SCOPE"
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,2 @@
block(SCOPE_FOR WRONG_SCOPE)
endblock()

View File

@ -8,6 +8,7 @@ foreach(command IN ITEMS
"if" "elseif" "else" "endif"
"while" "endwhile"
"foreach" "endforeach"
"block" "endblock"
)
message(STATUS "Running call_invalid_command for ${command}...")
run_cmake_with_options(call_invalid_command -Dcommand=${command})
@ -42,6 +43,7 @@ foreach(command IN ITEMS
"if" "elseif" "else" "endif"
"while" "endwhile"
"foreach" "endforeach"
"block" "endblock"
"return"
)
message(STATUS "Running defer_call_invalid_command for ${command}...")

View File

@ -301,6 +301,7 @@ CMAKE_CXX_SOURCES="\
cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \
cmBinUtilsWindowsPELinker \
cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \
cmBlockCommand \
cmBreakCommand \
cmBuildCommand \
cmCMakeLanguageCommand \