mirror of
https://git.savannah.gnu.org/git/make.git
synced 2024-12-24 12:58:39 +00:00
[SV 62206] Fix %-substitution in second expansion of pattern rules
During second expansion of pattern rules only the first pattern in each "group" was being substituted. E.g. in this makefile: .SECONDEXPANSION: all: hello.x %.x: $$(wordlist 1, 99, %.1 %.%.2) ; $(info $@ from $^) hello.1 hello.\%.2 \%.1 \%.\%.2: ; the output would build "hello.1" and "%.%.2" because each function is considered a single "word" and only the first pattern is replaced. Fix the expansion so each whitespace-separated string is considered a word and the first pattern is replaced, giving "hello.1" and "hello.%.2". * src/rule.c (snap_implicit_rules): Keep enough space to replace % with $(*F) if necessary. * src/implicit.c (pattern_search): During second expansion break each get_next_word result into individual words and replace the first % in each with $* or $(*F) as needed. * tests/scripts/features/patternrules: Add tests for variations.
This commit is contained in:
parent
4e1be4a60c
commit
668eda0527
3 changed files with 171 additions and 21 deletions
|
@ -230,7 +230,9 @@ pattern_search (struct file *file, int archive,
|
|||
|
||||
/* Names of possible dependencies are constructed in this buffer.
|
||||
We may replace % by $(*F) for second expansion, increasing the length. */
|
||||
char *depname = alloca (namelen + max_pattern_dep_length + 4);
|
||||
size_t deplen = namelen + max_pattern_dep_length + 4;
|
||||
char *depname = alloca (deplen);
|
||||
char *dend = depname + deplen;
|
||||
|
||||
/* The start and length of the stem of FILENAME for the current rule. */
|
||||
const char *stem = 0;
|
||||
|
@ -597,6 +599,7 @@ pattern_search (struct file *file, int archive,
|
|||
{
|
||||
int add_dir = 0;
|
||||
size_t len;
|
||||
const char *end;
|
||||
struct dep **dptr;
|
||||
int is_explicit;
|
||||
const char *cp;
|
||||
|
@ -605,12 +608,13 @@ pattern_search (struct file *file, int archive,
|
|||
nptr = get_next_word (nptr, &len);
|
||||
if (nptr == 0)
|
||||
continue;
|
||||
end = nptr + len;
|
||||
|
||||
/* See if this is a transition to order-only prereqs. */
|
||||
if (! order_only && len == 1 && nptr[0] == '|')
|
||||
{
|
||||
order_only = 1;
|
||||
nptr += len;
|
||||
nptr = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -625,7 +629,7 @@ pattern_search (struct file *file, int archive,
|
|||
(since $* and $(*F) are simple variables) there won't be
|
||||
additional re-expansion of the stem. */
|
||||
|
||||
cp = lindex (nptr, nptr + len, '%');
|
||||
cp = lindex (nptr, end, '%');
|
||||
if (cp == 0)
|
||||
{
|
||||
memcpy (depname, nptr, len);
|
||||
|
@ -634,28 +638,56 @@ pattern_search (struct file *file, int archive,
|
|||
}
|
||||
else
|
||||
{
|
||||
size_t i = cp - nptr;
|
||||
/* Go through all % between NPTR and END.
|
||||
Copy contents of [NPTR, END) to depname, with the
|
||||
first % after NPTR and then each first % after white
|
||||
space replaced with $* or $(*F). depname has enough
|
||||
room to substitute each % with $(*F). */
|
||||
char *o = depname;
|
||||
memcpy (o, nptr, i);
|
||||
o += i;
|
||||
if (check_lastslash)
|
||||
{
|
||||
add_dir = 1;
|
||||
memcpy (o, "$(*F)", 5);
|
||||
o += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (o, "$*", 2);
|
||||
o += 2;
|
||||
}
|
||||
memcpy (o, cp + 1, len - i - 1);
|
||||
o[len - i - 1] = '\0';
|
||||
|
||||
is_explicit = 0;
|
||||
for (;;)
|
||||
{
|
||||
size_t i = cp - nptr;
|
||||
assert (o + i < dend);
|
||||
memcpy (o, nptr, i);
|
||||
o += i;
|
||||
if (check_lastslash)
|
||||
{
|
||||
add_dir = 1;
|
||||
assert (o + 5 < dend);
|
||||
memcpy (o, "$(*F)", 5);
|
||||
o += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (o + 2 < dend);
|
||||
memcpy (o, "$*", 2);
|
||||
o += 2;
|
||||
}
|
||||
assert (o < dend);
|
||||
++cp;
|
||||
assert (cp <= end);
|
||||
nptr = cp;
|
||||
if (nptr == end)
|
||||
break;
|
||||
|
||||
/* Skip the rest of this word then find the next %.
|
||||
No need to worry about order-only, or nested
|
||||
functions: NPTR went though get_next_word. */
|
||||
while (cp < end && ! END_OF_TOKEN (*cp))
|
||||
++cp;
|
||||
cp = lindex (cp, end, '%');
|
||||
if (cp == 0)
|
||||
break;
|
||||
}
|
||||
len = end - nptr;
|
||||
memcpy (o, nptr, len);
|
||||
o[len] = '\0';
|
||||
}
|
||||
|
||||
/* Set up for the next word. */
|
||||
nptr += len;
|
||||
nptr = end;
|
||||
|
||||
/* Initialize and set file variables if we haven't already
|
||||
done so. */
|
||||
|
|
13
src/rule.c
13
src/rule.c
|
@ -129,7 +129,18 @@ snap_implicit_rules (void)
|
|||
|
||||
for (dep = prereqs; dep; dep = dep->next)
|
||||
{
|
||||
size_t l = strlen (dep_name (dep));
|
||||
const char *d = dep_name (dep);
|
||||
size_t l = strlen (d);
|
||||
|
||||
if (dep->need_2nd_expansion)
|
||||
/* When pattern_search allocates a buffer, allow 5 bytes per each % to
|
||||
substitute each % with $(*F) while avoiding realloc. */
|
||||
while ((d = strchr (d, '%')) != 0)
|
||||
{
|
||||
l += 4;
|
||||
++d;
|
||||
}
|
||||
|
||||
if (l > max_pattern_dep_length)
|
||||
max_pattern_dep_length = l;
|
||||
++pre_deps;
|
||||
|
|
|
@ -469,5 +469,112 @@ run_make_test(q!
|
|||
|
||||
unlink('1.all', '1.q', '1.r');
|
||||
|
||||
# sv 62206.
|
||||
|
||||
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
|
||||
# %.x: ...
|
||||
#
|
||||
# 2.
|
||||
# all: lib/bye.x
|
||||
# %.x: ...
|
||||
#
|
||||
# 3.
|
||||
# all: 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
|
||||
# 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 target 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 : '';
|
||||
|
||||
my $prereqs = "${pdir}bye.1";
|
||||
|
||||
# One func, one %.
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${d}bye.x
|
||||
$r%.x: $dollar\$(firstword %.1); \$(info \$@ from \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||
|
||||
$prereqs = "${pdir}bye.1 ${pdir}bye.2";
|
||||
|
||||
# Multiple funcs, each has one %.
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${d}bye.x
|
||||
$r%.x: $dollar\$(firstword %.1) $dollar\$(firstword %.2); \$(info \$@ from \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||
|
||||
$prereqs = "${pdir}bye.1 ${pdir}bye.2 ${pdir}bye.3 ${pdir}bye.4";
|
||||
|
||||
# Multiple funcs, each has multiple %.
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${d}bye.x
|
||||
$r%.x: $dollar\$(wordlist 1, 99, %.1 %.2) $dollar\$(wordlist 1, 99, %.3 %.4); \$(info \$@ from \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||
|
||||
$prereqs = "${pdir}bye.1 ${pdir}bye.2 ${pdir}bye.3 ${pdir}bye.4";
|
||||
|
||||
# Nested functions.
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${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 \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||
|
||||
$prereqs = "${pdir}bye1%2% ${pdir}bye ${pdir}3bye4%5 ${pdir}6bye ${pdir}bye7%8 ${pdir}bye9 ${pdir}bye10% ${pdir}11bye12 13";
|
||||
|
||||
# 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.
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${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 \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${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 = "${pdir}${prefix}bye${suffix}.1 ${pdir}${prefix}bye${suffix}.2";
|
||||
|
||||
run_make_test("
|
||||
$se
|
||||
all: ${d}bye.x
|
||||
$r%.x: $dollar\$(wordlist 1, 99, ${prefix}%${suffix}.1 ${prefix}%${suffix}.2); \$(info \$@ from \$^)
|
||||
.PHONY: $prereqs
|
||||
", '', "${d}bye.x from $prereqs\n#MAKE#: Nothing to be done for 'all'.\n");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This tells the test driver that the perl test script executed properly.
|
||||
1;
|
||||
|
|
Loading…
Reference in a new issue