Introduce $(intcmp ...) for numerical comparison

Numbers can come from $(words ...), automatic variables such as
$(MAKELEVEL), from environment variables, or from shell output such as
through $(shell expr ...).  The $(intcmp ...) function allows
conditional evaluation controlled by numerical variables.

* NEWS: Announce this feature.
* doc/make.texi (Functions for Conditionals): Document 'intcmp'.
* src/function.c (func_intcmp): Create the 'intcmp' built-in function.
* tests/scripts/functions/intcmp: Test the 'intcmp' built-in function.
This commit is contained in:
Jouke Witteveen 2021-07-16 14:04:41 +02:00 committed by Paul Smith
parent d9291d09b8
commit 71eb0a8038
4 changed files with 156 additions and 1 deletions

4
NEWS
View File

@ -50,6 +50,10 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se
user-defined function and they will not impact global variable assignments. user-defined function and they will not impact global variable assignments.
Implementation provided by Jouke Witteveen <j.witteveen@gmail.com> Implementation provided by Jouke Witteveen <j.witteveen@gmail.com>
* New feature: The $(intcmp ...) function
This function allows conditional evaluation controlled by a numerical
comparison.
* If the MAKEFLAGS variable is modified in a makefile, it will be re-parsed * If the MAKEFLAGS variable is modified in a makefile, it will be re-parsed
immediately rather than after all makefiles have been read. Note that immediately rather than after all makefiles have been read. Note that
although all options are parsed immediately, some special effects won't although all options are parsed immediately, some special effects won't

View File

@ -7820,7 +7820,7 @@ the file names to refer to an existing file or directory. Use the
@section Functions for Conditionals @section Functions for Conditionals
@findex if @findex if
@cindex conditional expansion @cindex conditional expansion
There are three functions that provide conditional expansion. A key There are four functions that provide conditional expansion. A key
aspect of these functions is that not all of the arguments are aspect of these functions is that not all of the arguments are
expanded initially. Only those arguments which need to be expanded, expanded initially. Only those arguments which need to be expanded,
will be expanded. will be expanded.
@ -7867,6 +7867,35 @@ empty string the processing stops and the result of the expansion is
the empty string. If all arguments expand to a non-empty string then the empty string. If all arguments expand to a non-empty string then
the result of the expansion is the expansion of the last argument. the result of the expansion is the expansion of the last argument.
@item $(intcmp @var{lhs},@var{rhs}[,@var{lt-part}[,@var{eq-part}[,@var{gt-part}]]])
@findex intcmp
The @code{intcmp} function provides support for numerical comparison of
integers. This function has no counterpart among the GNU @code{make} makefile
conditionals.
The left-hand side, @var{lhs}, and right-hand side, @var{rhs}, are expanded
and parsed as integral numbers in base 10. Expansion of the remaining
arguments is controlled by how the numerical left-hand side compares to the
numerical right-hand side.
If there are no further arguments, then the function expands to empty if the
left-hand side and right-hand side do not compare equal, or to their numerical
value if they do compare equal.
Else if the left-hand side is strictly less than the right-hand side, the
@code{intcmp} function evaluates to the expansion of the third argument,
@var{lt-part}. If both sides compare equal, then the @code{intcmp} function
evaluates to the expansion of the fourth argument, @var{eq-part}. If the
left-hand side is strictly greater than the right-hand side, then the
@code{intcmp} function evaluates to the expansion of the fifth argument,
@var{gt-part}.
If @var{gt-part} is missing, it defaults to @var{eq-part}. If @var{eq-part}
is missing, it defaults to the empty string. Thus both @samp{$(intcmp
9,7,hello)} and @samp{$(intcmp 9,7,hello,world,)} evaluate to the empty
string, while @samp{$(intcmp 9,7,hello,world)} (notice the absence of a comma
after @code{world}) evaluates to @samp{world}.
@end table @end table
@node Let Function, Foreach Function, Conditional Functions, Functions @node Let Function, Foreach Function, Conditional Functions, Functions
@ -12707,6 +12736,13 @@ all expansions result in a non-empty string, substitute the expansion
of the last @var{condition}.@* of the last @var{condition}.@*
@xref{Conditional Functions, ,Functions for Conditionals}. @xref{Conditional Functions, ,Functions for Conditionals}.
@item $(intcmp @var{lhs},@var{rhs}[,@var{lt-part}[,@var{eq-part}[,@var{gt-part}]]])
Compare @var{lhs} and @var{rhs} numerically; substitute the expansion of
@var{lt-part}, @var{eq-part}, or @var{gt-part} depending on whether the
left-hand side is less-than, equal-to, or greater-than the right-hand
side, respectively.@*
@xref{Conditional Functions, ,Functions for Conditionals}.
@item $(call @var{var},@var{param},@dots{}) @item $(call @var{var},@var{param},@dots{})
Evaluate the variable @var{var} replacing any references to @code{$(1)}, Evaluate the variable @var{var} replacing any references to @code{$(1)},
@code{$(2)} with the first, second, etc.@: @var{param} values.@* @code{$(2)} with the first, second, etc.@: @var{param} values.@*

View File

