Implement the shortest stem first search order for pattern-specific variables and pattern rules.

This commit is contained in:
Boris Kolpackov 2009-09-28 12:31:55 +00:00
parent f9c15cac35
commit f5891a26d8
9 changed files with 179 additions and 23 deletions

View File

@ -1,3 +1,23 @@
2009-09-28 Boris Kolpackov <boris@codesynthesis.com>
* varible.c (create_pattern_var): Insert variables into the
PATTERN_VARS list in the shortest patterns first order.
* implicit.c (tryrule): Add STEMLEN and ORDER members. These are
used to sort the rules.
(stemlen_compare): Compare two tryrule elements.
(pattern_search): Sort the rules so that they are in the shortest
stem first order.
* main.c (.FEATURES): Add a keyword to indicate the new behavior.
* doc/make.texi (Pattern-specific Variable Values): Describe the
new pattern-specific variables application order.
(Introduction to Pattern Rules): Describe the new pattern rules
search order.
* NEWS: Add a note about the new behavior.
2009-09-27 Juan Manuel Guerrero <juan.guerrero@gmx.de> 2009-09-27 Juan Manuel Guerrero <juan.guerrero@gmx.de>
* configh.dos.template: Remove unconditional definition of * configh.dos.template: Remove unconditional definition of
@ -42,12 +62,12 @@
* function.c (string_glob): Free NAME in the nameseq chain. * function.c (string_glob): Free NAME in the nameseq chain.
2009-09-25 Boris Kolpackov <boris@codesynthesis.com> 2009-09-25 Boris Kolpackov <boris@codesynthesis.com>
* implicit.c (pattern_search): Terminate early if we haven't * implicit.c (pattern_search): Terminate early if we haven't
found any rules to try (performance improvement). found any rules to try (performance improvement).
2009-09-25 Boris Kolpackov <boris@codesynthesis.com> 2009-09-25 Boris Kolpackov <boris@codesynthesis.com>
* implicit.c (pattern_search): Merge three parallel arrays, * implicit.c (pattern_search): Merge three parallel arrays,
TRYRULES, MATCHES, and CHECKED_LASTSLASH, into one array TRYRULES, MATCHES, and CHECKED_LASTSLASH, into one array

8
NEWS
View File

@ -39,6 +39,14 @@ Version 3.81.90
prerequisites. This is most useful for target- and pattern-specific prerequisites. This is most useful for target- and pattern-specific
variables. variables.
* WARNING: Backward-incompatibility!
The pattern-specific variables and pattern rules are now applied in the
shortest stem first order instead of the definition order (variables
and rules with the same stem length are still applied in the definition
order). This produces the usually-desired behavior where more specific
patterns are preferred. To detect this feature search for 'shortest-stem'
in the .FEATURES special variable.
Version 3.81 Version 3.81

View File

