help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Implement "grouped targets" in ordinary rules.


From: Kaz Kylheku (gmake)
Subject: [PATCH] Implement "grouped targets" in ordinary rules.
Date: Tue, 12 Mar 2019 20:50:59 -0700
User-agent: Roundcube Webmail/0.9.2

This patch (against 4.2.1) adds the + (plus) syntax:

  + tgt1 tgt2 ... tgtn : pre1 pre2 ...
        recipe

When the + is present, then the targets are understood to be built
together by one invocation of the recipe: each one lists the others in
its "also_make" list, similarly to what multiple-target pattern rules
already do.

* doc/make.texi: Section on Multiple Targets updated.

* read.c (enum make_word_type): New enum member, w_plus.
(record_files): New argument, are_also_makes flag which indicates
that the targets are all linked together as also-makes.
If this is true, then we build an also_make list which contains
each target represented as a dep. When all targets are registered,
we then make a second pass, installing a copy of this also_make
list into each target, possibly catenating it with an existing
also_make list.
(record_waiting_files): Pass new argument to record_files.
(eval): Check for the + token and set a new local variable
also_make_targets if that is so. This is then passed to
record_files by the record_waiting_files macro.
(get_next_mword): If a + is followed by whitespace, or end of string,
rathe than =, then instead of falling through, recognize it as the new
w_plus token.
---
 doc/make.texi | 41 ++++++++++++++++++++++++++++-----
 read.c        | 64 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/doc/make.texi b/doc/make.texi
index 01bcec7..033c72d 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -3012,13 +3012,27 @@ both pieces to the suffix list. In practice, suffixes normally begin with
 @cindex targets, multiple
 @cindex rule, with multiple targets

-A rule with multiple targets is equivalent to writing many rules, each with -one target, and all identical aside from that. The same recipe applies to
-all the targets, but its effect may vary because you can substitute the
-actual target name into the recipe using @samp{$@@}. The rule contributes
-the same prerequisites to all the targets also.
+When a rule has multiple targets, the semantics depends on what kind
+of rule it is, and whether the "grouped targets" feature is invoked.

-This is useful in two cases.
+There are two possible semantics. A rule with multiple targets may be
+equivalent to writing many rules, each with one target, and all identical aside +from that. Under this semantics, the same recipe applies to all the targets, +but its effect may vary because you can substitute the actual target name into +the recipe using @samp{$@@}. The rule contributes the same prerequisites to
+all the targets also. This is known as "replicated recipe semantics".
+The other semantics is "grouped target" semantics. Under this semantics, +the files are all understood to be updated or created together by a single
+invocation of the recipe.
+
+Pattern rules implicitly follow "grouped target" semantics.
+
+Ordinary rules use "replicated recipe" semantics by default. If
+the symbol @samp{+} is placed before the first target, separated
+from it by whitespace, then the rule follows "grouped target"
+semantics.
+
+"Replicated recipe semantics" is useful in two cases.

 @itemize @bullet
 @item
@@ -3064,6 +3078,21 @@ types of output, one if given @samp{-big} and one if given
 for an explanation of the @code{subst} function.
 @end itemize

+"Grouped target" semantics is useful when a recipe builds multiple
+files, which are further involved in dependencies. For instance,
+it can express a rule rule for building a parser using Yacc, whose
+recipe generates @samp{y.tab.c} and @samp{y.tab.h} at the same time.
+Both are made to be dependent on @samp{parser.y}. If neither one
+exists, or if both are out of date with respect to @samp{parser.y},
+the recipe is invoked only once. Furthermore, if for some reason
+just one of these two files is missing or out of date, GNU Make
+knows that running the recipe also updates the other.
+
+Note that under "grouped target" semantics, the @samp{$@@} variable
+still expands to a single target: it refers to that target which
+triggered the execution of the recipe. The recipe cannot rely on
address@hidden@@} being a stable reference to a particular one of the targets.
+
 Suppose you would like to vary the prerequisites according to the
 target, much as the variable @samp{$@@} allows you to vary the recipe.
 You cannot do this with multiple targets in an ordinary rule, but you
diff --git a/read.c b/read.c
index b870aa8..983bdc5 100644
--- a/read.c
+++ b/read.c
@@ -71,7 +71,7 @@ struct vmodifiers
 enum make_word_type
   {
w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
-     w_varassign
+     w_varassign, w_plus
   };


