bug-make
[Top][All Lists]
Advanced

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

[PATCH 1/2 V2] [SV 51286] Support for lexically scoped variables


From: Jouke Witteveen
Subject: [PATCH 1/2 V2] [SV 51286] Support for lexically scoped variables
Date: Sun, 1 Nov 2020 22:48:53 +0100

* src/function.c: Introduce the 'let' built-in function
* tests/scripts/functions/let: Tests for the 'let' built-in function
---

This replaces:
> [PATCH 1/2] * src/function.c: Introduce the 'let' built-in function

and goes together with:
> [PATCH 2/2 V2] * doc/make.texi: Document the let function

On Fri, Oct 23, 2020 at 3:14 PM Paul Smith <psmith@gnu.org> wrote:
> Sorry for the delay.  I have been super-busy (you'd think working from
> home would give one _more_ time but it seems not).

No problem. I am not in a hurry, but would love to see this land in the
next release.

> The thing most critically missing is a set of regression tests we can
> use to ensure that the feature is working (including various corner
> cases you would like to ensure), so that we can be sure it _keeps_
> working.  Also I run these tests under valgrind and ASAN and similar to
> check for memory leaks and other errors.

The tests introduced here are mostly inspired by those of foreach.

Regards,
- Jouke

 src/function.c              | 53 +++++++++++++++++++++-
 tests/scripts/functions/let | 89 +++++++++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 tests/scripts/functions/let

diff --git a/src/function.c b/src/function.c
index 0917e0c..c4ff030 100644
--- a/src/function.c
+++ b/src/function.c
@@ -908,6 +908,55 @@ func_foreach (char *o, char **argv, const char *funcname 
UNUSED)
   return o;
 }
 
+static char *
+func_let (char *o, char **argv, const char *funcname UNUSED)
+{
+  /* expand only the first two.  */
+  char *varnames = expand_argument (argv[0], NULL);
+  char *list = expand_argument (argv[1], NULL);
+  const char *body = argv[2];
+
+  const char *vp;
+  const char *vp_next = varnames;
+  const char *list_iterator = list;
+  char *p;
+  size_t len;
+  size_t vlen;
+
+  push_new_variable_scope ();
+
+  /* loop through LIST for all but the last VARNAME */
+  vp = find_next_token (&vp_next, &vlen);
+  NEXT_TOKEN (vp_next);
+  while (*vp_next != '\0')
+    {
+      p = find_next_token (&list_iterator, &len);
+      if (*list_iterator != '\0')
+        {
+          ++list_iterator;
+          p[len] = '\0';
+        }
+      define_variable (vp, vlen, p ? p : "", o_automatic, 0);
+
+      vp = find_next_token (&vp_next, &vlen);
+      NEXT_TOKEN (vp_next);
+    }
+  /* set the last VARNAME to the remainder of LIST */
+  if (vp)
+    define_variable (vp, vlen, next_token (list_iterator), o_automatic, 0);
+
+  /* Expand the body in the context of the arguments, adding the result to
+     the variable buffer.  */
+
+  o = variable_expand_string (o, body, SIZE_MAX);
+
+  pop_variable_scope ();
+  free (varnames);
+  free (list);
+
+  return o + strlen (o);
+}
+
 struct a_word
 {
   struct a_word *next;
@@ -2337,7 +2386,8 @@ func_abspath (char *o, char **argv, const char *funcname 
UNUSED)
    comma-separated values are treated as arguments.
 
    EXPAND_ARGS means that all arguments should be expanded before invocation.
-   Functions that do namespace tricks (foreach) don't automatically expand.  */
+   Functions that do namespace tricks (foreach, let) don't automatically
+   expand.  */
 
 static char *func_call (char *o, char **argv, const char *funcname);
 
@@ -2373,6 +2423,7 @@ static struct function_table_entry function_table_init[] =
   FT_ENTRY ("words",         0,  1,  1,  func_words),
   FT_ENTRY ("origin",        0,  1,  1,  func_origin),
   FT_ENTRY ("foreach",       3,  3,  0,  func_foreach),
+  FT_ENTRY ("let",           3,  3,  0,  func_let),
   FT_ENTRY ("call",          1,  0,  1,  func_call),
   FT_ENTRY ("info",          0,  1,  1,  func_error),
   FT_ENTRY ("error",         0,  1,  1,  func_error),
diff --git a/tests/scripts/functions/let b/tests/scripts/functions/let
new file mode 100644
index 0000000..d3dbe1a
--- /dev/null
+++ b/tests/scripts/functions/let
@@ -0,0 +1,89 @@
+#                                                                    -*-perl-*-
+# $Id$
+
+$description = "Test the let function.";
+
+$details = "This is a test of the let function in gnu make.
+This function destructures a list of values and binds each
+value to a variable name in a list of variable names.
+Superfluous variable names are assigned the empty string and
+the remaining values are assigned to the last variable name.
+The binding holds for the duration of the evaluation of the
+given text and no longer.  The general form of the command
+is $(let \$vars,\$list,\$text).  Several types of let
+assignments are tested\n";
+
+
+# Allow empty variable names and empty value list.
+# We still expand the list and body.
+run_make_test('
+null =
+x = $(let $(null),$(info side-effect),abc)
+y = $(let y,,$ydef)
+
+all: ; @echo $x$y',
+              '', "side-effect\nabcdef\n");
+
+# The example macro from the manual.
+run_make_test('
+reverse = $(let first rest,$1,$(if $(rest),$(call reverse,$(rest)) )$(first))
+
+all: ; @echo $(call reverse, \
+                 moe   miny  meeny eeny \
+              )',
+              '', "eeny meeny miny moe\n");
+
+
+# Set an environment variable that we can test in the makefile.
+$ENV{FOOFOO} = 'foo foo';
+
+# Verify masking: expansion outside the scope of let is unaffected.
+run_make_test('
+auto_var = \
+  udef \
+  CC \
+  FOOFOO \
+  MAKE \
+  foo \
+  CFLAGS \
+  WHITE \
+  @ \
+  <
+av = $(foreach var, $(auto_var), $(origin $(var)) )
+foo = bletch null @ garf
+override WHITE := BLACK
+
+define mktarget
+target: foo := $(foo)
+target: ; @echo $(AR)_$(foo)_
+endef
+
+all: auto target
+auto: ; @echo $(let $(auto_var),,$(av)) $(av)
+$(let AR foo,bar foo ,$(eval $(value mktarget)))',
+              '-e WHITE=WHITE CFLAGS=',
+              "automatic automatic automatic automatic automatic automatic 
automatic automatic automatic undefined default environment default file 
command line override automatic automatic
+ar_foo _
+");
+
+
+# Check some error conditions.
+run_make_test('
+x = $(let )
+y = $x
+
+all: ; @echo $y',
+              '',
+              "#MAKEFILE#:2: *** insufficient number of arguments (1) to 
function 'let'.  Stop.",
+              512);
+
+run_make_test('
+x = $(let x,y)
+y := $x
+
+all: ; @echo $y',
+              '',
+              "#MAKEFILE#:2: *** insufficient number of arguments (2) to 
function 'let'.  Stop.",
+              512);
+
+1;
-- 
2.29.2




reply via email to

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