From 510e5ce801d957338be764a794f1463b662a7edc Mon Sep 17 00:00:00 2001 From: Dmitry Goncharov <dgoncharov@users.sf.net> Date: Mon, 15 Mar 2021 02:10:49 -0400 Subject: [PATCH] [SV 60188] Explicit prereqs cannot be intermediate files If a prereq of a pattern is an explicit target, it should not be considered an intermediate file. (Minor tweaks by Paul Smith <psmith@gnu.org>) * src/dep.h (struct nameseq): Add is_explicit flag. * src/implicit.c (struct patdeps): Ditto. (pattern_search): Set the is_explicit flag appropriately for each prerequisite, based on whether it contained a pattern or not. Update the help output to note implicit vs. explicit prereqs. * tests/scripts/features/double_colon: Add tests. * tests/scripts/features/grouped_targets: Ditto. * tests/scripts/features/patternrules: Ditto. * tests/scripts/features/se_implicit: Ditto. * tests/scripts/features/statipattrules: Ditto. --- src/dep.h | 8 ++- src/implicit.c | 17 ++++- tests/scripts/features/double_colon | 15 +++++ tests/scripts/features/grouped_targets | 26 ++++++++ tests/scripts/features/patternrules | 89 +++++++++++++++++++++++++- tests/scripts/features/se_implicit | 63 ++++++++++++++++++ tests/scripts/features/statipattrules | 21 ++++++ 7 files changed, 233 insertions(+), 6 deletions(-) diff --git a/src/dep.h b/src/dep.h index 76718f00..a86e40fa 100644 --- a/src/dep.h +++ b/src/dep.h @@ -38,7 +38,10 @@ struct nameseq /* Structure representing one dependency of a file. Each struct file's 'deps' points to a chain of these, through 'next'. - 'stem' is the stem for this dep line of static pattern rule or NULL. */ + 'stem' is the stem for this dep line of static pattern rule or NULL. + explicit is set when implicit rule search is performed and the prerequisite + does not contain %. When explicit is set the file is not intermediate. */ + #define DEP(_t) \ NAMESEQ (_t); \ @@ -49,7 +52,8 @@ struct nameseq unsigned int ignore_mtime : 1; \ unsigned int staticpattern : 1; \ unsigned int need_2nd_expansion : 1; \ - unsigned int ignore_automatic_vars : 1 + unsigned int ignore_automatic_vars : 1; \ + unsigned int is_explicit : 1; struct dep { diff --git a/src/implicit.c b/src/implicit.c index b281a177..5bbd6b79 100644 --- a/src/implicit.c +++ b/src/implicit.c @@ -153,6 +153,7 @@ struct patdeps struct file *file; unsigned int ignore_mtime : 1; unsigned int ignore_automatic_vars : 1; + unsigned int is_explicit : 1; }; /* This structure stores information about pattern rules that we need @@ -540,6 +541,7 @@ pattern_search (struct file *file, int archive, /* If we don't need a second expansion, just replace the %. */ if (! dep->need_2nd_expansion) { + int is_explicit = 1; p = strchr (nptr, '%'); if (p == 0) strcpy (depname, nptr); @@ -556,6 +558,7 @@ pattern_search (struct file *file, int archive, memcpy (o, stem, stemlen); o += stemlen; strcpy (o, p + 1); + is_explicit = 0; } /* Parse the expanded string. It might have wildcards. */ @@ -566,6 +569,7 @@ pattern_search (struct file *file, int archive, ++deps_found; d->ignore_mtime = dep->ignore_mtime; d->ignore_automatic_vars = dep->ignore_automatic_vars; + d->is_explicit = is_explicit; } /* We've used up this dep, so next time get a new one. */ @@ -586,6 +590,7 @@ pattern_search (struct file *file, int archive, int add_dir = 0; size_t len; struct dep **dptr; + int is_explicit; nptr = get_next_word (nptr, &len); if (nptr == 0) @@ -615,6 +620,7 @@ pattern_search (struct file *file, int archive, { memcpy (depname, nptr, len); depname[len] = '\0'; + is_explicit = 1; } else { @@ -635,6 +641,7 @@ pattern_search (struct file *file, int archive, } memcpy (o, p + 1, len - i - 1); o[len - i - 1] = '\0'; + is_explicit = 0; } /* Set up for the next word. */ @@ -674,6 +681,7 @@ pattern_search (struct file *file, int archive, ++deps_found; if (order_only) d->ignore_mtime = 1; + d->is_explicit = is_explicit; dptr = &d->next; } @@ -726,6 +734,7 @@ pattern_search (struct file *file, int archive, memset (pat, '\0', sizeof (struct patdeps)); pat->ignore_mtime = d->ignore_mtime; pat->ignore_automatic_vars = d->ignore_automatic_vars; + pat->is_explicit = d->is_explicit; DBS (DB_IMPLICIT, (is_rule @@ -777,13 +786,14 @@ pattern_search (struct file *file, int archive, } /* We could not find the file in any place we should look. - Try to make this dependency as an intermediate file, but + Look for an implicit rule to make this dependency, but only on the second pass. */ if (intermed_ok) { DBS (DB_IMPLICIT, - (_("Looking for a rule with intermediate file '%s'.\n"), + (_("Looking for a rule with %s file '%s'.\n"), + d->is_explicit ? "explicit" : "intermediate", d->name)); if (int_file == 0) @@ -899,7 +909,7 @@ pattern_search (struct file *file, int archive, f->pat_searched = imf->pat_searched; f->also_make = imf->also_make; f->is_target = 1; - f->intermediate = 1; + f->intermediate = !pat->is_explicit; f->tried_implicit = 1; imf = lookup_file (pat->pattern); @@ -916,6 +926,7 @@ pattern_search (struct file *file, int archive, dep = alloc_dep (); dep->ignore_mtime = pat->ignore_mtime; + dep->is_explicit = pat->is_explicit; dep->ignore_automatic_vars = pat->ignore_automatic_vars; s = strcache_add (pat->name); if (recursions) diff --git a/tests/scripts/features/double_colon b/tests/scripts/features/double_colon index 58f126f6..cd51ea4f 100644 --- a/tests/scripts/features/double_colon +++ b/tests/scripts/features/double_colon @@ -212,6 +212,21 @@ FORCE: unlink('joe-is-forced'); +# sv 60188. +# Even though test.x is explicitly mentioned, terminal pattern rules still +# apply only if the prerequisite exists. +touch('hello.z'); + +run_make_test(q! +all: hello.z +%.z:: test.x ; touch $@ +%.x: ; +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +unlink('hello.z'); + + + # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/grouped_targets b/tests/scripts/features/grouped_targets index 615e9627..131b8c0b 100644 --- a/tests/scripts/features/grouped_targets +++ b/tests/scripts/features/grouped_targets @@ -129,5 +129,31 @@ f g h&:: ; @echo Z '', "Z"); +# sv 60188. +# Test that a file explicitly mentioned by the user and made by an implicit +# rule is not considered intermediate. + +touch('hello.z'); +touch('hello.q'); + +# subtest 1 +# hello.x is not explicitly mentioned and thus is an intermediate file. +run_make_test(q! +all: hello.z +%.z %.q: %.x ; touch $*.z $*.q +%.x: ; +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +# subtest 2 +# test.x is explicitly mentioned and thus is not an intermediate file. +run_make_test(q! +all: hello.z +%.z %.q: %.x test.x ; @echo $*.z $*.q +%.x: ; +!, '', "hello.z hello.q\n"); + +unlink('hello.z'); +unlink('hello.q'); + # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/patternrules b/tests/scripts/features/patternrules index 6510c387..ded8e0db 100644 --- a/tests/scripts/features/patternrules +++ b/tests/scripts/features/patternrules @@ -18,7 +18,7 @@ $dir =~ s,.*/([^/]+)$,../$1,; run_make_test(q! .PHONY: all -all: case.1 case.2 case.3 +all: case.1 case.2 case.3 case.4 # We can't have this, due to "Implicit Rule Search Algorithm" step 5c #xxx: void @@ -43,6 +43,13 @@ all: case.1 case.2 case.3 @exit 0 3.implicit-phony: + +# 4 - explicitly mentioned file made by an implicit rule +%.4: void + @exit 1 +%.4: test.x + @exit 0 +%.x: ; !, '', ''); # TEST #1: make sure files that are built via implicit rules are marked @@ -244,6 +251,86 @@ run_make_test(q! unlink('some file.xx', 'some file.yy'); + +# sv 60188. +# Test that a file explicitly mentioned by the user and made by an implicit +# rule is not considered intermediate. + +touch('hello.z'); +unlink('hello.x'); +unlink('test.x'); + +# subtest 1 +# hello.x is not explicitly mentioned and thus is an intermediate file. +run_make_test(q! +all: hello.z +%.z: %.x + touch $@ +%.x: ; +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +# subtest 2 +# test.x is explicitly mentioned and thus is not an intermediate file. +run_make_test(q! +all: hello.z +%.z: %.x test.x + touch $@ +%.x: ; +!, '', "touch hello.z"); + +unlink('hello.z'); + +# sv 60188. +# Test that a file explicitly mentioned by the user and made by an implicit +# rule is not considered intermediate, even when the builtin rules are used. + +touch('hello.x'); +touch('test.x'); +touch('hello.tsk'); + +# subtest 1 +# hello.o is not explicitly mentioned and thus is an intermediate file. +run_make_test(q! +all: hello.tsk +%.tsk: %.z ; @echo $@ +%.z : %.x ; @echo $@ +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +# subtest 2 +# test.z is explicitly mentioned and thus is not an intermediate file. +# test.z is built first because until it's built we don't know if we +# need to rebuild the intermediate hello.z +run_make_test(q! +all: hello.tsk +%.tsk: %.z test.z ; @echo $@ +%.z : %.x ; @echo $@ +!, '', "test.z\nhello.z\nhello.tsk\n"); + +# subtest 3 +# hello.o is not explicitly mentioned and thus is an intermediate file. +run_make_test(q! +all: hello.tsk +dep:=%.o +%.tsk: $(dep) ; @echo $@ +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +# subtest 4 +# Even when test.z is constructed from 2 variables it is still explicitly +# mentioned and thus is not an intermediate file. +# test.z is built first because until it's built we don't know if we +# need to rebuild the intermediate hello.z +run_make_test(q! +all: hello.tsk +name:=test +suf:=.z +%.tsk: %.z $(name)$(suf) ; @echo $@ +%.z: %.x ; @echo $@ +!, '', "test.z\nhello.z\nhello.tsk\n"); + +unlink('hello.x'); +unlink('test.x'); + + # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/se_implicit b/tests/scripts/features/se_implicit index 866d1fb0..07ce8023 100644 --- a/tests/scripts/features/se_implicit +++ b/tests/scripts/features/se_implicit @@ -262,5 +262,68 @@ run_make_test(q! !, 'q/ux', "q/u\nq/u\n"); + + +# sv 60188. +# Test that a file explicitly mentioned by the user and made by an implicit +# rule is not considered intermediate. + +touch('hello.z'); + +# subtest 1. +# hello.x is derived from the stem and thus is an intermediate file. +run_make_test(q! +.SECONDEXPANSION: +dep:=.x +all: hello.z +%.z: %$$(dep) ; @echo $@ +%.x: ; +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + + +# subtest 2. +# test.x is explicitly mentioned and thus is not an intermediate file. +run_make_test(q! +.SECONDEXPANSION: +dep:=test.x +all: hello.z +%.z: %.x $$(dep) ; @echo $@ +%.x: ; +!, '', "hello.z\n"); + +unlink('hello.z'); + + +# sv 60188. +# Test that a file explicitly mentioned by the user and made by an implicit +# rule is not considered intermediate, even when the builtin rules are used. + +touch('hello.x'); +touch('hello.tsk'); + +# subtest 1. +# hello.z is explicitly mentioned and thus is not an intermediate file. +run_make_test(q! +.SECONDEXPANSION: +dep:=hello.z +all: hello.tsk +%.tsk: $$(dep) ; @echo $@ +%.z : %.x ; @echo $@ +!, '', "hello.z\nhello.tsk"); + +# subtest 2. +# hello.z is derived from the stem and thus is an intermediate file. +run_make_test(q! +.SECONDEXPANSION: +dep:=.z +all: hello.tsk +%.tsk: %$$(dep) ; @echo $@ +%.z : %.x ; @echo $@ +!, '', "#MAKE#: Nothing to be done for 'all'.\n"); + +unlink('hello.x'); +unlink('hello.tsk'); + + # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/statipattrules b/tests/scripts/features/statipattrules index 3f363def..096521ec 100644 --- a/tests/scripts/features/statipattrules +++ b/tests/scripts/features/statipattrules @@ -108,4 +108,25 @@ all.foo.bar: 'all.foo all.one all-one all.foo.two all.foo-two'); +# Test #8: +# sv 60188. +# Static pattern rules are considered explicit rules: no prerequisite of +# a static pattern rule can ever be considered intermediate. + +touch('hello.z'); + +# subtest 1 +run_make_test(q! +hello.z: %.z: %.x ; @echo $@ +%.x: ; +!, '', "hello.z\n"); + +# subtest 2 +run_make_test(q! +hello.z: %.z: test.x ; @echo $@ +%.x: ; +!, '', "hello.z\n"); + +unlink('hello.z'); + 1;