@@ -141,7 +141,8 @@ static void do_undefine (char *name, enum variable_origin origin, static struct variable *do_define (char *name, enum variable_origin origin,
                                    struct ebuffer *ebuf);
 static int conditional_line (char *line, int len, const floc *flocp);
-static void record_files (struct nameseq *filenames, const char *pattern, +static void record_files (struct nameseq *filenames, int are_also_makes,
+                          const char *pattern,
                           const char *pattern_percent, char *depstr,
                           unsigned int cmds_started, char *commands,
                           unsigned int commands_idx, int two_colon,
@@ -576,6 +577,7 @@ eval (struct ebuffer *ebuf, int set_default)
   unsigned int cmds_started, tgts_started;
   int ignoring = 0, in_ignored_define = 0;
int no_targets = 0; /* Set when reading a rule without targets. */
+  int also_make_targets = 0;    /* Set when reading grouped targets. */
   struct nameseq *filenames = 0;
   char *depstr = 0;
   long nlines = 0;
@@ -593,7 +595,8 @@ eval (struct ebuffer *ebuf, int set_default)
{ \ fi.lineno = tgts_started; \ fi.offset = 0; \ - record_files (filenames, pattern, pattern_percent, depstr, \ + record_files (filenames, also_make_targets, pattern, \ + pattern_percent, depstr, \ cmds_started, commands, commands_idx, two_colon, \ prefix, &fi); \ filenames = 0; \
@@ -601,6 +604,7 @@ eval (struct ebuffer *ebuf, int set_default)
commands_idx = 0; \ no_targets = 0; \ pattern = 0; \ + also_make_targets = 0; \
     } while (0)

   pattern_percent = 0;
@@ -1027,6 +1031,20 @@ eval (struct ebuffer *ebuf, int set_default)
            beginning, expanding as we go, and looking for "interesting"
            chars.  The first word is always expandable.  */
         wtype = get_next_mword (line, NULL, &lb_next, &wlen);
+
+        /* If the line starts with + followed by whitespace, then
+ it specifies that the targets are understood to be updated/created
+           together by a single invocation of the recipe. In this case,
+ we record a single entry for the first target which follows the +, + and install the remaining targets as the also_make list of deps + for that file, rather than iterating over the targets and replicating
+           the rule for each one. */
+        if (wtype == w_plus) {
+          also_make_targets = 1;
+          lb_next += wlen;
+          wtype = get_next_mword (lb_next, NULL, &lb_next, &wlen);
+        }
+
         switch (wtype)
           {
           case w_eol:
@@ -1931,7 +1949,8 @@ record_target_var (struct nameseq *filenames, char *defn,
    that are not incorporated into other data structures.  */

 static void
-record_files (struct nameseq *filenames, const char *pattern,
+record_files (struct nameseq *filenames, int are_also_makes,
+              const char *pattern,
               const char *pattern_percent, char *depstr,
               unsigned int cmds_started, char *commands,
               unsigned int commands_idx, int two_colon,
@@ -1939,6 +1958,7 @@ record_files (struct nameseq *filenames, const char *pattern,
 {
   struct commands *cmds;
   struct dep *deps;
+  struct dep *also_make = 0;
   const char *implicit_percent;
   const char *name;

@@ -2158,6 +2178,15 @@ record_files (struct nameseq *filenames, const char *pattern,
           f->cmds = cmds;
         }

+      if (are_also_makes)
+        {
+          struct dep *also = alloc_dep();
+          also->name = f->name;
+          also->file = f;
+          also->next = also_make;
+          also_make = also;
+        }
+
       f->is_target = 1;

/* If this is a static pattern rule, set the stem to the part of its @@ -2222,6 +2251,27 @@ record_files (struct nameseq *filenames, const char *pattern,
         O (error, flocp,
_("*** mixed implicit and normal rules: deprecated syntax"));
     }
+
+ /* If the files are also-makes, then populate a copy of the also-make list
+     into each one. */
+
+  if (are_also_makes && also_make)
+    {
+      struct dep *i;
+
+      for (i = also_make; i != 0; i = i->next)
+        {
+          struct file *f = i->file;
+          struct dep *also_copy = copy_dep_chain(also_make);
+          struct dep *j;
+
+          for (j = also_copy; j->next != 0; j = j->next)
+            ;
+
+          j->next = f->also_make;
+          f->also_make = also_copy;
+        }
+    }
 }
 
/* Search STRING for an unquoted STOPCHAR or blank (if BLANK is nonzero). @@ -2676,6 +2726,12 @@ get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
       break;

     case '+':
+      if (isspace(*p) || *p == 0)
+        {
+          wtype = w_plus;
+          break;
+        }
+      /* fallthrough */
     case '?':
     case '!':
       if (*p == '=')






reply via email to

[Prev in Thread] Current Thread [Next in Thread]