From 9d89ad56bf74b22471617ed99f9e6e6272efba22 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 15 Jul 1999 07:36:44 +0000 Subject: [PATCH] * Fix up and document $(apply ...) function. --- ChangeLog | 16 +++++- NEWS | 7 ++- dep.h | 3 +- file.c | 3 ++ filedef.h | 2 + function.c | 147 ++++++++++++++++++--------------------------------- main.c | 4 +- make.texinfo | 100 +++++++++++++++++++++++++++++++++-- remake.c | 20 +++---- 9 files changed, 181 insertions(+), 121 deletions(-) diff --git a/ChangeLog b/ChangeLog index b88e0349..42ec9fe2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +1999-07-15 Paul D. Smith + + * 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 * 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, 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 1999-03-19 Rob Tulloh diff --git a/NEWS b/NEWS index 53e1c33c..4775cb4c 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,11 @@ Version 3.78 causes the text provided to be printed as a warning message, but make 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 + . + * Make defines a new variable, .LIBPATTERNS. This variable controls how 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) as well. -* A "job server" feature, proposed by Howard Chu . +* A "job server" feature, suggested by Howard Chu . 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 diff --git a/dep.h b/dep.h index 90999b6c..ca8112fe 100644 --- a/dep.h +++ b/dep.h @@ -38,8 +38,7 @@ struct dep struct dep *next; char *name; struct file *file; - unsigned short changed; - unsigned short deferred; /* Only used in update_goal_chain(). */ + int changed; }; diff --git a/file.c b/file.c index 1321b551..2a9a7912 100644 --- a/file.c +++ b/file.c @@ -348,6 +348,8 @@ in favor of those for `%s'.", /* %%% Kludge so -W wins on a file that gets vpathized. */ oldfile->last_mtime = file->last_mtime; + oldfile->mtime_before_update = file->mtime_before_update; + #define MERGE(field) oldfile->field |= file->field MERGE (precious); MERGE (tried_implicit); @@ -468,6 +470,7 @@ snap_deps () /* Mark this file as phony and nonexistent. */ f2->phony = 1; f2->last_mtime = (FILE_TIMESTAMP) -1; + f2->mtime_before_update = (FILE_TIMESTAMP) -1; } for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev) diff --git a/filedef.h b/filedef.h index 5d45d645..c96c2e35 100644 --- a/filedef.h +++ b/filedef.h @@ -35,6 +35,8 @@ struct file rule has been used */ struct dep *also_make; /* Targets that are made by making this. */ 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; used when there are multiple double-colon entries for the same file. */ diff --git a/function.c b/function.c index da13dd7e..982d128e 100644 --- a/function.c +++ b/function.c @@ -1719,130 +1719,83 @@ func_if (char* o, char **argv, char *funcname) } #endif -/* This might not be very useful, but the code was simple to - implement, I just had to do it. +/* User-defined functions. Expand the first argument as either a builtin + 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 * func_apply (o, argv, funcname) char *o; char **argv; const char *funcname; { - char *userfunc_name; - int func_len; - char *body = 0; - char *expanded_body = 0; + char *fname; + int flen; + char *body; int i; const struct function_table_entry *entry_p; - userfunc_name = argv[0]; - while (isspace (*userfunc_name)) - ++userfunc_name; + /* Applying nothing is a no-op. */ + if (*argv[0] == '\0') + 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) { - 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 o; + return expand_builtin_function (o, i, argv + 1, entry_p); } - func_len = strlen (userfunc_name); - body = xmalloc (func_len + 4); - strcpy (body + 2, userfunc_name); - body [func_len+2]=')'; - body [func_len+3]= 0; - body [1]='('; - body [0]='$'; + /* No, so the first argument is the name of a variable to be expanded and + interpreted as a function. Create the variable reference. */ + body = alloca (flen + 4); + body[0]='$'; + body[1]='('; + strcpy (body + 2, fname); + body[flen+2]=')'; + body[flen+3]= '\0'; + + /* Set up arguments $(1) .. $(N). $(0) is the function name. */ push_new_variable_scope (); - /* set up arguments $(1) .. $(N) */ - for (i=0; argv[i]; i++) + for (i=0; *argv; ++i, ++argv) { - char num[10]; - struct variable* var; + char num[11]; + 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); - o = variable_buffer_output (o, expanded_body, strlen (expanded_body)); - free (expanded_body); + /* Expand the body in the context of the arguments, adding the result to + the variable buffer. */ + + o = variable_expand_string (o, body, flen+3); + pop_variable_scope (); - free (body); - return o; + return o + strlen(o); } -#define STRING_SIZE_TUPLE(s) (s), (sizeof(s)-1) +#define STRING_SIZE_TUPLE(_s) (_s), (sizeof(_s)-1) /* Lookup table for builtin functions. @@ -1851,7 +1804,7 @@ func_apply (o, argv, funcname) table. 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 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("words"), 1, 1, func_words}, { 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("warning"), 1, 1, func_error}, - { STRING_SIZE_TUPLE("foreach"), 3, 0, func_foreach}, #ifdef EXPERIMENTAL - { STRING_SIZE_TUPLE("apply"), 1, 1, func_apply}, { STRING_SIZE_TUPLE("eq"), 2, 1, func_eq}, { STRING_SIZE_TUPLE("if"), 3, 0, func_if}, { STRING_SIZE_TUPLE("not"), 1, 1, func_not}, diff --git a/main.c b/main.c index cad697a4..f9b5abdc 100644 --- a/main.c +++ b/main.c @@ -1358,7 +1358,7 @@ int main (int argc, char ** argv) for (p = old_files->list; *p != 0; ++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->update_status = 0; f->command_state = cs_finished; @@ -1369,7 +1369,7 @@ int main (int argc, char ** argv) for (p = new_files->list; *p != 0; ++p) { f = enter_command_line_file (*p); - f->last_mtime = NEW_MTIME; + f->last_mtime = f->mtime_before_update = NEW_MTIME; } } diff --git a/make.texinfo b/make.texinfo index 1e611836..b77afcf6 100644 --- a/make.texinfo +++ b/make.texinfo @@ -269,6 +269,7 @@ Functions for Transforming Text * Text Functions:: General-purpose text manipulation functions. * File Name Functions:: Functions for manipulating file names. * Foreach Function:: Repeat some text with controlled variation. +* Apply Function:: Expand a user-defined function. * Origin Function:: Find where a variable got its value. * 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. * File Name Functions:: Functions for manipulating file names. * Foreach Function:: Repeat some text with controlled variation. +* Apply Function:: Expand a user-defined function. * Origin Function:: Find where a variable got its value. * Shell Function:: Substitute the output of a shell command. * Make Control Functions:: Functions that control how make runs. @@ -5224,8 +5226,9 @@ or like this: $@{@var{function} @var{arguments}@} @end example -Here @var{function} is a function name; one of a short list of names that -are part of @code{make}. There is no provision for defining new functions. +Here @var{function} is a function name; one of a short list of names +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 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}. @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 @findex foreach @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, 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 @findex origin @cindex variables, origin of diff --git a/remake.c b/remake.c index 94b70e10..75af6586 100644 --- a/remake.c +++ b/remake.c @@ -94,7 +94,7 @@ update_goal_chain (goals, makefiles) struct dep *g; for (g = goals; g != 0; g = g->next) - g->changed = g->deferred = 0; + g->changed = 0; } #if 0 @@ -134,7 +134,6 @@ update_goal_chain (goals, makefiles) { unsigned int ocommands_started; int x; - FILE_TIMESTAMP mtime = MTIME (file); check_renamed (file); if (makefiles) { @@ -161,16 +160,6 @@ update_goal_chain (goals, makefiles) decide when to give an "up to date" diagnostic. */ 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; if (x != 0 || file->updated) { @@ -191,7 +180,8 @@ update_goal_chain (goals, makefiles) stop = (!keep_going_flag && !question_flag && !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 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 change STATUS. If STATUS is changed, we will get re-exec'd, and fall into an infinite loop. */ - g->deferred = 0; if (!makefiles || (!just_print_flag && !question_flag)) status = 0; @@ -736,6 +725,9 @@ notice_finished_file (file) { struct file *f; + assert(file->mtime_before_update == 0); + file->mtime_before_update = file->last_mtime; + if (just_print_flag || question_flag || (file->is_target && file->cmds == 0)) file->last_mtime = NEW_MTIME;