math(): Add INCREMENT and DECREMENT modes

This commit is contained in:
Kyle Edwards
2026-07-14 16:41:24 -04:00
parent 81ba0f12d2
commit 8cf21478ac
44 changed files with 232 additions and 2 deletions

View File

@@ -1,7 +1,10 @@
math
----
Evaluate a mathematical expression.
Perform mathematical operations.
Expression Evaluation
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: cmake
@@ -37,3 +40,16 @@ For example
math(EXPR value "100 * 0xA" OUTPUT_FORMAT DECIMAL) # value is set to "1000"
math(EXPR value "100 * 0xA" OUTPUT_FORMAT HEXADECIMAL) # value is set to "0x3e8"
Increment/Decrement
^^^^^^^^^^^^^^^^^^^
.. code-block:: cmake
math(INCREMENT <variable>)
math(DECREMENT <variable>)
.. versionadded:: 4.5
Take the value of ``<variable>``, increment/decrement it, and store the result back
in ``<variable>``. The variable must be defined and be a valid base-10 integer.

View File

@@ -0,0 +1,4 @@
math-increment-decrement
------------------------
* The :command:`math` command gained new ``INCREMENT`` and ``DECREMENT`` modes.

View File

@@ -3,6 +3,11 @@
#include "cmMathCommand.h"
#include <cstdio>
#include <limits>
#include <stdexcept>
#include <cm/string_view>
#include <cmext/string_view>
#include <cm3p/kwiml/int.h>
@@ -10,10 +15,16 @@
#include "cmExecutionStatus.h"
#include "cmExprParserHelper.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
#include "cmValue.h"
namespace {
bool HandleExprCommand(std::vector<std::string> const& args,
cmExecutionStatus& status);
bool HandleIncDecCommand(std::vector<std::string> const& args,
cmExecutionStatus& status, int amount,
long long overflowFrom, long long overflowTo,
cm::string_view verbing);
}
bool cmMathCommand(std::vector<std::string> const& args,
@@ -27,6 +38,16 @@ bool cmMathCommand(std::vector<std::string> const& args,
if (subCommand == "EXPR") {
return HandleExprCommand(args, status);
}
if (subCommand == "INCREMENT") {
return HandleIncDecCommand(
args, status, 1, std::numeric_limits<long long>::max(),
std::numeric_limits<long long>::min(), "incrementing"_s);
}
if (subCommand == "DECREMENT") {
return HandleIncDecCommand(
args, status, -1, std::numeric_limits<long long>::min(),
std::numeric_limits<long long>::max(), "decrementing"_s);
}
std::string e = "does not recognize sub-command " + subCommand;
status.SetError(e);
return false;
@@ -117,4 +138,50 @@ bool HandleExprCommand(std::vector<std::string> const& args,
status.GetMakefile().AddDefinition(outputVariable, buffer);
return true;
}
bool HandleIncDecCommand(std::vector<std::string> const& args,
cmExecutionStatus& status, int amount,
long long overflowFrom, long long overflowTo,
cm::string_view verbing)
{
std::string const messageHint = cmStrCat("sub-command ", args[0], " ");
if (args.size() != 2) {
status.SetError(cmStrCat(messageHint, "wrong number of arguments"));
return false;
}
auto value = status.GetMakefile().GetDefinition(args[1]);
if (!value) {
status.SetError(
cmStrCat(messageHint, "variable \"", args[1], "\" is not defined"));
return false;
}
if (value->empty()) {
status.SetError(
cmStrCat(messageHint, "value \"\" is not a valid integer"));
return false;
}
std::size_t pos = 0;
long long intValue = 0;
try {
intValue = std::stoll(*value, &pos, 10);
} catch (std::invalid_argument&) {
// Do nothing, leave pos as is, which will trigger the error
}
if (pos != value->length()) {
status.SetError(
cmStrCat(messageHint, "value \"", *value, "\" is not a valid integer"));
return false;
}
auto newValue = intValue + amount;
if (intValue == overflowFrom) {
status.GetMakefile().IssueDiagnostic(
cmDiagnosticCategory::CMD_AUTHOR,
cmStrCat("signed integer overflow while ", verbing, ":\n ", intValue,
"\n"));
// Overflow is undefined behavior in C++, so define it manually
newValue = overflowTo;
}
status.GetMakefile().AddDefinition(args[1], std::to_string(newValue));
return true;
}
}

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-Empty\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT value "" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value "")
math(DECREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-Extra\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT value "0a" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value 0a)
math(DECREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-Invalid\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT value "notanint" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value notanint)
math(DECREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-NotEnoughArgs\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT wrong number of arguments
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
math(DECREMENT)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-TooManyArgs\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT wrong number of arguments
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
math(DECREMENT value 1)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-DECREMENT-Undefined\.cmake:[0-9]+ \(math\):
math sub-command DECREMENT variable "value" is not defined
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value)
math(DECREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-Empty\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT value "" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value "")
math(INCREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-Extra\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT value "0a" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value 0a)
math(INCREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-Invalid\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT value "notanint" is not a valid integer
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value notanint)
math(INCREMENT value)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-NotEnoughArgs\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT wrong number of arguments
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
math(INCREMENT)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-TooManyArgs\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT wrong number of arguments
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
math(INCREMENT value 1)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at MATH-INCREMENT-Undefined\.cmake:[0-9]+ \(math\):
math sub-command INCREMENT variable "value" is not defined
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,2 @@
set(value)
math(INCREMENT value)

