diff --git a/src/file.c b/src/file.c index 2afad872..5aba3b6d 100644 --- a/src/file.c +++ b/src/file.c @@ -572,6 +572,7 @@ expand_deps (struct file *f) struct dep *d; struct dep **dp; const char *file_stem = f->stem; + const char *fstem; int initialized = 0; f->updating = 0; @@ -584,7 +585,6 @@ expand_deps (struct file *f) { char *p; struct dep *new, *next; - char *name = (char *)d->name; if (! d->name || ! d->need_2nd_expansion) { @@ -594,15 +594,47 @@ expand_deps (struct file *f) continue; } - /* If it's from a static pattern rule, convert the patterns into - "$*" so they'll expand properly. */ + /* If it's from a static pattern rule, convert the initial pattern in + each word to "$*" so they'll expand properly. */ if (d->staticpattern) { - char *o = subst_expand (variable_buffer, name, "%", "$*", 1, 2, 0); - *o = '\0'; - free (name); - d->name = name = xstrdup (variable_buffer); - d->staticpattern = 0; + const char *cs = d->name; + size_t nperc = 0; + + /* Count the number of % in the string. */ + 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 @@ -621,14 +653,14 @@ expand_deps (struct file *f) p = variable_expand_for_file (d->name, f); + /* Free the un-expanded name. */ + free ((char*)d->name); + if (d->stem != 0) 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. */ - 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 (new == 0) @@ -640,10 +672,21 @@ expand_deps (struct file *f) } /* Add newly parsed prerequisites. */ + fstem = d->stem; next = d->next; + free_dep (d); *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; d = *dp; } diff --git a/tests/scripts/features/statipattrules b/tests/scripts/features/statipattrules index 378fedc5..7a6a8bcb 100644 --- a/tests/scripts/features/statipattrules +++ b/tests/scripts/features/statipattrules @@ -72,13 +72,15 @@ foo.baz: ;@: # perprerequisite's stem (Savannah bug #16053). # run_make_test(' +.RECIPEPREFIX := > + all.foo.bar: %.foo.bar: %.one all.foo.bar: %.bar: %.two all.foo.bar: - @echo $* - @echo $^ +> @echo $* +> @echo $^ .DEFAULT:;@: ', @@ -92,6 +94,7 @@ all.one all.foo.two'); # (Savannah bug #16053). # run_make_test(' +.RECIPEPREFIX := > .SECONDEXPANSION: 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: - @echo $* - @echo $^ +> @echo $* +> @echo $^ .DEFAULT:;@: ', @@ -138,4 +141,161 @@ unrelated: hello.x 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;