@ -5720,12 +5720,7 @@ In addition to target-specific variable values
(@pxref{Target-specific, ,Target-specific Variable Values}), GNU (@pxref{Target-specific, ,Target-specific Variable Values}), GNU
@code{make} supports pattern-specific variable values. In this form, @code{make} supports pattern-specific variable values. In this form,
the variable is defined for any target that matches the pattern the variable is defined for any target that matches the pattern
specified. If a target matches more than one pattern, all the specified.
matching pattern-specific variables are interpreted in the order in
which they were defined in the makefile, and collected together into
one set. Variables defined in this way are searched after any
target-specific variables defined explicitly for that target, and
before target-specific variables defined for the parent target.
Set a pattern-specific variable value like this: Set a pattern-specific variable value like this:
@ -5748,6 +5743,31 @@ For example:
will assign @code{CFLAGS} the value of @samp{-O} for all targets will assign @code{CFLAGS} the value of @samp{-O} for all targets
matching the pattern @code{%.o}. matching the pattern @code{%.o}.
If a target matches more than one pattern, the matching pattern-specific
variables with longer stems are interpreted first. This results in more
specific variables taking precedence over the more generic ones, for
example:
@example
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@@
lib/%.o: CFLAGS := -fPIC -g
%.o: CFLAGS := -g
all: foo.o lib/bar.o
@end example
In this example the first definition of the @code{CFLAGS} variable
will be used to update @file{lib/bar.o} even though the second one
also applies to this target. Pattern-specific variables which result
in the same stem length are considered in the order in which they
were defined in the makefile.
Pattern-specific variables are searched after any target-specific
variables defined explicitly for that target, and before target-specific
variables defined for the parent target.
@node Suppressing Inheritance, Special Variables, Pattern-specific, Using Variables @node Suppressing Inheritance, Special Variables, Pattern-specific, Using Variables
@section Suppressing Inheritance @section Suppressing Inheritance
@findex private @findex private
@ -9143,11 +9163,28 @@ 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
The order in which pattern rules appear in the makefile is important It is possible that several pattern rules can be used to update a
since this is the order in which they are considered. target. In this case @code{make} considers rules which produce
Of equally applicable shorter stems first. This results in more specific rules being
rules, only the first one found is used. The rules you write take precedence preferred to the more generic ones, for example:
over those that are built in. Note however, that a rule whose
@example
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@@
lib/%.o: lib/%.c
$(CC) -fPIC -c $(CFLAGS) $(CPPFLAGS) $< -o $@@
all: foo.o lib/bar.o
@end example
In this example the second rule will be used to update @file{lib/bar.o}
even though the first rule can also be used.
Pattern rules which result in the same stem length are considered in
the order in which they appear in the makefile. Of equally applicable
rules, only the first one found is used. The rules you write take
precedence over those that are built in. Note however, that a rule whose
prerequisites actually exist or are mentioned always takes priority over a prerequisites actually exist or are mentioned always takes priority over a
rule with prerequisites that must be made by chaining other implicit rules. rule with prerequisites that must be made by chaining other implicit rules.
@cindex pattern rules, order of @cindex pattern rules, order of

View File

