* Fix up and document $(apply ...) function.

This commit is contained in:
Paul Smith 1999-07-15 07:36:44 +00:00
parent adb1632033
commit 9d89ad56bf
9 changed files with 181 additions and 121 deletions

View file

@ -1,3 +1,12 @@
1999-07-15 Paul D. Smith <psmith@gnu.org>
* function.c (func_apply): Various code cleanup and tightening.
(function_table): Add "apply" as a valid builtin function.
* make.texinfo (Apply Function): Document it.
* NEWS: Announce it.
1999-07-09 Paul D. Smith <psmith@gnu.org> 1999-07-09 Paul D. Smith <psmith@gnu.org>
* job.c (start_waiting_job): Don't get a second job token if we * job.c (start_waiting_job): Don't get a second job token if we
@ -227,7 +236,12 @@
* function.c: Rewrite to use one C function per make function, * function.c: Rewrite to use one C function per make function,
instead of a huge switch statement. Also allows some cleanup of instead of a huge switch statement. Also allows some cleanup of
multi-architecture issues. multi-architecture issues, and a cleaner API which makes things
like func_apply() simple.
* function.c (func_apply): Initial implementation. Expand either
a builtin function or a make variable in the context of some
arguments, provided as $1, $2, ... $N.
1999-03-19 Eli Zaretskii <eliz@is.elta.co.il> 1999-03-19 Eli Zaretskii <eliz@is.elta.co.il>
1999-03-19 Rob Tulloh <rob_tulloh@dev.tivoli.com> 1999-03-19 Rob Tulloh <rob_tulloh@dev.tivoli.com>

7
NEWS
View file

@ -18,6 +18,11 @@ Version 3.78
causes the text provided to be printed as a warning message, but make causes the text provided to be printed as a warning message, but make
proceeds normally. proceeds normally.
* A new function, $(apply ...) is provided. This allows users to create
their own parameterized macros and invoke them later. This
implementation of this feature was provided by Han-Wen Nienhuys
<hanwen@cs.uu.nl>.
* Make defines a new variable, .LIBPATTERNS. This variable controls how * Make defines a new variable, .LIBPATTERNS. This variable controls how
library dependency expansion (dependencies like ``-lfoo'') is performed. library dependency expansion (dependencies like ``-lfoo'') is performed.
@ -29,7 +34,7 @@ Version 3.78
LD, AR, etc.). Specifying this option forces -r (--no-builtin-rules) LD, AR, etc.). Specifying this option forces -r (--no-builtin-rules)
as well. as well.
* A "job server" feature, proposed by Howard Chu <hyc@highlandsun.com>. * A "job server" feature, suggested by Howard Chu <hyc@highlandsun.com>.
On systems that support POSIX pipe(2) semantics, GNU make can now pass On systems that support POSIX pipe(2) semantics, GNU make can now pass
-jN options to submakes rather than forcing them all to use -j1. The -jN options to submakes rather than forcing them all to use -j1. The

3
dep.h
View file

@ -38,8 +38,7 @@ struct dep
struct dep *next; struct dep *next;
char *name; char *name;
struct file *file; struct file *file;
unsigned short changed; int changed;
unsigned short deferred; /* Only used in update_goal_chain(). */
}; };

3
file.c
View file

