Hi, I would like make to have scoped variables. Here, I will propose an implementation of them. This implementation is currently without tests and documentation. Hopefully, the proposal is acceptable and I can add the tests and documentation.
Consider a situation in which we have macros F and G, and some variable X, and our makefile includes: $(call F,$(call G,$(X)),$(call G,$(X))) Here, we duplicate the call to G. To make that more transparent (and assuming G does not introduce side-effects), we could write the above as: Y := $(call G,$(X)) $(call F,$(Y),$(Y)) undefine Y However, this would interfere with any existing variable Y. Alternatively, we could try: $(foreach Y,$(call G,$(X)), \ $(call F,$(Y),$(Y))) but that would not work if $(call G,$(X)) yields a list. A solution would be a new function, 'let', which allows us to write $(let Y,$(call G,$(X)), \ $(call F,$(Y),$(Y))) This function can be implemented easily. It can even be given superpowers. If the first argument to the new let-function is a single name, it is assigned the full second argument. If it is multiple names, say n, we can assign the first n-1 names to the first n-1 words of the second argument and the final name to the remainder of the arguments (adding empty words as necessary). This also solves http://savannah.gnu.org/bugs/?51286 and makes something like reverse = $(let first rest,$1,$(if $(rest),$(call reverse,$(rest)) )$(first)) possible. I have included an implementation bbelow, borrowing from the implementation of foreach and call. Let me know if I can go forward with this idea and prepare a patch including tests and documentation. Regards, - Jouke --- src/function.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/function.c b/src/function.c index 4ebff16..1c1c38b 100644 --- a/src/function.c +++ b/src/function.c @@ -908,6 +908,53 @@ 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 *list_iterator = list; + char *p; + size_t len; + size_t vlen; + const char *vp_next = varnames; + const char *vp = find_next_token (&vp_next, &vlen); + + push_new_variable_scope (); + + /* loop through LIST for all but the last VARNAME */ + 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); + } + 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 +2384,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 +2421,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), -- 2.24.1