bug-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/3] Introduce $(compare ...) for numerical comparison


From: Jouke Witteveen
Subject: [PATCH 3/3] Introduce $(compare ...) for numerical comparison
Date: Fri, 16 Jul 2021 14:04:41 +0200

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 <j.witteveen@gmail.com>
 
+* 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 *funcname 
UNUSED)
   return o;
 }
 
+/*
+  $(compare lhs,rhs,lt-part[,eq-part[,gt-part]])
+
+  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, nothing is evaluated if LHS is not
+  strictly less than RHS.  Thus, $(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])
 
@@ -2438,6 +2483,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.32.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]