]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Introduce $(intcmp ...) for numerical comparison
authorJouke Witteveen <j.witteveen@gmail.com>
Fri, 16 Jul 2021 12:04:41 +0000 (14:04 +0200)
committerPaul Smith <psmith@gnu.org>
Sun, 28 Nov 2021 19:27:10 +0000 (14:27 -0500)
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.

NEWS
doc/make.texi
src/function.c
tests/scripts/functions/intcmp [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index f0a9f75a7571fddf77c93b9b8aeb226d20a5901c..87f94666b5a6c06fddee8c3c8f00b58ae5248435 100644 (file)
--- 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 <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
   immediately rather than after all makefiles have been read.  Note that
   although all options are parsed immediately, some special effects won't
index f97f15ffc1cf36abcfbc77f43d4c6fd7e56af1df..3dea24f1806306c2197fa2d4673987e482a157f9 100644 (file)
@@ -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.@*
index b870cbffb795636fd77b672305b86f8b405aabdb..63fd238c149eb2258853c9979775bf3af661ccb6 100644 (file)
@@ -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 (file)
index 0000000..1abb205
--- /dev/null
@@ -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;