View File

@@ -10,3 +10,21 @@ math_test("100 * 10" 1000)
math_test("100 * 10" 1000 OUTPUT_FORMAT DECIMAL)
math_test("100 * 0xA" 1000 OUTPUT_FORMAT DECIMAL)
math_test("100 * 0xA" 0x3e8 OUTPUT_FORMAT HEXADECIMAL)
function(inc_dec_test command initial_value expected)
set(value "${initial_value}")
math("${command}" value)
if(NOT value STREQUAL "${expected}")
message(FATAL_ERROR "wrong ${command} result: ${value} != ${expected}")
endif()
endfunction()
inc_dec_test(INCREMENT 0 1)
inc_dec_test(INCREMENT 4 5)
inc_dec_test(INCREMENT -04 -3)
inc_dec_test(INCREMENT -9223372036854775808 -9223372036854775807)
inc_dec_test(DECREMENT 0 -1)
inc_dec_test(DECREMENT 4 3)
inc_dec_test(DECREMENT -04 -5)
inc_dec_test(DECREMENT 9223372036854775807 9223372036854775806)

View File

@@ -68,4 +68,26 @@ CMake Warning \(author\) at [^
-9223372036854775808
This warning is for project developers\. Use -Wno-author to suppress it\.
CMake Warning \(author\) at [^
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
signed integer overflow while incrementing:
9223372036854775807
Call Stack \(most recent call first\):
[^
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(inc_dec\)
This warning is for project developers\. Use -Wno-author to suppress it\.
CMake Warning \(author\) at [^
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
signed integer overflow while decrementing:
-9223372036854775808
Call Stack \(most recent call first\):
[^
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(inc_dec\)
This warning is for project developers\. Use -Wno-author to suppress it\.$

View File

@@ -7,4 +7,6 @@
-- 0x7FFFFFFFFFFFFFFF \+ 1: -9223372036854775808
-- -0x7FFFFFFFFFFFFFFF - 2: 9223372036854775807
-- 0x7FFFFFFFFFFFFFFF \* 2: -2
-- -~0x7FFFFFFFFFFFFFFF: -9223372036854775808$
-- -~0x7FFFFFFFFFFFFFFF: -9223372036854775808
-- INCREMENT 9223372036854775807: -9223372036854775808
-- DECREMENT -9223372036854775808: 9223372036854775807$

View File

@@ -13,3 +13,12 @@ foreach(expr IN ITEMS
math(EXPR result "${expr}")
message(STATUS "${expr}: ${result}")
endforeach()
function(inc_dec command value)
set(new_value "${value}")
math("${command}" new_value)
message(STATUS "${command} ${value}: ${new_value}")
endfunction()
inc_dec(INCREMENT 9223372036854775807)
inc_dec(DECREMENT -9223372036854775808)

View File

@@ -10,6 +10,18 @@ run_cmake(MATH-DivideByZero)
run_cmake(MATH-ModByZero)
run_cmake(MATH-DivideMinByMinusOne)
run_cmake(MATH-ModMinByMinusOne)
run_cmake(MATH-INCREMENT-NotEnoughArgs)
run_cmake(MATH-INCREMENT-TooManyArgs)
run_cmake(MATH-INCREMENT-Undefined)
run_cmake(MATH-INCREMENT-Empty)
run_cmake(MATH-INCREMENT-Invalid)
run_cmake(MATH-INCREMENT-Extra)
run_cmake(MATH-DECREMENT-NotEnoughArgs)
run_cmake(MATH-DECREMENT-TooManyArgs)
run_cmake(MATH-DECREMENT-Undefined)
run_cmake(MATH-DECREMENT-Empty)
run_cmake(MATH-DECREMENT-Invalid)
run_cmake(MATH-DECREMENT-Extra)
if(CMake_TEST_MATH_OVERFLOW)
run_cmake_script(Overflow)