From: Jouke Witteveen Date: Fri, 16 Jul 2021 12:04:41 +0000 (+0200) Subject: Introduce $(intcmp ...) for numerical comparison X-Git-Tag: 4.3.90~137 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=71eb0a80384617b553546228648f1f1926f43977;p=thirdparty%2Fmake.git 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. --- diff --git a/NEWS b/NEWS index f0a9f75a..87f94666 100644 --- a/NEWS +++ b/NEWS @@ -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. Implementation provided by Jouke Witteveen +* 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 immediately rather than after all makefiles have been read. Note that although all options are parsed immediately, some special effects won't diff --git a/doc/make.texi b/doc/make.texi index f97f15ff..3dea24f1 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -7820,7 +7820,7 @@ the file names to refer to an existing file or directory. Use the @section Functions for Conditionals @findex if @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 expanded initially. Only those arguments which need to 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 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 @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}.@* @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{}) Evaluate the variable @var{var} replacing any references to @code{$(1)}, @code{$(2)} with the first, second, etc.@: @var{param} values.@* diff --git a/src/function.c b/src/function.c index b870cbff..63fd238c 100644 --- a/src/function.c +++ b/src/function.c @@ -1269,6 +1269,66 @@ func_sort (char *o, char **argv, const char *funcname UNUSED) 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]) @@ -2435,6 +2495,7 @@ static struct function_table_entry function_table_init[] = FT_ENTRY ("info", 0, 1, 1, func_error), FT_ENTRY ("error", 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 ("or", 1, 0, 0, func_or), FT_ENTRY ("and", 1, 0, 0, func_and), diff --git a/tests/scripts/functions/intcmp b/tests/scripts/functions/intcmp new file mode 100644 index 00000000..1abb205f --- /dev/null +++ b/tests/scripts/functions/intcmp @@ -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;