* Fix up and document $(apply ...) function.
authorPaul Smith <psmith@gnu.org>
Thu, 15 Jul 1999 07:36:44 +0000 (07:36 +0000)
committerPaul Smith <psmith@gnu.org>
Thu, 15 Jul 1999 07:36:44 +0000 (07:36 +0000)
ChangeLog
NEWS
dep.h
file.c
filedef.h
function.c
main.c
make.texinfo
remake.c

index b88e034..42ec9fe 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * job.c (start_waiting_job): Don't get a second job token if we
 
        * 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  <eliz@is.elta.co.il>
 1999-03-19  Rob Tulloh  <rob_tulloh@dev.tivoli.com>
diff --git a/NEWS b/NEWS
index 53e1c33..4775cb4 100644 (file)
--- 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
+  <hanwen@cs.uu.nl>.
+
 * 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 <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
   -jN options to submakes rather than forcing them all to use -j1.  The
diff --git a/dep.h b/dep.h
index 90999b6..ca8112f 100644 (file)
--- 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 1321b55..2a9a791 100644 (file)
--- 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)
index 5d45d64..c96c2e3 100644 (file)
--- 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.  */
index da13dd7..982d128 100644 (file)
@@ -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);
 }
 \f
 
-#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 cad697a..f9b5abd 100644 (file)
--- 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;
        }
     }
 
index 1e61183..b77afcf 100644 (file)
@@ -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
index 94b70e1..75af658 100644 (file)
--- 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;