This is an implementation of a $(compare) function as proposed by Edward Welbourne. It differs from his original proposal in that it behaves differently when given 4 or 5 arguments. In short, it's signature is
$(compare lhs,rhs,less-than[,greater-or-equal]) $(compare lhs,rhs,less-than,equal,greater-than) Documentation and a proper commit message is still missing, but this proof-of-concept shows that it is in fact pretty simple to implement. Additionally, I feel that the interface is clean. In that way, it differs from the various proposals for integer operations. After thinking about them some more, I came to dislike all current proposals because of the unintuitive behavior of subtraction and division. We should only support integer mathematics, so division is always going to be integer division, which suggests that we need a modulus operator as well. Moreover, 1/$x will not be supported, so we can't implement the same behavior for $(math -,$x) as for $(math /,$x). While verbose, the only clean way I can think of is simply $(add $x,$y), $(subtract $x,$y), etc. All supporting precisely two arguments. Multi argument versions can be defined like sum = $(let first rest,$1, \ $(if $(rest),$(add $(first),$(call sum,$(rest))), \ $(first))) I'd like to repeat that on POSIX systems, mathematical expressions can be evaluated using expr = $(shell expr '$1') Indeed, not all environments are POSIX, but this does show that $(compare) on its own (without any mathematical operators in make) can be very useful. --- src/function.c | 47 +++++++++++++++++++++++++++ tests/scripts/functions/compare | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 tests/scripts/functions/compare diff --git a/src/function.c b/src/function.c index ccad300..aa23349 100644 --- a/src/function.c +++ b/src/function.c @@ -1220,6 +1220,52 @@ func_sort (char *o, char **argv, const char *funcname UNUSED) return o; } +/* + $(compare lhs,rhs,lt-part[,eq-part[,gt-part]]) + + LT-PART is evaluated when LHS is strictly less than RHS. Otherwise, if + GT-PART is not provided, EQ-PART is evaluated (if it exists). In case + both EQ-PART and GT-PART are provided, EQ-PART is evaluated when LHS equals + RHS, and GT-PART is evaluated when LHS is strictly greater than RHS. + + Only one of the PARTs is evaluated, so $(compare ...) can be used to create + side-effects (with $(shell ...), for example). +*/ + +static char * +func_compare (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 'compare' function")); + rhs = parse_numeric (rhs_str, + _("non-numeric second argument to 'compare' function")); + free (lhs_str); + free (rhs_str); + + argv += 2; + 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]) @@ -2378,6 +2424,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 ("compare", 3, 5, 0, func_compare), 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/compare b/tests/scripts/functions/compare new file mode 100644 index 0000000..574397f --- /dev/null +++ b/tests/scripts/functions/compare @@ -0,0 +1,57 @@ +# -*-perl-*- +$description = "Test the compare function.\n"; + +$details = "Try various uses of compare and ensure they all give the correct +results.\n"; + +open(MAKEFILE, "> $makefile"); + +print MAKEFILE <<'EOF'; +# Negative +n = -10 +# Zero +z = 0 +# Positive +p = 1000000000 + +all: + @echo 1_1 $(compare $n,$n,$(shell echo lt)) + @echo 1_2 $(compare $n,$z,$(shell echo lt)) + @echo 1_3 $(compare $z,$n,$(shell echo lt)) + @echo 2_1 $(compare $n,$p,lt,ge) + @echo 2_2 $(compare $z,$z,lt,ge) + @echo 2_3 $(compare $p,$n,lt,ge) + @echo 3_0 $(compare $p,$n,lt,eq,) + @echo 3_1 $(compare $z,$p,lt,eq,gt) + @echo 3_2 $(compare $p,$z,lt,eq,gt) + @echo 3_3 $(compare $p,$p,lt,eq,gt) +EOF +close(MAKEFILE); + +&run_make_with_options($makefile, "", &get_logfile); +$answer = "1_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"; +&compare_output($answer, &get_logfile(1)); + + +# Test error conditions + +run_make_test(' +compare-e1: ; @echo $(compare 12a,1,foo) +compare-e2: ; @echo $(compare 0,,foo) +compare-e3: ; @echo $(compare -1,9999999999999999999,foo)', + 'compare-e1', + "#MAKEFILE#:2: *** non-numeric first argument to 'compare' function: '12a'. Stop.", + 512); + +run_make_test(undef, + 'compare-e2', + "#MAKEFILE#:3: *** non-numeric second argument to 'compare' function: ''. Stop.", + 512); + +run_make_test(undef, + 'compare-e3', + "#MAKEFILE#:4: *** Numerical result out of range: '9999999999999999999'. Stop.", + 512); + + +1; -- 2.27.0