mirror of
https://github.com/mirror/make.git
synced 2025-01-16 07:10:13 +08:00
[SV 62278] Only expand the first pattern during secondary expansion
During normal pattern rule expansion only the first pattern (%) is expanded; however during secondary expansion all patterns were expanded. Modify secondary expansion to match the behavior of normal expansion. Implementation tweaked by Paul Smith <psmith@gnu.org> * src/file.c (expand_deps): Don't use subst_expand() which replaces all % with $*: instead replace only the first one, by hand. Fix a memory leak where the dep structure was not always freed. * tests/scripts/features/statipattrules: Use .RECIPEPREFIX not TAB. Add a series of tests verifying that static pattern rules with and without secondary expansion both return the same results.
This commit is contained in:
parent
93fd0c0c9a
commit
bb21dd4d2d
71
src/file.c
71
src/file.c
@ -572,6 +572,7 @@ expand_deps (struct file *f)
|
|||||||
struct dep *d;
|
struct dep *d;
|
||||||
struct dep **dp;
|
struct dep **dp;
|
||||||
const char *file_stem = f->stem;
|
const char *file_stem = f->stem;
|
||||||
|
const char *fstem;
|
||||||
int initialized = 0;
|
int initialized = 0;
|
||||||
|
|
||||||
f->updating = 0;
|
f->updating = 0;
|
||||||
@ -584,7 +585,6 @@ expand_deps (struct file *f)
|
|||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
struct dep *new, *next;
|
struct dep *new, *next;
|
||||||
char *name = (char *)d->name;
|
|
||||||
|
|
||||||
if (! d->name || ! d->need_2nd_expansion)
|
if (! d->name || ! d->need_2nd_expansion)
|
||||||
{
|
{
|
||||||
@ -594,15 +594,47 @@ expand_deps (struct file *f)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If it's from a static pattern rule, convert the patterns into
|
/* If it's from a static pattern rule, convert the initial pattern in
|
||||||
"$*" so they'll expand properly. */
|
each word to "$*" so they'll expand properly. */
|
||||||
if (d->staticpattern)
|
if (d->staticpattern)
|
||||||
{
|
{
|
||||||
char *o = subst_expand (variable_buffer, name, "%", "$*", 1, 2, 0);
|
const char *cs = d->name;
|
||||||
*o = '\0';
|
size_t nperc = 0;
|
||||||
free (name);
|
|
||||||
d->name = name = xstrdup (variable_buffer);
|
/* Count the number of % in the string. */
|
||||||
d->staticpattern = 0;
|
while ((cs = strchr (cs, '%')) != NULL)
|
||||||
|
{
|
||||||
|
++nperc;
|
||||||
|
++cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nperc)
|
||||||
|
{
|
||||||
|
/* Allocate enough space to replace all % with $*. */
|
||||||
|
size_t slen = strlen (d->name) + nperc + 1;
|
||||||
|
const char *pcs = d->name;
|
||||||
|
char *name = xmalloc (slen);
|
||||||
|
char *s = name;
|
||||||
|
|
||||||
|
/* Substitute the first % in each word. */
|
||||||
|
cs = strchr (pcs, '%');
|
||||||
|
|
||||||
|
while (cs)
|
||||||
|
{
|
||||||
|
memcpy (s, pcs, cs - pcs);
|
||||||
|
s += cs - pcs;
|
||||||
|
*(s++) = '$';
|
||||||
|
*(s++) = '*';
|
||||||
|
pcs = ++cs;
|
||||||
|
|
||||||
|
/* Find the first % after the next whitespace. */
|
||||||
|
cs = strchr (end_of_token (cs), '%');
|
||||||
|
}
|
||||||
|
strcpy (s, pcs);
|
||||||
|
|
||||||
|
free ((char*)d->name);
|
||||||
|
d->name = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're going to do second expansion so initialize file variables for
|
/* We're going to do second expansion so initialize file variables for
|
||||||
@ -621,14 +653,14 @@ expand_deps (struct file *f)
|
|||||||
|
|
||||||
p = variable_expand_for_file (d->name, f);
|
p = variable_expand_for_file (d->name, f);
|
||||||
|
|
||||||
|
/* Free the un-expanded name. */
|
||||||
|
free ((char*)d->name);
|
||||||
|
|
||||||
if (d->stem != 0)
|
if (d->stem != 0)
|
||||||
f->stem = file_stem;
|
f->stem = file_stem;
|
||||||
|
|
||||||
/* At this point we don't need the name anymore: free it. */
|
|
||||||
free (name);
|
|
||||||
|
|
||||||
/* Parse the prerequisites and enter them into the file database. */
|
/* Parse the prerequisites and enter them into the file database. */
|
||||||
new = enter_prereqs (split_prereqs (p), d->stem);
|
new = split_prereqs (p);
|
||||||
|
|
||||||
/* If there were no prereqs here (blank!) then throw this one out. */
|
/* If there were no prereqs here (blank!) then throw this one out. */
|
||||||
if (new == 0)
|
if (new == 0)
|
||||||
@ -640,10 +672,21 @@ expand_deps (struct file *f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Add newly parsed prerequisites. */
|
/* Add newly parsed prerequisites. */
|
||||||
|
fstem = d->stem;
|
||||||
next = d->next;
|
next = d->next;
|
||||||
|
free_dep (d);
|
||||||
*dp = new;
|
*dp = new;
|
||||||
for (dp = &new->next, d = new->next; d != 0; dp = &d->next, d = d->next)
|
for (dp = &new, d = new; d != 0; dp = &d->next, d = d->next)
|
||||||
;
|
{
|
||||||
|
d->file = lookup_file (d->name);
|
||||||
|
if (d->file == 0)
|
||||||
|
d->file = enter_file (d->name);
|
||||||
|
d->name = 0;
|
||||||
|
d->stem = fstem;
|
||||||
|
if (!fstem)
|
||||||
|
/* This file is explicitly mentioned as a prereq. */
|
||||||
|
d->file->is_explicit = 1;
|
||||||
|
}
|
||||||
*dp = next;
|
*dp = next;
|
||||||
d = *dp;
|
d = *dp;
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,15 @@ foo.baz: ;@:
|
|||||||
# perprerequisite's stem (Savannah bug #16053).
|
# perprerequisite's stem (Savannah bug #16053).
|
||||||
#
|
#
|
||||||
run_make_test('
|
run_make_test('
|
||||||
|
.RECIPEPREFIX := >
|
||||||
|
|
||||||
all.foo.bar: %.foo.bar: %.one
|
all.foo.bar: %.foo.bar: %.one
|
||||||
|
|
||||||
all.foo.bar: %.bar: %.two
|
all.foo.bar: %.bar: %.two
|
||||||
|
|
||||||
all.foo.bar:
|
all.foo.bar:
|
||||||
@echo $*
|
> @echo $*
|
||||||
@echo $^
|
> @echo $^
|
||||||
|
|
||||||
.DEFAULT:;@:
|
.DEFAULT:;@:
|
||||||
',
|
',
|
||||||
@ -92,6 +94,7 @@ all.one all.foo.two');
|
|||||||
# (Savannah bug #16053).
|
# (Savannah bug #16053).
|
||||||
#
|
#
|
||||||
run_make_test('
|
run_make_test('
|
||||||
|
.RECIPEPREFIX := >
|
||||||
.SECONDEXPANSION:
|
.SECONDEXPANSION:
|
||||||
|
|
||||||
all.foo.bar: %.foo.bar: %.one $$*-one
|
all.foo.bar: %.foo.bar: %.one $$*-one
|
||||||
@ -99,8 +102,8 @@ all.foo.bar: %.foo.bar: %.one $$*-one
|
|||||||
all.foo.bar: %.bar: %.two $$*-two
|
all.foo.bar: %.bar: %.two $$*-two
|
||||||
|
|
||||||
all.foo.bar:
|
all.foo.bar:
|
||||||
@echo $*
|
> @echo $*
|
||||||
@echo $^
|
> @echo $^
|
||||||
|
|
||||||
.DEFAULT:;@:
|
.DEFAULT:;@:
|
||||||
',
|
',
|
||||||
@ -138,4 +141,161 @@ unrelated: hello.x
|
|||||||
|
|
||||||
unlink('hello.z');
|
unlink('hello.z');
|
||||||
|
|
||||||
|
my @dir = ('', 'lib/'); # With and without last slash.
|
||||||
|
my @secondexpansion = ('', '.SECONDEXPANSION:');
|
||||||
|
|
||||||
|
# The following combinations are generated with and without second expansion.
|
||||||
|
# 1.
|
||||||
|
# all: bye.x
|
||||||
|
# bye.x: %.x: ...
|
||||||
|
#
|
||||||
|
# 2.
|
||||||
|
# all: lib/bye.x
|
||||||
|
# lib/bye.x: %.x: ...
|
||||||
|
#
|
||||||
|
# 3.
|
||||||
|
# all: lib/bye.x
|
||||||
|
# lib/bye.x: lib/%.x: ...
|
||||||
|
#
|
||||||
|
# The following combination is not generated, because there is no rule to
|
||||||
|
# build bye.x, no stem substitution takes place, not of interest of this test.
|
||||||
|
# 4.
|
||||||
|
# all: bye.x
|
||||||
|
# bye.x: lib/%.x: ...
|
||||||
|
#
|
||||||
|
|
||||||
|
for my $se (@secondexpansion) {
|
||||||
|
for my $d (@dir) { # The directory of the prerequisite of 'all'.
|
||||||
|
for my $r (@dir) { # The directory of the prerequisite in the rule definition.
|
||||||
|
(!$d && $r) && next; # Combination 4.
|
||||||
|
my $dollar = $se ? '$' : '';
|
||||||
|
|
||||||
|
# The prerequisite should only have directory if the prerequisite of 'all' has
|
||||||
|
# it and if the prequisite pattern in the rule definition does not have it.
|
||||||
|
# That is combination 2.
|
||||||
|
my $pdir = $d && !$r ? $d : '';
|
||||||
|
|
||||||
|
|
||||||
|
# One func, one %.
|
||||||
|
my $prereqs = "${pdir}bye.1";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(firstword %.1); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# Multiple funcs, each has one %.
|
||||||
|
$prereqs = "${pdir}bye.1 ${pdir}bye.2";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(firstword %.1) $dollar\$(firstword %.2); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# Multiple funcs, each has multiple %.
|
||||||
|
$prereqs = "${pdir}bye.1 ${pdir}bye.2 ${pdir}bye.3 ${pdir}bye.4";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(wordlist 1, 99, %.1 %.2) $dollar\$(wordlist 1, 99, %.3 %.4); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# Multiple funcs, each has multiple %, each prerequisite has multiple %.
|
||||||
|
$prereqs = "${pdir}bye_%_%.1 ${pdir}bye_%_%.2 ${pdir}bye_%_%.3 ${pdir}bye_%_%.4";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(wordlist 1, 99, %_%_%.1 %_%_%.2) $dollar\$(wordlist 1, 99, %_%_%.3 %_%_%.4); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# Nested functions.
|
||||||
|
$prereqs = "${pdir}bye.1 ${pdir}bye.2 ${pdir}bye.3 ${pdir}bye.4";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(wordlist 1, 99, $dollar\$(wordlist 1, 99, %.1 %.2)) $dollar\$(wordlist 1, 99, $dollar\$(wordlist 1,99, %.3 %.4)); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# Multiple funcs, each has multiple words, each word has multiple %, sole %,
|
||||||
|
# various corner cases.
|
||||||
|
# Make should substitude the first % and only the first % in each word with the
|
||||||
|
# stem.
|
||||||
|
$prereqs = "${pdir}bye1%2% ${pdir}bye 3${pdir}bye4%5 6${pdir}bye ${pdir}bye7%8 ${pdir}bye9 ${pdir}bye10% 11${pdir}bye12 13";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(wordlist 1, 99, %1%2% % 3%4%5 6%) %7%8 %9 $dollar\$(wordlist 1, 99, %10% 11%12) 13; \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
if ($port_type eq 'UNIX') {
|
||||||
|
# Test that make does not use some hardcoded array of a finite size on stack.
|
||||||
|
# Long prerequisite name. This prerequisite name is over 66K long.
|
||||||
|
my $prefix = 'abcdefgh' x 128 x 33; # 33K long.
|
||||||
|
my $suffix = 'stuvwxyz' x 128 x 33; # 33K long.
|
||||||
|
$prereqs = "${prefix}${pdir}bye${suffix}.1 ${prefix}${pdir}bye${suffix}.2";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(wordlist 1, 99, ${prefix}%${suffix}.1 ${prefix}%${suffix}.2); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Empty stem.
|
||||||
|
$prereqs = "${pdir}.1";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%bye.x: $dollar\$(firstword %.1); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
|
||||||
|
# A word expands to an empty prerequisite.
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
all: ${d}bye.x
|
||||||
|
${d}bye.x: $r%.x: $dollar\$(%); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}bye.x from \n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Escaped %.
|
||||||
|
# The following combinations are generated without second expansion.
|
||||||
|
# 1.
|
||||||
|
# all: the%weird\\_hello_pattern\\.x
|
||||||
|
# the\\%weird\\_hello_pattern\\.x: the\\%weird\\_%_pattern\\.x: ...
|
||||||
|
#
|
||||||
|
# 2.
|
||||||
|
# all: lib/the%weird\\_hello_pattern\\.x
|
||||||
|
# lib/the\\%weird\\_hello_pattern\\.x: lib/the\\%weird\\_%_pattern\\.x: ...
|
||||||
|
#
|
||||||
|
# Other combinations or second expansion are not tested, because escaped % is
|
||||||
|
# not implemented for those.
|
||||||
|
|
||||||
|
for my $d (@dir) {
|
||||||
|
my $prereqs = "${d}the%weird\\\\_hello_pattern%\\\\.1 ${d}the%weird\\\\_hello_pattern%\\\\.2";
|
||||||
|
run_make_test("
|
||||||
|
$se
|
||||||
|
.PHONY: $prereqs
|
||||||
|
all: ${d}the%weird\\\\_hello_pattern\\\\.x
|
||||||
|
${d}the\\%weird\\\\_hello_pattern\\\\.x: ${d}the\\%weird\\\\_%_pattern\\\\.x: $dollar\$(wordlist 1, 99, ${d}the\\%weird\\\\_%_pattern%\\\\.1 ${d}the\\%weird\\\\_%_pattern%\\\\.2); \$(info \$@ from \$^)
|
||||||
|
", '', "${d}the%weird\\\\_hello_pattern\\\\.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user