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
+@samp{$@@} 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 == '=')
_______________________________________________
Help-make mailing list
Help-make@gnu.org
https://lists.gnu.org/mailman/listinfo/help-make