mirror of
https://github.com/mirror/make.git
synced 2025-01-27 12:51:07 +08: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
1
AUTHORS
1
AUTHORS
@ -67,6 +67,7 @@ Other contributors:
|
||||
David A. Wheeler <dwheeler@dwheeler.com>
|
||||
David Boyce <dsb@boyski.com>
|
||||
Frank Heckenbach <f.heckenbach@fh-soft.de>
|
||||
Kaz Kylheku <kaz@kylheku.com>
|
||||
|
||||
With suggestions/comments/bug reports from a cast of ... well ...
|
||||
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
|
||||
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
|
||||
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 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 an explicit rule has multiple targets they can be treated in one
|
||||
of two possible ways: as independent targets or as grouped targets.
|
||||
The manner in which they are treated is determined by the separator that
|
||||
appears after the list of targets.
|
||||
|
||||
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
|
||||
@item
|
||||
@ -3030,13 +3039,18 @@ kbd.o command.o files.o: command.h
|
||||
|
||||
@noindent
|
||||
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
|
||||
Similar recipes work for all the targets. The recipes do not need
|
||||
to be absolutely identical, since the automatic variable @samp{$@@}
|
||||
can be used to substitute the particular target to be remade into the
|
||||
commands (@pxref{Automatic Variables}). For example:
|
||||
Similar recipes work for all the targets. The automatic variable
|
||||
@samp{$@@} can be used to substitute the particular target to be
|
||||
remade into the commands (@pxref{Automatic Variables}). For example:
|
||||
|
||||
@example
|
||||
@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,
|
||||
,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
|
||||
@section 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,
|
||||
,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 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
|
||||
@subsection Pattern Rule Examples
|
||||
|
@ -1315,6 +1315,7 @@ main (int argc, char **argv, char **envp)
|
||||
{
|
||||
const char *features = "target-specific order-only second-expansion"
|
||||
" else-if shortest-stem undefine oneshell nocomment"
|
||||
" grouped-target"
|
||||
#ifndef NO_ARCHIVES
|
||||
" archives"
|
||||
#endif
|
||||
|
@ -67,6 +67,7 @@ char *alloca ();
|
||||
# define __NO_STRING_INLINES
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
|
148
src/read.c
148
src/read.c
@ -72,7 +72,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_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,
|
||||
struct ebuffer *ebuf);
|
||||
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,
|
||||
unsigned int cmds_started, char *commands,
|
||||
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,
|
||||
struct vmodifiers *vmod,
|
||||
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);
|
||||
static void remove_comments (char *line);
|
||||
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;
|
||||
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;
|
||||
@ -591,7 +593,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; \
|
||||
@ -599,6 +602,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;
|
||||
@ -1023,7 +1027,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||
variable we don't want to expand it. So, walk from the
|
||||
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);
|
||||
wtype = get_next_mword (line, &lb_next, &wlen);
|
||||
switch (wtype)
|
||||
{
|
||||
case w_eol:
|
||||
@ -1035,6 +1039,8 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||
|
||||
case w_colon:
|
||||
case w_dcolon:
|
||||
case w_ampcolon:
|
||||
case w_ampdcolon:
|
||||
/* We accept and ignore rules without targets for
|
||||
compatibility with SunOS 4 make. */
|
||||
no_targets = 1;
|
||||
@ -1080,20 +1086,29 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||
}
|
||||
|
||||
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)
|
||||
break;
|
||||
|
||||
@ -1123,12 +1138,21 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||
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. */
|
||||
*colonp = '\0';
|
||||
filenames = PARSE_SIMPLE_SEQ (&p2, struct nameseq);
|
||||
*colonp = ':';
|
||||
p2 = colonp;
|
||||
{
|
||||
char save = *colonp;
|
||||
|
||||
/* If we have &:, it specifies that the targets are understood to be
|
||||
updated/created together by a single invocation of the recipe. */
|
||||
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)
|
||||
{
|
||||
@ -1930,7 +1954,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,
|
||||
size_t commands_idx, int two_colon,
|
||||
@ -1938,6 +1963,7 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||
{
|
||||
struct commands *cmds;
|
||||
struct dep *deps;
|
||||
struct dep *also_make = NULL;
|
||||
const char *implicit_percent;
|
||||
const char *name;
|
||||
|
||||
@ -1963,8 +1989,10 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||
cmds->command_lines = 0;
|
||||
cmds->recipe_prefix = prefix;
|
||||
}
|
||||
else if (are_also_makes)
|
||||
O (fatal, flocp, _("grouped targets must provide a recipe"));
|
||||
else
|
||||
cmds = 0;
|
||||
cmds = NULL;
|
||||
|
||||
/* If there's a prereq string then parse it--unless it's eligible for 2nd
|
||||
expansion: if so, snap_deps() will do it. */
|
||||
@ -2159,6 +2187,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
|
||||
@ -2223,6 +2260,29 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||
O (error, flocp,
|
||||
_("*** 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.
|
||||
@ -2660,6 +2720,8 @@ readline (struct ebuffer *ebuf)
|
||||
w_variable A word containing one or more variables/functions
|
||||
w_colon A colon
|
||||
w_dcolon A double-colon
|
||||
w_ampcolon An ampersand-colon (&:) token
|
||||
w_ampdcolon An ampersand-double-colon (&::) token
|
||||
w_semicolon A semicolon
|
||||
w_varassign A variable assignment operator (=, :=, ::=, +=, ?=, or !=)
|
||||
|
||||
@ -2668,7 +2730,7 @@ readline (struct ebuffer *ebuf)
|
||||
in a command list, etc.) */
|
||||
|
||||
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;
|
||||
char *p = buffer, *beg;
|
||||
@ -2717,6 +2779,21 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||
wtype = w_colon;
|
||||
goto done;
|
||||
|
||||
case '&':
|
||||
if (*p == ':')
|
||||
{
|
||||
++p;
|
||||
if (*p != ':')
|
||||
wtype = w_ampcolon; /* &: */
|
||||
else
|
||||
{
|
||||
++p;
|
||||
wtype = w_ampdcolon; /* &:: */
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
|
||||
case '+':
|
||||
case '?':
|
||||
case '!':
|
||||
@ -2726,19 +2803,15 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||
wtype = w_varassign; /* += or ?= or != */
|
||||
goto done;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
break;
|
||||
|
||||
default:
|
||||
if (delim && strchr (delim, c))
|
||||
{
|
||||
wtype = w_static;
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* This is some non-operator word. A word consists of the longest
|
||||
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
|
||||
adjust our assumptions then. */
|
||||
@ -2818,10 +2891,13 @@ get_next_mword (char *buffer, char *delim, char **startp, size_t *length)
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (delim && strchr (delim, c))
|
||||
case '&':
|
||||
if (*p == ':')
|
||||
goto done_word;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
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 New Issue
Block a user