@ -167,10 +167,25 @@ struct tryrule
/* Index of the target in this rule that matched the file. */ /* Index of the target in this rule that matched the file. */
unsigned int matches; unsigned int matches;
/* Stem length for this match. */
unsigned int stemlen;
/* Definition order of this rule. Used to implement stable sort.*/
unsigned int order;
/* Nonzero if the LASTSLASH logic was used in matching this rule. */ /* Nonzero if the LASTSLASH logic was used in matching this rule. */
char checked_lastslash; char checked_lastslash;
}; };
int
stemlen_compare (const void *v1, const void *v2)
{
const struct tryrule *r1 = (const struct tryrule *)v1;
const struct tryrule *r2 = (const struct tryrule *)v2;
int r = r1->stemlen - r2->stemlen;
return r != 0 ? r : (int)(r1->order - r1->order);
}
/* Search the pattern rules for a rule with an existing dependency to make /* Search the pattern rules for a rule with an existing dependency to make
FILE. If a rule is found, the appropriate commands and deps are put in FILE FILE. If a rule is found, the appropriate commands and deps are put in FILE
and 1 is returned. If not, 0 is returned. and 1 is returned. If not, 0 is returned.
@ -385,6 +400,8 @@ pattern_search (struct file *file, int archive,
that rule will be in TRYRULES more than once. */ that rule will be in TRYRULES more than once. */
tryrules[nrules].rule = rule; tryrules[nrules].rule = rule;
tryrules[nrules].matches = ti; tryrules[nrules].matches = ti;
tryrules[nrules].stemlen = stemlen + (check_lastslash ? pathlen : 0);
tryrules[nrules].order = nrules;
tryrules[nrules].checked_lastslash = check_lastslash; tryrules[nrules].checked_lastslash = check_lastslash;
++nrules; ++nrules;
} }
@ -394,6 +411,11 @@ pattern_search (struct file *file, int archive,
if (nrules == 0) if (nrules == 0)
goto done; goto done;
/* Sort the rules to place matches with the shortest stem first. This
way the most specific rules will be tried first. */
if (nrules > 1)
qsort (tryrules, nrules, sizeof (struct tryrule), stemlen_compare);
/* If we have found a matching rule that won't match all filenames, /* If we have found a matching rule that won't match all filenames,
retroactively reject any non-"terminal" rules that do always match. */ retroactively reject any non-"terminal" rules that do always match. */
if (specific_rule_matched) if (specific_rule_matched)

3
main.c
View File

@ -1120,7 +1120,8 @@ main (int argc, char **argv, char **envp)
/* Set up .FEATURES */ /* Set up .FEATURES */
define_variable (".FEATURES", 9, define_variable (".FEATURES", 9,
"target-specific order-only second-expansion else-if", "target-specific order-only second-expansion else-if"
"shortest-stem",
o_default, 0); o_default, 0);
#ifndef NO_ARCHIVES #ifndef NO_ARCHIVES
do_variable_definition (NILF, ".FEATURES", "archives", do_variable_definition (NILF, ".FEATURES", "archives",

View File

@ -1,3 +1,11 @@
2009-09-28 Boris Kolpackov <boris@codesynthesis.com>
* scripts/features/patspecific_vars: Add a test for the shortest
stem first order.
* scripts/features/patternrules: Add a test for the shortest stem
first order.
2009-09-24 Paul Smith <psmith@gnu.org> 2009-09-24 Paul Smith <psmith@gnu.org>
* scripts/features/se_implicit: Add a test for order-only * scripts/features/se_implicit: Add a test for order-only

View File

@ -131,4 +131,18 @@ ab: ; @echo "$(FOO)"
run_make_test(undef, 'FOO=C', "C f1\n"); run_make_test(undef, 'FOO=C', "C f1\n");
# TEST #9: Test shortest stem selection in pattern-specific variables.
run_make_test('
%-mt.x: x := two
%.x: x := one
all: foo.x foo-mt.x
foo.x: ;@echo $x
foo-mt.x: ;@echo $x
',
'',
"one\ntwo");
1; 1;

View File

@ -207,6 +207,18 @@ CWEAVE := :
unlink(@f); unlink(@f);
# TEST #9: Test shortest stem selection in pattern rules.
run_make_test('
%.x: ;@echo one
%-mt.x: ;@echo two
all: foo.x foo-mt.x
',
'',
"one\ntwo");
1;
# This tells the test driver that the perl test script executed properly. # This tells the test driver that the perl test script executed properly.
1; 1;

View File

@ -35,28 +35,62 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
static struct pattern_var *pattern_vars; static struct pattern_var *pattern_vars;
/* Pointer to last struct in the chain, so we can add onto the end. */ /* Pointer to the last struct in the pack of a specific size, from 1 to 255.*/
static struct pattern_var *last_pattern_var; static struct pattern_var *last_pattern_vars[256];
/* Create a new pattern-specific variable struct. */ /* Create a new pattern-specific variable struct. The new variable is
inserted into the PATTERN_VARS list in the shortest patterns first
order to support the shortest stem matching (the variables are
matched in the reverse order so the ones with the longest pattern
will be considered first). Variables with the same pattern length
are inserted in the definition order. */
struct pattern_var * struct pattern_var *
create_pattern_var (const char *target, const char *suffix) create_pattern_var (const char *target, const char *suffix)
{ {
register unsigned int len = strlen (target);
register struct pattern_var *p = xmalloc (sizeof (struct pattern_var)); register struct pattern_var *p = xmalloc (sizeof (struct pattern_var));
if (last_pattern_var != 0) if (pattern_vars != 0)
last_pattern_var->next = p; {
if (len < 256 && last_pattern_vars[len] != 0)
{
p->next = last_pattern_vars[len]->next;
last_pattern_vars[len]->next = p;
}
else
{
/* Find the position where we can insert this variable. */
register struct pattern_var **v;
for (v = &pattern_vars; ; v = &(*v)->next)
{
/* Insert at the end of the pack so that patterns with the
same length appear in the order they were defined .*/
if (*v == 0 || (*v)->len > len)
{
p->next = *v;
*v = p;
break;
}
}
}
}
else else
pattern_vars = p; {
last_pattern_var = p; pattern_vars = p;
p->next = 0; p->next = 0;
}
p->target = target; p->target = target;
p->len = strlen (target); p->len = len;
p->suffix = suffix + 1; p->suffix = suffix + 1;
if (len < 256)
last_pattern_vars[len] = p;
return p; return p;
} }