@ -348,6 +348,8 @@ in favor of those for `%s'.",
/* %%% Kludge so -W wins on a file that gets vpathized. */ /* %%% Kludge so -W wins on a file that gets vpathized. */
oldfile->last_mtime = file->last_mtime; oldfile->last_mtime = file->last_mtime;
oldfile->mtime_before_update = file->mtime_before_update;
#define MERGE(field) oldfile->field |= file->field #define MERGE(field) oldfile->field |= file->field
MERGE (precious); MERGE (precious);
MERGE (tried_implicit); MERGE (tried_implicit);
@ -468,6 +470,7 @@ snap_deps ()
/* Mark this file as phony and nonexistent. */ /* Mark this file as phony and nonexistent. */
f2->phony = 1; f2->phony = 1;
f2->last_mtime = (FILE_TIMESTAMP) -1; f2->last_mtime = (FILE_TIMESTAMP) -1;
f2->mtime_before_update = (FILE_TIMESTAMP) -1;
} }
for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev) for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev)

View file

@ -35,6 +35,8 @@ struct file
rule has been used */ rule has been used */
struct dep *also_make; /* Targets that are made by making this. */ struct dep *also_make; /* Targets that are made by making this. */
FILE_TIMESTAMP last_mtime; /* File's modtime, if already known. */ FILE_TIMESTAMP last_mtime; /* File's modtime, if already known. */
FILE_TIMESTAMP mtime_before_update; /* File's modtime before any updating
has been performed. */
struct file *prev; /* Previous entry for same file name; struct file *prev; /* Previous entry for same file name;
used when there are multiple double-colon used when there are multiple double-colon
entries for the same file. */ entries for the same file. */

View file

@ -1719,130 +1719,83 @@ func_if (char* o, char **argv, char *funcname)
} }
#endif #endif
/* This might not be very useful, but the code was simple to /* User-defined functions. Expand the first argument as either a builtin
implement, I just had to do it. function or a make variable, in the context of the rest of the arguments
assigned to $1, $2, ... $N. $0 is the name of the function. */
Here goes anyway
Apply & User defined functions.
SYNTAX
$(apply funcname, arg1, arg2, .. )
SEMANTICS
You can specify a builtin function, for funcname, eg
f = addprefix
$(apply addprefix,a, b c d)
This will result in
ab ac ad
You can specify your own functions, eg
funcname=BODY
BODY contains $(1) .. $(N) as argument markers.
upon expansions the strings ARG1 .. ARGN are substituted for $(1) .. $(N)
into BODY
Because the funcname is computed as well you can combine this do some
funky things, eg
map=$(foreach a, $(2), $(apply $(1), $(a)))
LIMITATIONS.
Make has no support for nested lists (or tuples), so you can't do
stuff like (Haskell notation):
f :: (a,b) -> c -- type of F
map :: (a->b) -> [a] -> b -- type of MAP
map f [(1,2), (2,3)] -- map F over list containing (1,2) and (2,3)
to get
[f (1, 2), f (2, 3)]
If only we had nested lists and quotes, we could duplicate LISP in make by
transforming
$(a, b, c) <-> (a b c)
$(quote $(a, b, c)) <-> '(a b c)
(or something alike ;-) (We could have automatic integration of
GUILE and make :-)
[Actually -- why should this be a joke? If we could somehow integrate the
rules and targets into a functional model make could be a lot cleaner in
concept. ]
*/
char * char *
func_apply (o, argv, funcname) func_apply (o, argv, funcname)
char *o; char *o;
char **argv; char **argv;
const char *funcname; const char *funcname;
{ {
char *userfunc_name; char *fname;
int func_len; int flen;
char *body = 0; char *body;
char *expanded_body = 0;
int i; int i;
const struct function_table_entry *entry_p; const struct function_table_entry *entry_p;
userfunc_name = argv[0]; /* Applying nothing is a no-op. */
while (isspace (*userfunc_name)) if (*argv[0] == '\0')
++userfunc_name; return o;
entry_p = lookup_function (function_table, userfunc_name); /* There is no way to define a variable with a space in the name, so strip
trailing whitespace as a favor to the user. */
flen = strlen (argv[0]);
fname = argv[0] + flen - 1;
while (isspace (*fname))
--fname;
fname[1] = '\0';
flen = fname - argv[0] + 1;
fname = argv[0];
/* Are we invoking a builtin function? */
entry_p = lookup_function (function_table, fname);
/* builtin function? */
if (entry_p) if (entry_p)
{ {
for (i=0; argv[i+1]; i++) for (i=0; argv[i+1]; ++i)
; ;
o = expand_builtin_function (o, i, argv + 1, entry_p); return expand_builtin_function (o, i, argv + 1, entry_p);
return o;
} }
func_len = strlen (userfunc_name); /* No, so the first argument is the name of a variable to be expanded and
body = xmalloc (func_len + 4); interpreted as a function. Create the variable reference. */
strcpy (body + 2, userfunc_name); body = alloca (flen + 4);
body [func_len+2]=')'; body[0]='$';
body [func_len+3]= 0; body[1]='(';
body [1]='('; strcpy (body + 2, fname);
body [0]='$'; body[flen+2]=')';
body[flen+3]= '\0';
/* Set up arguments $(1) .. $(N). $(0) is the function name. */
push_new_variable_scope (); push_new_variable_scope ();
/* set up arguments $(1) .. $(N) */ for (i=0; *argv; ++i, ++argv)
for (i=0; argv[i]; i++)
{ {
char num[10]; char num[11];
struct variable* var;
sprintf (num, "%d", i); sprintf (num, "%d", i);
var = define_variable (num, strlen (num), argv[i], o_automatic, 0); define_variable (num, strlen (num), *argv, o_automatic, 0);
} }
expanded_body = allocated_variable_expand (body); /* Expand the body in the context of the arguments, adding the result to
o = variable_buffer_output (o, expanded_body, strlen (expanded_body)); the variable buffer. */
free (expanded_body);
o = variable_expand_string (o, body, flen+3);
pop_variable_scope (); pop_variable_scope ();
free (body); return o + strlen(o);
return o;
} }
#define STRING_SIZE_TUPLE(s) (s), (sizeof(s)-1) #define STRING_SIZE_TUPLE(_s) (_s), (sizeof(_s)-1)
/* Lookup table for builtin functions. /* Lookup table for builtin functions.
@ -1851,7 +1804,7 @@ func_apply (o, argv, funcname)
table. table.
REQUIRED_ARGUMENTS is the minimum number of arguments. A function REQUIRED_ARGUMENTS is the minimum number of arguments. A function
can have more, but they will be ignored. can have more, but if they have less an error will be generated.
EXPAND_ALL_ARGUMENTS means that all arguments should be expanded EXPAND_ALL_ARGUMENTS means that all arguments should be expanded
before invocation. Functions that do namespace tricks (foreach) before invocation. Functions that do namespace tricks (foreach)
@ -1881,11 +1834,11 @@ static struct function_table_entry function_table[] =
{ STRING_SIZE_TUPLE("wordlist"), 3, 1, func_wordlist}, { STRING_SIZE_TUPLE("wordlist"), 3, 1, func_wordlist},
{ STRING_SIZE_TUPLE("words"), 1, 1, func_words}, { STRING_SIZE_TUPLE("words"), 1, 1, func_words},
{ STRING_SIZE_TUPLE("origin"), 1, 1, func_origin}, { STRING_SIZE_TUPLE("origin"), 1, 1, func_origin},
{ STRING_SIZE_TUPLE("foreach"), 3, 0, func_foreach},
{ STRING_SIZE_TUPLE("apply"), 1, 1, func_apply},
{ STRING_SIZE_TUPLE("error"), 1, 1, func_error}, { STRING_SIZE_TUPLE("error"), 1, 1, func_error},
{ STRING_SIZE_TUPLE("warning"), 1, 1, func_error}, { STRING_SIZE_TUPLE("warning"), 1, 1, func_error},
{ STRING_SIZE_TUPLE("foreach"), 3, 0, func_foreach},
#ifdef EXPERIMENTAL #ifdef EXPERIMENTAL
{ STRING_SIZE_TUPLE("apply"), 1, 1, func_apply},
{ STRING_SIZE_TUPLE("eq"), 2, 1, func_eq}, { STRING_SIZE_TUPLE("eq"), 2, 1, func_eq},
{ STRING_SIZE_TUPLE("if"), 3, 0, func_if}, { STRING_SIZE_TUPLE("if"), 3, 0, func_if},
{ STRING_SIZE_TUPLE("not"), 1, 1, func_not}, { STRING_SIZE_TUPLE("not"), 1, 1, func_not},

4
main.c
View file

@ -1358,7 +1358,7 @@ int main (int argc, char ** argv)
for (p = old_files->list; *p != 0; ++p) for (p = old_files->list; *p != 0; ++p)
{ {
f = enter_command_line_file (*p); f = enter_command_line_file (*p);
f->last_mtime = (FILE_TIMESTAMP) 1; f->last_mtime = f->mtime_before_update = (FILE_TIMESTAMP) 1;
f->updated = 1; f->updated = 1;
f->update_status = 0; f->update_status = 0;
f->command_state = cs_finished; f->command_state = cs_finished;
@ -1369,7 +1369,7 @@ int main (int argc, char ** argv)
for (p = new_files->list; *p != 0; ++p) for (p = new_files->list; *p != 0; ++p)
{ {
f = enter_command_line_file (*p); f = enter_command_line_file (*p);
f->last_mtime = NEW_MTIME; f->last_mtime = f->mtime_before_update = NEW_MTIME;
} }
} }

View file

@ -269,6 +269,7 @@ Functions for Transforming Text
* Text Functions:: General-purpose text manipulation functions. * Text Functions:: General-purpose text manipulation functions.
* File Name Functions:: Functions for manipulating file names. * File Name Functions:: Functions for manipulating file names.
* Foreach Function:: Repeat some text with controlled variation. * Foreach Function:: Repeat some text with controlled variation.
* Apply Function:: Expand a user-defined function.
* Origin Function:: Find where a variable got its value. * Origin Function:: Find where a variable got its value.
* Shell Function:: Substitute the output of a shell command. * Shell Function:: Substitute the output of a shell command.
@ -5199,6 +5200,7 @@ call, just as a variable might be substituted.
* Text Functions:: General-purpose text manipulation functions. * Text Functions:: General-purpose text manipulation functions.
* File Name Functions:: Functions for manipulating file names. * File Name Functions:: Functions for manipulating file names.
* Foreach Function:: Repeat some text with controlled variation. * Foreach Function:: Repeat some text with controlled variation.
* Apply Function:: Expand a user-defined function.
* Origin Function:: Find where a variable got its value. * Origin Function:: Find where a variable got its value.
* Shell Function:: Substitute the output of a shell command. * Shell Function:: Substitute the output of a shell command.
* Make Control Functions:: Functions that control how make runs. * Make Control Functions:: Functions that control how make runs.
@ -5224,8 +5226,9 @@ or like this:
$@{@var{function} @var{arguments}@} $@{@var{function} @var{arguments}@}
@end example @end example
Here @var{function} is a function name; one of a short list of names that Here @var{function} is a function name; one of a short list of names
are part of @code{make}. There is no provision for defining new functions. that are part of @code{make}. You can also essentially create your own
functions by using the @code{apply} builtin function.
The @var{arguments} are the arguments of the function. They are The @var{arguments} are the arguments of the function. They are
separated from the function name by one or more spaces or tabs, and if separated from the function name by one or more spaces or tabs, and if
@ -5746,7 +5749,7 @@ that match the pattern.
@xref{Wildcards, ,Using Wildcard Characters in File Names}. @xref{Wildcards, ,Using Wildcard Characters in File Names}.
@end table @end table
@node Foreach Function, Origin Function, File Name Functions, Functions @node Foreach Function, Apply Function, File Name Functions, Functions
@section The @code{foreach} Function @section The @code{foreach} Function
@findex foreach @findex foreach
@cindex words, iterating over @cindex words, iterating over
@ -5834,7 +5837,96 @@ might be useful if the value of @code{find_files} references the variable
whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo, whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo,
no?), but it is more likely to be a mistake. no?), but it is more likely to be a mistake.
@node Origin Function, Shell Function, Foreach Function, Functions @node Apply Function, Origin Function, Foreach Function, Functions
@section The @code{apply} Function
@findex apply
@cindex functions, user defined
@cindex user defined functions
The @code{apply} function is unique in that it can be used to create new
parameterized functions. You can write a complex expression as the
value of a variable, then use @code{apply} to expand it with different
values.
The syntax of the @code{apply} function is:
@example
$(apply @var{variable}, @var{param}, @var{param}, @dots{})
@end example
When @code{make} expands this function, it assigns each @var{param} to
temporary variables @var{$(1)}, @var{$(2)}, etc. The variable
@var{$(0)} will contain @var{variable}. There is no maximum number of
parameter arguments. There is no minimum, either, but it doesn't make
sense to use @code{apply} with no parameters.
Then @var{variable} is expanded as a @code{make} variable in the context
of these temporary assignments. Thus, any reference to @var{$(1)} in
the value of @var{variable} will resolve to the first @var{param} in the
invocation of @code{apply}.
Note that @var{variable} is the @emph{name} of a variable, not a
@emph{reference} to that variable. Therefore you would not normally use
a @samp{$} or parentheses when writing it. (You can, however, use a
variable reference in the name if you want the name not to be a
constant.)
If @var{variable} is the name of a builtin function, the builtin function
is always invoked (even if a @code{make} variable by that name also
exists).
Some examples may make this clearer.
This macro simply reverses its arguments:
@smallexample
reverse = $2 $1
foo = a b
bar = $(apply reverse,$(foo))
@end smallexample
@noindent
Here @var{bar} will contain @samp{b a}.
This one is slightly more interesting: it defines a macro to search for
the first instance of a program in @code{PATH}:
@smallexample
pathsearch = $(firstword $(wildcard $(addsufix /$1,$(subst :, ,$(PATH)))))
LS := $(apply pathsearch,ls)
@end smallexample
@noindent
Now the variable LS contains @code{/bin/ls} or similar.
The @code{apply} function can be nested. Each recursive invocation gets
its own local values for @var{$(1)}, etc. that mask the values of
higher-level @code{apply}. For example, here is an implementation of a
@dfn{map} function:
@smallexample
map = $(foreach a,$2,$(apply $1,$a))
@end smallexample
Now you can @var{map} a function that normally takes only one argument,
such as @code{origin}, to multiple values in one step:
@smallexample
o = $(apply map,origin,o map MAKE)
@end smallexample
and end up with @var{o} containing something like @samp{file file default}.
A final caution: be careful when adding whitespace to the arguments to
@code{apply}. As with other functions, any whitespace contained in the
second and subsequent arguments is kept; this can cause strange
effects. It's generally safest to remove all extraneous whitespace when
defining variables for use with @code{apply}.
@node Origin Function, Shell Function, Apply Function, Functions
@section The @code{origin} Function @section The @code{origin} Function
@findex origin @findex origin
@cindex variables, origin of @cindex variables, origin of

View file

@ -94,7 +94,7 @@ update_goal_chain (goals, makefiles)
struct dep *g; struct dep *g;
for (g = goals; g != 0; g = g->next) for (g = goals; g != 0; g = g->next)
g->changed = g->deferred = 0; g->changed = 0;
} }
#if 0 #if 0
@ -134,7 +134,6 @@ update_goal_chain (goals, makefiles)
{ {
unsigned int ocommands_started; unsigned int ocommands_started;
int x; int x;
FILE_TIMESTAMP mtime = MTIME (file);
check_renamed (file); check_renamed (file);
if (makefiles) if (makefiles)
{ {
@ -161,16 +160,6 @@ update_goal_chain (goals, makefiles)
decide when to give an "up to date" diagnostic. */ decide when to give an "up to date" diagnostic. */
g->changed += commands_started - ocommands_started; g->changed += commands_started - ocommands_started;
/* Set the goal's `deferred' flag if we started a command but
it didn't finish (parallel builds). We need to remember
this, because the next time through the goal chain the call
to reap_children() will set the mtime, not the call to
update_file() above. So, the saved mtime from before
update_file() will be the same as the mtime after it, and
we'll think nothing changed when it did (see below). */
if (file->command_state == cs_running)
g->deferred = 1;
stop = 0; stop = 0;
if (x != 0 || file->updated) if (x != 0 || file->updated)
{ {
@ -191,7 +180,8 @@ update_goal_chain (goals, makefiles)
stop = (!keep_going_flag && !question_flag stop = (!keep_going_flag && !question_flag
&& !makefiles); && !makefiles);
} }
else if (MTIME (file) != mtime || g->deferred) else if (file->updated && g->changed &&
file->last_mtime != file->mtime_before_update)
{ {
/* Updating was done. If this is a makefile and /* Updating was done. If this is a makefile and
just_print_flag or question_flag is set just_print_flag or question_flag is set
@ -199,7 +189,6 @@ update_goal_chain (goals, makefiles)
specified as a command-line target), don't specified as a command-line target), don't
change STATUS. If STATUS is changed, we will change STATUS. If STATUS is changed, we will
get re-exec'd, and fall into an infinite loop. */ get re-exec'd, and fall into an infinite loop. */
g->deferred = 0;
if (!makefiles if (!makefiles
|| (!just_print_flag && !question_flag)) || (!just_print_flag && !question_flag))
status = 0; status = 0;
@ -736,6 +725,9 @@ notice_finished_file (file)
{ {
struct file *f; struct file *f;
assert(file->mtime_before_update == 0);
file->mtime_before_update = file->last_mtime;
if (just_print_flag || question_flag if (just_print_flag || question_flag
|| (file->is_target && file->cmds == 0)) || (file->is_target && file->cmds == 0))
file->last_mtime = NEW_MTIME; file->last_mtime = NEW_MTIME;