[PATCH 3/3] Introduce $(compare ...) 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 $(compare ...) function allows conditional evaluation controlled by numerical variables. * NEWS: Announce this feature. * doc/make.texi (Functions for Conditionals): Document 'compare'. * src/function.c (func_compare): Create the 'compare' built-in function. * tests/scripts/functions/compare: Test the 'compare' built-in function. --- This is a cleaned-up and documented version of a proposal submitted a year ago. The interface was conceived by Edward Welbourne and myself. Personally, I would not introduce mathematical operators to make, but given that numbers are a thing and they do pop up in variables, a comparison function makes sense to me. Thanks for considering! - Jouke NEWS| 4 +++ doc/make.texi | 36 - src/function.c | 46 ++ tests/scripts/functions/compare | 57 + 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tests/scripts/functions/compare diff --git a/NEWS b/NEWS index 5356260..6a3a744 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,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 $(compare ...) function + This function allows conditional evaluation controlled by a numerical + comparison. + * New debug option "print" will show the recipe to be run, even when silent mode is set. diff --git a/doc/make.texi b/doc/make.texi index 1ebf6ae..14a59f6 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -7654,7 +7654,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. @@ -7701,6 +7701,32 @@ 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 $(compare @var{lhs},@var{rhs},@var{lt-part}[,@var{eq-part}[,@var{gt-part}]]) +@findex compare +The @code{compare} 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 the left-hand side is strictly less than the right-hand side, the +entire @code{compare} function evaluates to the expansion of the third +argument, @var{lt-part}. If both sides compare equal, then the fourth +argument, @var{eq-part}, is expanded and becomes the result of the +@code{compare} function. If the left-hand side is strictly greater than +the right-hand side, then the @code{compare} function evaluates to the +expansion of the fifth argument, @var{gt-part}. + +In case of missing arguments, @var{gt-part} defaults to @var{eq-part}, +and @var{eq-part} defaults to the empty string. Thus both +@samp{$(compare 9,7,hello)} and @samp{$(compare 9,7,hello,world,)} +evaluate to the empty string, while @samp{$(compare 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 @@ -12523,6 +12549,14 @@ all expansions result in a non-empty string, substitute the expansion of the last @var{condition}.@* @xref{Conditional Functions, ,Functions for Conditionals}. +@item $(compare @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. If @var{gt-part} is left out, it defaults to +@var{eq-part}.@* +@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 964169a..587786a 100644 --- a/src/function.c +++ b/src/function.c @@ -1272,6 +1272,51 @@ func_sort (char *o, char **argv, const char *f
[PATCH 2/3] Use strtol instead of atoi
strtol is part of C89 and a fallback is provided by gnulib * src/function.c (func_word, func_wordlist): Use strtol instead of atoi * test/scripts/functions/word: Add out-of-range verification testing --- bootstrap.conf | 1 + src/function.c | 51 ++-- tests/scripts/functions/word | 12 ++--- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 7c4c9ab..c9c3668 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -50,4 +50,5 @@ findprog-in getloadavg host-cpu-c-abi strerror +strtol make-glob" diff --git a/src/function.c b/src/function.c index 396f129..964169a 100644 --- a/src/function.c +++ b/src/function.c @@ -766,19 +766,24 @@ strip_whitespace (const char **begpp, const char **endpp) return (char *)*begpp; } -static void -check_numeric (const char *s, const char *msg) +static long +parse_numeric (const char *s, const char *msg) { - const char *end = s + strlen (s) - 1; const char *beg = s; - strip_whitespace (&s, &end); - - for (; s <= end; ++s) -if (!ISDIGIT (*s)) /* ISDIGIT only evals its arg once: see makeint.h. */ - break; + const char *end = s + strlen (s) - 1; + char *endp; + long num; + strip_whitespace (&beg, &end); - if (s <= end || end - beg < 0) -OSS (fatal, *expanding_var, "%s: '%s'", msg, beg); + errno = 0; + num = strtol (beg, &endp, 10); + if (errno == ERANGE) +OSS (fatal, *expanding_var, "%s: '%s'", strerror (errno), s); + else if (endp == beg || endp <= end) +/* Empty or non-numeric input */ +OSS (fatal, *expanding_var, "%s: '%s'", msg, s); + + return num; } @@ -788,13 +793,11 @@ func_word (char *o, char **argv, const char *funcname UNUSED) { const char *end_p; const char *p; - int i; + long i; - /* Check the first argument. */ - check_numeric (argv[0], _("non-numeric first argument to 'word' function")); - i = atoi (argv[0]); - - if (i == 0) + i = parse_numeric (argv[0], + _("non-numeric first argument to 'word' function")); + if (i <= 0) O (fatal, *expanding_var, _("first argument to 'word' function must be greater than 0")); @@ -812,20 +815,18 @@ func_word (char *o, char **argv, const char *funcname UNUSED) static char * func_wordlist (char *o, char **argv, const char *funcname UNUSED) { - int start, count; + long start, stop, count; - /* Check the arguments. */ - check_numeric (argv[0], - _("non-numeric first argument to 'wordlist' function")); - check_numeric (argv[1], - _("non-numeric second argument to 'wordlist' function")); + start = parse_numeric (argv[0], + _("non-numeric first argument to 'wordlist' function")); + stop = parse_numeric (argv[1], +_("non-numeric second argument to 'wordlist' function")); - start = atoi (argv[0]); if (start < 1) ON (fatal, *expanding_var, -"invalid first argument to 'wordlist' function: '%d'", start); +"invalid first argument to 'wordlist' function: '%ld'", start); - count = atoi (argv[1]) - start + 1; + count = stop - start + 1; if (count > 0) { diff --git a/tests/scripts/functions/word b/tests/scripts/functions/word index 4dcc940..044bc94 100644 --- a/tests/scripts/functions/word +++ b/tests/scripts/functions/word @@ -51,6 +51,7 @@ run_make_test('FOO = foo bar biz baz word-e1: ; @echo $(word ,$(FOO)) word-e2: ; @echo $(word abc ,$(FOO)) word-e3: ; @echo $(word 1a,$(FOO)) +word-e4: ; @echo $(word 999,$(FOO)) wordlist-e1: ; @echo $(wordlist ,,$(FOO)) wordlist-e2: ; @echo $(wordlist abc ,,$(FOO)) @@ -69,19 +70,24 @@ run_make_test(undef, "#MAKEFILE#:5: *** non-numeric first argument to 'word' function: '1a'. Stop.", 512); +run_make_test(undef, + 'word-e4', + "#MAKEFILE#:6: *** Numerical result out of range: '999'. Stop.", + 512); + run_make_test(undef, 'wordlist-e1', - "#MAKEFILE#:7: *** non-numeric first argument to 'wordlist' function: ''. Stop.", + "#MAKEFILE#:8: *** non-numeric first argument to 'wordlist' function: ''. Stop.", 512); run_make_test(undef, 'wordlist-e2', - "#MAKEFILE#:8: *** non-numeric first argument to 'wordlist' function: 'abc '. Stop.", + "#MAKEFILE#:9: *** non-numeric first argument to 'wordlist' function: 'abc '. Stop.", 512); run_make_test(undef, 'wordlist-e3', - "#MAKEFILE#:9: *** non-numeric second argument to 'wordlist' function: ' 12a '. Stop.", + "#MAKEFILE#:10: *** non-numeric second argument to 'wordlist' function: ' 12a '. Stop.", 512); # Test error conditions again, but this time in a variable reference -- 2.32.0
[PATCH 1/3] * src/makeint.h: Removed unused atol declaration
--- src/makeint.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/makeint.h b/src/makeint.h index fcfb7bd..ca6d49d 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -631,7 +631,6 @@ void spin (const char* suffix); #if !defined (__GNU_LIBRARY__) && !defined (POSIX) && !defined (_POSIX_VERSION) && !defined(WINDOWS32) -long int atol (); # ifndef VMS long int lseek (); # endif -- 2.32.0