mirror of
https://salsa.debian.org/srivasta/make-dfsg.git
synced 2025-01-30 16:41:44 +00:00
[SV 8297] Implement "grouped targets" for explicit rules.
This patch allows "grouped targets" using the &: syntax: tgt1 tgt2 ... tgtn &: pre1 pre2 ... recipe When the &: separator is used (in single or double colon forms), all the targets are understood to be built by a single invocation of the recipe. This is accomplished by piggy-backing on the already-existing pattern rule feature, using the file's "also_make" list. * NEWS: Add information about grouped targets. * doc/make.texi (Multiple Targets): Add information on grouped targets. (Pattern Intro): Refer to the new section to discuss multiple patterns. * src/main.c (main): Add "grouped-targets" to .FEATURES * src/read.c (make_word_type): Add new types for &: and &::. (eval): Recognize the &: and &:: separator and remember when used. (record_files): Accept an indicator of whether the rule is grouped. If so, update also_make for each file to depend on the other files. (get_next_mword): Recognize the &: and &:: word types. * tests/scripts/features/grouped_targets: New test script. * AUTHORS: Add Kaz Kylheku
This commit is contained in:
parent
1710573272
commit
8c888d95f6
7 changed files with 337 additions and 59 deletions
1
AUTHORS
1
AUTHORS
|
@ -67,6 +67,7 @@ Other contributors:
|
||||||
David A. Wheeler <dwheeler@dwheeler.com>
|
David A. Wheeler <dwheeler@dwheeler.com>
|
||||||
David Boyce <dsb@boyski.com>
|
David Boyce <dsb@boyski.com>
|
||||||
Frank Heckenbach <f.heckenbach@fh-soft.de>
|
Frank Heckenbach <f.heckenbach@fh-soft.de>
|
||||||
|
Kaz Kylheku <kaz@kylheku.com>
|
||||||
|
|
||||||
With suggestions/comments/bug reports from a cast of ... well ...
|
With suggestions/comments/bug reports from a cast of ... well ...
|
||||||
hundreds, anyway :)
|
hundreds, anyway :)
|
||||||
|
|
8
NEWS
8
NEWS
|
@ -48,6 +48,14 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=108&set
|
||||||
treated BOTH as simple targets AND as pattern rules. Behavior now matches
|
treated BOTH as simple targets AND as pattern rules. Behavior now matches
|
||||||
the documentation, and pattern rules are no longer created in this case.
|
the documentation, and pattern rules are no longer created in this case.
|
||||||
|
|
||||||
|
* New feature: Grouped explicit targets
|
||||||
|
Pattern rules have always had the ability to generate multiple targets with
|
||||||
|
a single invocation of the recipe. It's now possible to declare that an
|
||||||
|
explicit rule generates multiple targets with a single invocation. To use
|
||||||
|
this, replace the ":" token with "&:" in the rule. To detect this feature
|
||||||
|
search for 'grouped-target' in the .FEATURES special variable.
|
||||||
|
Implementation contributed by Kaz Kylheku <kaz@kylheku.com>
|
||||||
|
|
||||||
* Makefiles can now specify the '-j' option in their MAKEFLAGS variable and
|
* Makefiles can now specify the '-j' option in their MAKEFLAGS variable and
|
||||||
this will cause make to enable that parallelism mode.
|
this will cause make to enable that parallelism mode.
|
||||||
|
|
||||||
|
|
104
doc/make.texi
104
doc/make.texi
|
@ -3012,13 +3012,22 @@ both pieces to the suffix list. In practice, suffixes normally begin with
|
||||||
@cindex targets, multiple
|
@cindex targets, multiple
|
||||||
@cindex rule, with multiple targets
|
@cindex rule, with multiple targets
|
||||||
|
|
||||||
A rule with multiple targets is equivalent to writing many rules, each with
|
When an explicit rule has multiple targets they can be treated in one
|
||||||
one target, and all identical aside from that. The same recipe applies to
|
of two possible ways: as independent targets or as grouped targets.
|
||||||
all the targets, but its effect may vary because you can substitute the
|
The manner in which they are treated is determined by the separator that
|
||||||
actual target name into the recipe using @samp{$@@}. The rule contributes
|
appears after the list of targets.
|
||||||
the same prerequisites to all the targets also.
|
|
||||||
|
|
||||||
This is useful in two cases.
|
@subsubheading Rules with Independent Targets
|
||||||
|
@cindex independent targets
|
||||||
|
@cindex targets, independent
|
||||||
|
|
||||||
|
Rules that use the standard target separator, @code{:}, define
|
||||||
|
independent targets. This is equivalent to writing the same rule once
|
||||||
|
for each target, with duplicated prerequisites and recipes. Typically,
|
||||||
|
the recipe would use automatic variables such as @samp{$@@} to specify
|
||||||
|
which target is being built.
|
||||||
|
|
||||||
|
Rules with independent targets are useful in two cases:
|
||||||
|
|
||||||
@itemize @bullet
|
@itemize @bullet
|
||||||
@item
|
@item
|
||||||
|
@ -3030,13 +3039,18 @@ kbd.o command.o files.o: command.h
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
gives an additional prerequisite to each of the three object files
|
gives an additional prerequisite to each of the three object files
|
||||||
mentioned.
|
mentioned. It is equivalent to writing:
|
||||||
|
|
||||||
|
@example
|
||||||
|
kbd.o: command.h
|
||||||
|
command.o: command.h
|
||||||
|
files.o: command.h
|
||||||
|
@end example
|
||||||
|
|
||||||
@item
|
@item
|
||||||
Similar recipes work for all the targets. The recipes do not need
|
Similar recipes work for all the targets. The automatic variable
|
||||||
to be absolutely identical, since the automatic variable @samp{$@@}
|
@samp{$@@} can be used to substitute the particular target to be
|
||||||
can be used to substitute the particular target to be remade into the
|
remade into the commands (@pxref{Automatic Variables}). For example:
|
||||||
commands (@pxref{Automatic Variables}). For example:
|
|
||||||
|
|
||||||
@example
|
@example
|
||||||
@group
|
@group
|
||||||
|
@ -3070,6 +3084,57 @@ You cannot do this with multiple targets in an ordinary rule, but you
|
||||||
can do it with a @dfn{static pattern rule}. @xref{Static Pattern,
|
can do it with a @dfn{static pattern rule}. @xref{Static Pattern,
|
||||||
,Static Pattern Rules}.
|
,Static Pattern Rules}.
|
||||||
|
|
||||||
|
@subsubheading Rules with Grouped Targets
|
||||||
|
@cindex grouped targets
|
||||||
|
@cindex targets, grouped
|
||||||
|
|
||||||
|
If instead of independent targets you have a recipe that generates
|
||||||
|
multiple files from a single invocation, you can express that
|
||||||
|
relationship by declaring your rule to use @emph{grouped targets}. A
|
||||||
|
grouped target rule uses the separator @code{&:} (the @samp{&} here is
|
||||||
|
used to imply ``all'').
|
||||||
|
|
||||||
|
When @code{make} builds any one of the grouped targets, it understands
|
||||||
|
that all the other targets in the group are also created as a result
|
||||||
|
of the invocation of the recipe. Furthermore, if only some of the
|
||||||
|
grouped targets are out of date or missing @code{make} will realize
|
||||||
|
that running the recipe will update all of the targets.
|
||||||
|
|
||||||
|
As an example, this rule defines a grouped target:
|
||||||
|
|
||||||
|
@example
|
||||||
|
@group
|
||||||
|
foo bar biz &: baz boz
|
||||||
|
echo $^ > foo
|
||||||
|
echo $^ > bar
|
||||||
|
echo $^ > biz
|
||||||
|
@end group
|
||||||
|
@end example
|
||||||
|
|
||||||
|
During the execution of a grouped target's recipe, the automatic
|
||||||
|
variable @samp{$@@} is set to the name of the particular target in the
|
||||||
|
group which triggered the rule. Caution must be used if relying on
|
||||||
|
this variable in the recipe of a grouped target rule.
|
||||||
|
|
||||||
|
Unlike independent targets, a grouped target rule @emph{must} include
|
||||||
|
a recipe. However, targets that are members of a grouped target may
|
||||||
|
also appear in independent target rule definitions that do not have
|
||||||
|
recipes.
|
||||||
|
|
||||||
|
Each target may have only one recipe associated with it. If a grouped
|
||||||
|
target appears in either an independent target rule or in another
|
||||||
|
grouped target rule with a recipe, you will get a warning and the
|
||||||
|
latter recipe will replace the former recipe. Additionally the target
|
||||||
|
will be removed from the previous group and appear only in the new
|
||||||
|
group.
|
||||||
|
|
||||||
|
If you would like a target to appear in multiple groups, then you must
|
||||||
|
use the double-colon grouped target separator, @code{&::} when
|
||||||
|
declaring all of the groups containing that target. Grouped
|
||||||
|
double-colon targets are each considered independently, and each
|
||||||
|
grouped double-colon rule's recipe is executed at most once, if at
|
||||||
|
least one of its multiple targets requires updating.
|
||||||
|
|
||||||
@node Multiple Rules, Static Pattern, Multiple Targets, Rules
|
@node Multiple Rules, Static Pattern, Multiple Targets, Rules
|
||||||
@section Multiple Rules for One Target
|
@section Multiple Rules for One Target
|
||||||
@cindex multiple rules for one target
|
@cindex multiple rules for one target
|
||||||
|
@ -9772,20 +9837,13 @@ More than one pattern rule may match a target. In this case
|
||||||
@code{make} will choose the ``best fit'' rule. @xref{Pattern Match,
|
@code{make} will choose the ``best fit'' rule. @xref{Pattern Match,
|
||||||
,How Patterns Match}.
|
,How Patterns Match}.
|
||||||
|
|
||||||
@c !!! The end of of this paragraph should be rewritten. --bob
|
|
||||||
Pattern rules may have more than one target. Unlike normal rules,
|
|
||||||
this does not act as many different rules with the same prerequisites
|
|
||||||
and recipe. If a pattern rule has multiple targets, @code{make} knows
|
|
||||||
that the rule's recipe is responsible for making all of the targets.
|
|
||||||
The recipe is executed only once to make all the targets. When
|
|
||||||
searching for a pattern rule to match a target, the target patterns of
|
|
||||||
a rule other than the one that matches the target in need of a rule
|
|
||||||
are incidental: @code{make} worries only about giving a recipe and
|
|
||||||
prerequisites to the file presently in question. However, when this
|
|
||||||
file's recipe is run, the other targets are marked as having been
|
|
||||||
updated themselves.
|
|
||||||
@cindex multiple targets, in pattern rule
|
@cindex multiple targets, in pattern rule
|
||||||
@cindex target, multiple in pattern rule
|
@cindex target, multiple in pattern rule
|
||||||
|
Pattern rules may have more than one target; however, every target
|
||||||
|
must contain a @code{%} character. Pattern rules are always treated
|
||||||
|
as grouped targets (@pxref{Multiple Targets, , Multiple Targets in a
|
||||||
|
Rule}) regardless of whether they use the @code{:} or @code{&:}
|
||||||
|
separator.
|
||||||
|
|
||||||
@node Pattern Examples, Automatic Variables, Pattern Intro, Pattern Rules
|
@node Pattern Examples, Automatic Variables, Pattern Intro, Pattern Rules
|
||||||
@subsection Pattern Rule Examples
|
@subsection Pattern Rule Examples
|
||||||
|
|
|
@ -1315,6 +1315,7 @@ main (int argc, char **argv, char **envp)
|
||||||
{
|
{
|
||||||
const char *features = "target-specific order-only second-expansion"
|
const char *features = "target-specific order-only second-expansion"
|
||||||
" else-if shortest-stem undefine oneshell nocomment"
|
" else-if shortest-stem undefine oneshell nocomment"
|
||||||
|
" grouped-target"
|
||||||
#ifndef NO_ARCHIVES
|
#ifndef NO_ARCHIVES
|
||||||
" archives"
|
" archives"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -67,6 +67,7 @@ char *alloca ();
|
||||||
# define __NO_STRING_INLINES
|
# define __NO_STRING_INLINES
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
148
src/read.c
148
src/read.c
|
@ -72,7 +72,7 @@ struct vmodifiers
|
||||||
enum make_word_type
|
enum make_word_type
|
||||||
{
|
{
|
||||||
w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
|
w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
|
||||||
w_varassign
|
w_varassign, w_ampcolon, w_ampdcolon
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +142,8 @@ static void do_undefine (char *name, enum variable_origin origin,
|
||||||
static struct variable *do_define (char *name, enum variable_origin origin,
|
static struct variable *do_define (char *name, enum variable_origin origin,
|
||||||
struct ebuffer *ebuf);
|
struct ebuffer *ebuf);
|
||||||
static int conditional_line (char *line, size_t len, const floc *flocp);
|
static int conditional_line (char *line, size_t 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,
|
const char *pattern_percent, char *depstr,
|
||||||
unsigned int cmds_started, char *commands,
|
unsigned int cmds_started, char *commands,
|
||||||
size_t commands_idx, int two_colon,
|
size_t commands_idx, int two_colon,
|
||||||
|
@ -151,7 +152,7 @@ static void record_target_var (struct nameseq *filenames, char *defn,
|
||||||
enum variable_origin origin,
|
enum variable_origin origin,
|
||||||
struct vmodifiers *vmod,
|
struct vmodifiers *vmod,
|
||||||
const floc *flocp);
|
const floc *flocp);
|
||||||
static enum make_word_type get_next_mword (char *buffer, char *delim,
|
static enum make_word_type get_next_mword (char *buffer,
|
||||||
char **startp, size_t *length);
|
char **startp, size_t *length);
|
||||||
static void remove_comments (char *line);
|
static void remove_comments (char *line);
|
||||||
static char *find_map_unquote (char *string, int map);
|
static char *find_map_unquote (char *string, int map);
|
||||||
|
@ -574,6 +575,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
unsigned int cmds_started, tgts_started;
|
unsigned int cmds_started, tgts_started;
|
||||||
int ignoring = 0, in_ignored_define = 0;
|
int ignoring = 0, in_ignored_define = 0;
|
||||||
int no_targets = 0; /* Set when reading a rule without targets. */
|
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;
|
struct nameseq *filenames = 0;
|
||||||
char *depstr = 0;
|
char *depstr = 0;
|
||||||
long nlines = 0;
|
long nlines = 0;
|
||||||
|
@ -591,7 +593,8 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
{ \
|
{ \
|
||||||
fi.lineno = tgts_started; \
|
fi.lineno = tgts_started; \
|
||||||
fi.offset = 0; \
|
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, \
|
cmds_started, commands, commands_idx, two_colon, \
|
||||||
prefix, &fi); \
|
prefix, &fi); \
|
||||||
filenames = 0; \
|
filenames = 0; \
|
||||||
|
@ -599,6 +602,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
commands_idx = 0; \
|
commands_idx = 0; \
|
||||||
no_targets = 0; \
|
no_targets = 0; \
|
||||||
pattern = 0; \
|
pattern = 0; \
|
||||||
|
also_make_targets = 0; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
pattern_percent = 0;
|
pattern_percent = 0;
|
||||||
|
@ -1023,7 +1027,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
variable we don't want to expand it. So, walk from the
|
variable we don't want to expand it. So, walk from the
|
||||||
beginning, expanding as we go, and looking for "interesting"
|
beginning, expanding as we go, and looking for "interesting"
|
||||||
chars. The first word is always expandable. */
|
chars. The first word is always expandable. */
|
||||||
wtype = get_next_mword (line, NULL, &lb_next, &wlen);
|
wtype = get_next_mword (line, &lb_next, &wlen);
|
||||||
switch (wtype)
|
switch (wtype)
|
||||||
{
|
{
|
||||||
case w_eol:
|
case w_eol:
|
||||||
|
@ -1035,6 +1039,8 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
|
|
||||||
case w_colon:
|
case w_colon:
|
||||||
case w_dcolon:
|
case w_dcolon:
|
||||||
|
case w_ampcolon:
|
||||||
|
case w_ampdcolon:
|
||||||
/* We accept and ignore rules without targets for
|
/* We accept and ignore rules without targets for
|
||||||
compatibility with SunOS 4 make. */
|
compatibility with SunOS 4 make. */
|
||||||
no_targets = 1;
|
no_targets = 1;
|
||||||
|
@ -1080,20 +1086,29 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
colonp = find_char_unquote (p2, ':');
|
colonp = find_char_unquote (p2, ':');
|
||||||
#ifdef HAVE_DOS_PATHS
|
|
||||||
/* The drive spec brain-damage strikes again... */
|
|
||||||
/* Note that the only separators of targets in this context
|
|
||||||
are whitespace and a left paren. If others are possible,
|
|
||||||
they should be added to the string in the call to index. */
|
|
||||||
while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
|
|
||||||
colonp > p2 && isalpha ((unsigned char)colonp[-1]) &&
|
|
||||||
(colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
|
|
||||||
colonp = find_char_unquote (colonp + 1, ':');
|
|
||||||
#endif
|
|
||||||
if (colonp != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
wtype = get_next_mword (lb_next, NULL, &lb_next, &wlen);
|
#ifdef HAVE_DOS_PATHS
|
||||||
|
if (colonp > p2)
|
||||||
|
/* The drive spec brain-damage strikes again...
|
||||||
|
Note that the only separators of targets in this context are
|
||||||
|
whitespace and a left paren. If others are possible, add them
|
||||||
|
to the string in the call to strchr. */
|
||||||
|
while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
|
||||||
|
isalpha ((unsigned char) colonp[-1]) &&
|
||||||
|
(colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
|
||||||
|
colonp = find_char_unquote (colonp + 1, ':');
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (colonp)
|
||||||
|
{
|
||||||
|
/* If the previous character is '&', back up before '&:' */
|
||||||
|
if (colonp > p2 && colonp[-1] == '&')
|
||||||
|
--colonp;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wtype = get_next_mword (lb_next, &lb_next, &wlen);
|
||||||
if (wtype == w_eol)
|
if (wtype == w_eol)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1123,12 +1138,21 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||||
O (fatal, fstart, _("missing separator"));
|
O (fatal, fstart, _("missing separator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the colon the end-of-string so we know where to stop
|
{
|
||||||
looking for targets. Start there again once we're done. */
|
char save = *colonp;
|
||||||
*colonp = '\0';
|
|
||||||
filenames = PARSE_SIMPLE_SEQ (&p2, struct nameseq);
|
/* If we have &:, it specifies that the targets are understood to be
|
||||||
*colonp = ':';
|
updated/created together by a single invocation of the recipe. */
|
||||||
p2 = colonp;
|
if (save == '&')
|
||||||
|
also_make_targets = 1;
|
||||||
|
|
||||||
|
/* Make the colon the end-of-string so we know where to stop
|
||||||
|
looking for targets. Start there again once we're done. */
|
||||||
|
*colonp = '\0';
|
||||||
|
filenames = PARSE_SIMPLE_SEQ (&p2, struct nameseq);
|
||||||
|
*colonp = save;
|
||||||
|
p2 = colonp + (save == '&');
|
||||||
|
}
|
||||||
|
|
||||||
if (!filenames)
|
if (!filenames)
|
||||||
{
|
{
|
||||||
|
@ -1930,7 +1954,8 @@ record_target_var (struct nameseq *filenames, char *defn,
|
||||||
that are not incorporated into other data structures. */
|
that are not incorporated into other data structures. */
|
||||||
|
|
||||||
static void
|
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,
|
const char *pattern_percent, char *depstr,
|
||||||
unsigned int cmds_started, char *commands,
|
unsigned int cmds_started, char *commands,
|
||||||
size_t commands_idx, int two_colon,
|
size_t commands_idx, int two_colon,
|
||||||
|
@ -1938,6 +1963,7 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||||
{
|
{
|
||||||
struct commands *cmds;
|
struct commands *cmds;
|
||||||
struct dep *deps;
|
struct dep *deps;
|
||||||
|
struct dep *also_make = NULL;
|
||||||
const char *implicit_percent;
|
const char *implicit_percent;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
@ -1963,8 +1989,10 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||||
cmds->command_lines = 0;
|
cmds->command_lines = 0;
|
||||||
cmds->recipe_prefix = prefix;
|
cmds->recipe_prefix = prefix;
|
||||||
}
|
}
|
||||||
|
else if (are_also_makes)
|
||||||
|
O (fatal, flocp, _("grouped targets must provide a recipe"));
|
||||||
else
|
else
|
||||||
cmds = 0;
|
cmds = NULL;
|
||||||
|
|
||||||
/* If there's a prereq string then parse it--unless it's eligible for 2nd
|
/* If there's a prereq string then parse it--unless it's eligible for 2nd
|
||||||
expansion: if so, snap_deps() will do it. */
|
expansion: if so, snap_deps() will do it. */
|
||||||
|
@ -2159,6 +2187,15 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||||
f->cmds = cmds;
|
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;
|
f->is_target = 1;
|
||||||
|
|
||||||
/* If this is a static pattern rule, set the stem to the part of its
|
/* If this is a static pattern rule, set the stem to the part of its
|
||||||
|
@ -2223,6 +2260,29 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||||
O (error, flocp,
|
O (error, flocp,
|
||||||
_("*** mixed implicit and normal rules: deprecated syntax"));
|
_("*** mixed implicit and normal rules: deprecated syntax"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there are also-makes, then populate a copy of the also-make list into
|
||||||
|
each one. For the last file, we take our original also_make list instead
|
||||||
|
wastefully copying it one more time and freeing it. */
|
||||||
|
{
|
||||||
|
struct dep *i;
|
||||||
|
|
||||||
|
for (i = also_make; i != NULL; i = i->next)
|
||||||
|
{
|
||||||
|
struct file *f = i->file;
|
||||||
|
struct dep *cpy = i->next ? copy_dep_chain (also_make) : also_make;
|
||||||
|
|
||||||
|
if (f->also_make)
|
||||||
|
{
|
||||||
|
OS (error, &cmds->fileinfo,
|
||||||
|
_("warning: overriding group membership for target '%s'"),
|
||||||
|
f->name);
|
||||||
|
free_dep_chain (f->also_make);
|
||||||
|
}
|
||||||
|
|
||||||
|
f->also_make = cpy;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search STRING for an unquoted STOPMAP.
|
/* Search STRING for an unquoted STOPMAP.
|
||||||
|
@ -2660,6 +2720,8 @@ readline (struct ebuffer *ebuf)
|
||||||
w_variable A word containing one or more variables/functions
|
w_variable A word containing one or more variables/functions
|
||||||
w_colon A colon
|
w_colon A colon
|
||||||
w_dcolon A double-colon
|
w_dcolon A double-colon
|
||||||
|
w_ampcolon An ampersand-colon (&:) token
|
||||||
|
w_ampdcolon An ampersand-double-colon (&::) token
|
||||||
w_semicolon A semicolon
|
w_semicolon A semicolon
|
||||||
w_varassign A variable assignment operator (=, :=, ::=, +=, ?=, or !=)
|
w_varassign A variable assignment operator (=, :=, ::=, +=, ?=, or !=)
|
||||||
|
|
||||||
|
@ -2668,7 +2730,7 @@ readline (struct ebuffer *ebuf)
|
||||||
in a command list, etc.) */
|
in a command list, etc.) */
|
||||||
|
|
||||||
static enum make_word_type
|
static enum make_word_type
|
||||||
get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
get_next_mword (char *buffer, char **startp, size_t *length)
|
||||||
{
|
{
|
||||||
enum make_word_type wtype;
|
enum make_word_type wtype;
|
||||||
char *p = buffer, *beg;
|
char *p = buffer, *beg;
|
||||||
|
@ -2717,6 +2779,21 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||||
wtype = w_colon;
|
wtype = w_colon;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
case '&':
|
||||||
|
if (*p == ':')
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
if (*p != ':')
|
||||||
|
wtype = w_ampcolon; /* &: */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
wtype = w_ampdcolon; /* &:: */
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case '+':
|
case '+':
|
||||||
case '?':
|
case '?':
|
||||||
case '!':
|
case '!':
|
||||||
|
@ -2726,19 +2803,15 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||||
wtype = w_varassign; /* += or ?= or != */
|
wtype = w_varassign; /* += or ?= or != */
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* FALLTHROUGH */
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (delim && strchr (delim, c))
|
break;
|
||||||
{
|
|
||||||
wtype = w_static;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is some non-operator word. A word consists of the longest
|
/* This is some non-operator word. A word consists of the longest
|
||||||
string of characters that doesn't contain whitespace, one of [:=#],
|
string of characters that doesn't contain whitespace, one of [:=#],
|
||||||
or [?+!]=, or one of the chars in the DELIM string. */
|
or [?+!]=, or &:. */
|
||||||
|
|
||||||
/* We start out assuming a static word; if we see a variable we'll
|
/* We start out assuming a static word; if we see a variable we'll
|
||||||
adjust our assumptions then. */
|
adjust our assumptions then. */
|
||||||
|
@ -2818,10 +2891,13 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case '&':
|
||||||
if (delim && strchr (delim, c))
|
if (*p == ':')
|
||||||
goto done_word;
|
goto done_word;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = *(p++);
|
c = *(p++);
|
||||||
|
|
133
tests/scripts/features/grouped_targets
Normal file
133
tests/scripts/features/grouped_targets
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# -*-perl-*-
|
||||||
|
|
||||||
|
$description = "This test is about grouped multiple targets indicated by &:";
|
||||||
|
$details = "Here we test for requirements like\n"
|
||||||
|
."- if multiple such targets are updated, the recipe is run once\n"
|
||||||
|
."- parsing issues related to the &: syntax itself\n";
|
||||||
|
|
||||||
|
# Parsing: &: allowed without any targets.
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: all
|
||||||
|
&:;
|
||||||
|
all: ;@echo -n
|
||||||
|
},
|
||||||
|
'', "");
|
||||||
|
|
||||||
|
# Parsing: &: works not preceded by whitespace.
|
||||||
|
run_make_test(q{
|
||||||
|
foo&:;@echo foo
|
||||||
|
},
|
||||||
|
'foo', "foo");
|
||||||
|
|
||||||
|
# Ordinary rule runs recipe four times for t1 t2 t3 t4.
|
||||||
|
# Grouped target rule runs recipe once; others are considered updated.
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: t1 t2 t3 t4 g1 g2 g3 g4
|
||||||
|
t1 t2 t3 t4: ; @echo $@
|
||||||
|
g1 g2 g3 g4 &: ; @echo $@
|
||||||
|
},
|
||||||
|
't1 t2 t3 t4 g1 g2 g3 g4',
|
||||||
|
"t1\n"
|
||||||
|
."t2\n"
|
||||||
|
."t3\n"
|
||||||
|
."t4\n"
|
||||||
|
."g1\n"
|
||||||
|
."#MAKE#: Nothing to be done for 'g2'.\n"
|
||||||
|
."#MAKE#: Nothing to be done for 'g3'.\n"
|
||||||
|
."#MAKE#: Nothing to be done for 'g4'.");
|
||||||
|
|
||||||
|
# Similar to previous test, but targets come from m1 phony
|
||||||
|
# rather than from the command line. We don't see "Nothing to
|
||||||
|
# be done for" messages. Also, note reversed order g4 g3 ...
|
||||||
|
# Thus the auto variable $@ is "g4" when that rule fires.
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: m1 t1 t2 t3 t4 g1 g2 g3 g4
|
||||||
|
m1: t1 t2 t3 t4 g4 g3 g2 g1
|
||||||
|
t1 t2 t3 t4: ; @echo $@
|
||||||
|
g1 g2 g3 g4&: ; @echo $@
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"t1\nt2\nt3\nt4\ng4");
|
||||||
|
|
||||||
|
# Set a grouped target recipe for existing targets
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b
|
||||||
|
M: a b
|
||||||
|
a:
|
||||||
|
a b&: ; @echo Y
|
||||||
|
b:
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"Y");
|
||||||
|
|
||||||
|
# grouped targets require a recipe
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b
|
||||||
|
M: a b
|
||||||
|
a b&:
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"#MAKEFILE#:4: *** grouped targets must provide a recipe. Stop.", 512);
|
||||||
|
|
||||||
|
# Pattern rules use grouped targets anyway so it's a no-op
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M
|
||||||
|
M: a.q b.q
|
||||||
|
a.% b.%&: ; @echo Y
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"Y");
|
||||||
|
|
||||||
|
# Double-colon grouped target rules.
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b c d e f g h
|
||||||
|
M: a b
|
||||||
|
a b c&:: ; @echo X
|
||||||
|
c d e&:: ; @echo Y
|
||||||
|
f g h&:: ; @echo Z
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"X");
|
||||||
|
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b c d e f g h
|
||||||
|
M: c
|
||||||
|
a b c&:: ; @echo X
|
||||||
|
c d e&:: ; @echo Y
|
||||||
|
f g h&:: ; @echo Z
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"X\nY");
|
||||||
|
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b c d e f g h
|
||||||
|
M: a b c d e
|
||||||
|
a b c&:: ; @echo X
|
||||||
|
c d e&:: ; @echo Y
|
||||||
|
f g h&:: ; @echo Z
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"X\nY");
|
||||||
|
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b c d e f g h
|
||||||
|
M: d e
|
||||||
|
a b c&:: ; @echo X
|
||||||
|
c d e&:: ; @echo Y
|
||||||
|
f g h&:: ; @echo Z
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"Y");
|
||||||
|
|
||||||
|
run_make_test(q{
|
||||||
|
.PHONY: M a b c d e f g h
|
||||||
|
M: f g h
|
||||||
|
a b c&:: ; @echo X
|
||||||
|
c d e&:: ; @echo Y
|
||||||
|
f g h&:: ; @echo Z
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
"Z");
|
||||||
|
|
||||||
|
# This tells the test driver that the perl test script executed properly.
|
||||||
|
1;
|
Loading…
Reference in a new issue