mirror of
https://github.com/mirror/make.git
synced 2025-01-27 21:00:22 +08:00
Create $(let ...) providing lexically scoped variables
Add a new function $(let ...) which allows lexically scoped variables. * NEWS: Add information on this feature. * doc/make.texi (Let Function): Document the 'let' function. * src/function.c (func_let): Create the 'let' built-in function. * tests/scripts/functions/let: Test the 'let' built-in function.
This commit is contained in:
parent
a8f4669b23
commit
fcc11d05a6
6
NEWS
6
NEWS
@ -34,6 +34,12 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se
|
||||
https://www.gnu.org/software/gnulib/manual/html_node/C99-features-assumed.html
|
||||
The configure script should verify the compiler has these features.
|
||||
|
||||
* New feature: The $(let ...) function
|
||||
This function allows user-defined functions to provide a lexically-scoped
|
||||
set of variables: values can be assigned to these variables from within the
|
||||
user-defined function and they will not impact global variable assignments.
|
||||
Implementation provided by Jouke Witteveen <j.witteveen@gmail.com>
|
||||
|
||||
* New debug option "print" will show the recipe to be run, even when silent
|
||||
mode is set.
|
||||
|
||||
|
102
doc/make.texi
102
doc/make.texi
@ -276,6 +276,7 @@ Functions for Transforming Text
|
||||
* Text Functions:: General-purpose text manipulation functions.
|
||||
* File Name Functions:: Functions for manipulating file names.
|
||||
* Conditional Functions:: Functions that implement conditions.
|
||||
* Let Function:: Lexically scoped variables.
|
||||
* Foreach Function:: Repeat some text with controlled variation.
|
||||
* File Function:: Write text to a file.
|
||||
* Call Function:: Expand a user-defined function.
|
||||
@ -7038,6 +7039,7 @@ be substituted.
|
||||
* Text Functions:: General-purpose text manipulation functions.
|
||||
* File Name Functions:: Functions for manipulating file names.
|
||||
* Conditional Functions:: Functions that implement conditions.
|
||||
* Let Function:: Lexically scoped variables.
|
||||
* Foreach Function:: Repeat some text with controlled variation.
|
||||
* File Function:: Write text to a file.
|
||||
* Call Function:: Expand a user-defined function.
|
||||
@ -7638,7 +7640,7 @@ the file names to refer to an existing file or directory. Use the
|
||||
@code{wildcard} function to test for existence.
|
||||
@end table
|
||||
|
||||
@node Conditional Functions, Foreach Function, File Name Functions, Functions
|
||||
@node Conditional Functions, Let Function, File Name Functions, Functions
|
||||
@section Functions for Conditionals
|
||||
@findex if
|
||||
@cindex conditional expansion
|
||||
@ -7691,14 +7693,84 @@ the result of the expansion is the expansion of the last argument.
|
||||
|
||||
@end table
|
||||
|
||||
@node Foreach Function, File Function, Conditional Functions, Functions
|
||||
@node Let Function, Foreach Function, Conditional Functions, Functions
|
||||
@section The @code{let} Function
|
||||
@findex let
|
||||
@cindex variables, lexically scoped
|
||||
|
||||
The @code{let} function provides a means to limit the scope of a
|
||||
variable. The assignment of the named variables in a @code{let}
|
||||
expression is in effect only within the text provided by the
|
||||
@code{let} expression, and this assignment doesn't impact that named
|
||||
variable in any outer scope.
|
||||
|
||||
Additionally, the @code{let} function enables list unpacking by
|
||||
assigning all unassigned values to the last named variable.
|
||||
|
||||
The syntax of the @code{let} function is:
|
||||
|
||||
@example
|
||||
$(let @var{var} [@var{var} ...],[@var{list}],@var{text})
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
The first two arguments, @var{var} and @var{list}, are expanded before
|
||||
anything else is done; note that the last argument, @var{text}, is
|
||||
@strong{not} expanded at the same time. Next, each word of the
|
||||
expanded value of @var{list} is bound to each of the variable names,
|
||||
@var{var}, in turn, with the final variable name being bound to the
|
||||
remainder of the expanded @var{list}. In other words, the first word
|
||||
of @var{list} is bound to the first variable @var{var}, the second
|
||||
word to the second variable @var{var}, and so on.
|
||||
|
||||
If there are more variable names in @var{var} than there are words in
|
||||
@var{list}, the remaining @var{var} variable names are set to the
|
||||
empty string. If there are fewer @var{var}s than words in @var{list}
|
||||
then the last @var{var} is set to all remaining words in @var{list}.
|
||||
|
||||
The variables in @var{var} are assigned as simply-expanded variables
|
||||
during the execution of @code{let}. @xref{Flavors, ,The Two Flavors
|
||||
of Variables}.@refill
|
||||
|
||||
After all variables are thus bound, @var{text} is expanded to provide
|
||||
the result of the @code{let} function.
|
||||
|
||||
For example, this macro reverses the order of the words in the list
|
||||
that it is given as its first argument:
|
||||
|
||||
@example
|
||||
reverse = $(let first rest,$1,$(if $(rest),$(call reverse,$(rest)) )$(first))
|
||||
|
||||
all: ; @@echo $(call reverse,d c b a)
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
will print @code{a b c d}. When first called, @code{let} will expand
|
||||
@var{$1} to @code{d c b a}. It will then assign @var{first} to
|
||||
@code{d} and assign @var{rest} to @code{c b a}. It will then expand
|
||||
the if-statement, where @code{$(rest)} is not empty so we recursively
|
||||
invoke the @var{reverse} function with the value of @var{rest} which
|
||||
is now @code{c b a}. The recursive invocation of @code{let} assigns
|
||||
@var{first} to @code{c} and @var{rest} to @code{b a}. The recursion
|
||||
continues until @code{let} is called with just a single value,
|
||||
@code{a}. Here @var{first} is @code{a} and @var{rest} is empty, so we
|
||||
do not recurse but simply expand @code{$(first)} to @code{a} and
|
||||
return, which adds @code{ b}, etc.
|
||||
|
||||
After the @var{reverse} call is complete, the @var{first} and
|
||||
@var{rest} variables are no longer set. If variables by those names
|
||||
existed beforehand, they are not affected by the expansion of the
|
||||
@code{reverse} macro.
|
||||
|
||||
@node Foreach Function, File Function, Let Function, Functions
|
||||
@section The @code{foreach} Function
|
||||
@findex foreach
|
||||
@cindex words, iterating over
|
||||
|
||||
The @code{foreach} function is very different from other functions. It
|
||||
causes one piece of text to be used repeatedly, each time with a different
|
||||
substitution performed on it. It resembles the @code{for} command in the
|
||||
The @code{foreach} function is similar to the @code{let} function, but very
|
||||
different from other functions. It causes one piece of text to be used
|
||||
repeatedly, each time with a different substitution performed on it. The
|
||||
@code{foreach} function resembles the @code{for} command in the
|
||||
shell @code{sh} and the @code{foreach} command in the C-shell @code{csh}.
|
||||
|
||||
The syntax of the @code{foreach} function is:
|
||||
@ -7757,13 +7829,14 @@ actual function call to be re-expanded under the control of @code{foreach};
|
||||
a simply-expanded variable would not do, since @code{wildcard} would be
|
||||
called only once at the time of defining @code{find_files}.
|
||||
|
||||
The @code{foreach} function has no permanent effect on the variable
|
||||
@var{var}; its value and flavor after the @code{foreach} function call are
|
||||
the same as they were beforehand. The other values which are taken from
|
||||
@var{list} are in effect only temporarily, during the execution of
|
||||
@code{foreach}. The variable @var{var} is a simply-expanded variable
|
||||
during the execution of @code{foreach}. If @var{var} was undefined
|
||||
before the @code{foreach} function call, it is undefined after the call.
|
||||
Like the @code{let} function, the @code{foreach} function has no permanent
|
||||
effect on the variable @var{var}; its value and flavor after the
|
||||
@code{foreach} function call are the same as they were beforehand. The
|
||||
other values which are taken from @var{list} are in effect only
|
||||
temporarily, during the execution of @code{foreach}. The variable
|
||||
@var{var} is a simply-expanded variable during the execution of
|
||||
@code{foreach}. If @var{var} was undefined before the @code{foreach}
|
||||
function call, it is undefined after the call.
|
||||
@xref{Flavors, ,The Two Flavors of Variables}.@refill
|
||||
|
||||
You must take care when using complex variable expressions that result in
|
||||
@ -12409,6 +12482,11 @@ Return a string describing the flavor of the @code{make} variable
|
||||
@var{variable}.@*
|
||||
@xref{Flavor Function, , The @code{flavor} Function}.
|
||||
|
||||
@item $(let @var{var} [@var{var} ...],@var{words},@var{text})
|
||||
Evaluate @var{text} with the @var{var}s bound to the words in
|
||||
@var{words}.@*
|
||||
@xref{Let Function, ,The @code{let} Function}.
|
||||
|
||||
@item $(foreach @var{var},@var{words},@var{text})
|
||||
Evaluate @var{text} with @var{var} bound to each word in @var{words},
|
||||
and concatenate the results.@*
|
||||
|
@ -908,6 +908,56 @@ 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 *chain;
|
||||
@ -2344,7 +2394,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);
|
||||
|
||||
@ -2380,6 +2431,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),
|
||||
|
110
tests/scripts/functions/let
Normal file
110
tests/scripts/functions/let
Normal file
@ -0,0 +1,110 @@
|
||||
# -*-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";
|
||||
|
||||
# check for mismatched var and list word counts
|
||||
run_make_test(q!
|
||||
a = bad
|
||||
b = news
|
||||
x = $(let a b,1 2,$a $b)
|
||||
y = $(let a,1 2,$a)
|
||||
z = $(let a b,1,$a $b)
|
||||
all:;@echo 'a=,$a,' 'b=,$b,' 'x=,$x,' 'y=,$y,' 'z=,$z,'
|
||||
!,
|
||||
'', "a=,bad, b=,news, x=,1 2, y=,1 2, z=,1 ,\n");
|
||||
|
||||
# check for whitespace
|
||||
run_make_test(q!
|
||||
a = bad
|
||||
b = news
|
||||
x = $(let a b, 1 2 ,+$a+$b+)
|
||||
y = $(let a, 1 2 ,+$a+)
|
||||
z = $(let a b, 1 ,+$a+$b+)
|
||||
all:;@echo 'a=,$a,' 'b=,$b,' 'x=,$x,' 'y=,$y,' 'z=,$z,'
|
||||
!,
|
||||
'', "a=,bad, b=,news, x=,+1+2 +, y=,+1 2 +, z=,+1++,\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;
|
Loading…
Reference in New Issue
Block a user