@ -1269,6 +1269,66 @@ func_sort (char *o, char **argv, const char *funcname UNUSED)
return o; return o;
} }
/*
$(intcmp lhs,rhs[,lt-part[,eq-part[,gt-part]]])
LHS and RHS must be integer values (leading/trailing whitespace is ignored).
If none of LT-PART, EQ-PART, or GT-PART are given then the function expands
to empty if LHS and RHS are not equal, or the numeric value if they are equal.
LT-PART is evaluated when LHS is strictly less than RHS, EQ-PART is evaluated
when LHS is equal to RHS, and GT-part is evaluated when LHS is strictly
greater than RHS.
If GT-PART is not provided, it defaults to EQ-PART. When neither EQ-PART
nor GT-PART are provided, the function expands to empty if LHS is not
strictly less than RHS.
*/
static char *
func_intcmp (char *o, char **argv, const char *funcname UNUSED)
{
char *lhs_str = expand_argument (argv[0], NULL);
char *rhs_str = expand_argument (argv[1], NULL);
long lhs, rhs;
lhs = parse_numeric (lhs_str,
_("non-numeric first argument to 'intcmp' function"));
rhs = parse_numeric (rhs_str,
_("non-numeric second argument to 'intcmp' function"));
free (lhs_str);
free (rhs_str);
argv += 2;
if (*argv == NULL)
{
if (lhs == rhs)
{
char buf[INTSTR_LENGTH+1];
sprintf (buf, "%ld", lhs);
o = variable_buffer_output(o, buf, strlen (buf));
}
return o;
}
if (lhs >= rhs)
{
++argv;
if (lhs > rhs && *argv && *(argv + 1))
++argv;
}
if (*argv)
{
char *expansion = expand_argument (*argv, NULL);
o = variable_buffer_output (o, expansion, strlen (expansion));
free (expansion);
}
return o;
}
/* /*
$(if condition,true-part[,false-part]) $(if condition,true-part[,false-part])
@ -2435,6 +2495,7 @@ static struct function_table_entry function_table_init[] =
FT_ENTRY ("info", 0, 1, 1, func_error), FT_ENTRY ("info", 0, 1, 1, func_error),
FT_ENTRY ("error", 0, 1, 1, func_error), FT_ENTRY ("error", 0, 1, 1, func_error),
FT_ENTRY ("warning", 0, 1, 1, func_error), FT_ENTRY ("warning", 0, 1, 1, func_error),
FT_ENTRY ("intcmp", 2, 5, 0, func_intcmp),
FT_ENTRY ("if", 2, 3, 0, func_if), FT_ENTRY ("if", 2, 3, 0, func_if),
FT_ENTRY ("or", 1, 0, 0, func_or), FT_ENTRY ("or", 1, 0, 0, func_or),
FT_ENTRY ("and", 1, 0, 0, func_and), FT_ENTRY ("and", 1, 0, 0, func_and),

View File

@ -0,0 +1,54 @@
# -*-perl-*-
$description = "Test the intcmp function.\n";
$details = "Try various uses of intcmp and ensure they all give the correct
results.\n";
run_make_test('# Negative
n = -10
# Zero
z = 0
# Positive
p = 1000000000
.RECIPEPREFIX = >
all:
> @echo 0_1 $(intcmp $n,$n)
> @echo 0_2 $(intcmp $n,$z)
> @echo 0_3 $(intcmp $z,$n)
> @echo 1_1 $(intcmp $n,$n,$(shell echo lt))
> @echo 1_2 $(intcmp $n,$z,$(shell echo lt))
> @echo 1_3 $(intcmp $z,$n,$(shell echo lt))
> @echo 2_1 $(intcmp $n,$p,lt,ge)
> @echo 2_2 $(intcmp $z,$z,lt,ge)
> @echo 2_3 $(intcmp $p,$n,lt,ge)
> @echo 3_0 $(intcmp $p,$n,lt,eq,)
> @echo 3_1 $(intcmp $z,$p,lt,eq,gt)
> @echo 3_2 $(intcmp $p,$z,lt,eq,gt)
> @echo 3_3 $(intcmp $p,$p,lt,eq,gt)
', '', "0_1 -10\n0_2\n0_3\n1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3 eq\n");
# Test error conditions
run_make_test('
intcmp-e1: ; @echo $(intcmp 12a,1,foo)
intcmp-e2: ; @echo $(intcmp 0,,foo)
intcmp-e3: ; @echo $(intcmp -1,9999999999999999999,foo)
intcmp-e4: ; @echo $(intcmp -1)
intcmp-e5: ; @echo $(intcmp ,55)',
'intcmp-e1',
"#MAKEFILE#:2: *** non-numeric first argument to 'intcmp' function: '12a'. Stop.",
512);
run_make_test(undef,
'intcmp-e2',
"#MAKEFILE#:3: *** non-numeric second argument to 'intcmp' function: ''. Stop.",
512);
run_make_test(undef,
'intcmp-e3',
"#MAKEFILE#:4: *** Numerical result out of range: '9999999999999999999'. Stop.",
512);
# This tells the test driver that the perl test script executed properly.
1;