diff --git a/src/implicit.c b/src/implicit.c index 7505ff69..a2888481 100644 --- a/src/implicit.c +++ b/src/implicit.c @@ -22,9 +22,11 @@ this program. If not, see . */ #include "variable.h" #include "job.h" /* struct child, used inside commands.h */ #include "commands.h" /* set_file_variables */ +#include static int pattern_search (struct file *file, int archive, - unsigned int depth, unsigned int recursions); + unsigned int depth, unsigned int recursions, + int allow_compat_rules); /* For a FILE which has no commands specified, try to figure out some from the implicit pattern rules. @@ -42,7 +44,7 @@ try_implicit_rule (struct file *file, unsigned int depth) (the archive search omits the archive name), it is more specific and should come first. */ - if (pattern_search (file, 0, depth, 0)) + if (pattern_search (file, 0, depth, 0, 0)) return 1; #ifndef NO_ARCHIVES @@ -52,7 +54,7 @@ try_implicit_rule (struct file *file, unsigned int depth) { DBF (DB_IMPLICIT, _("Looking for archive-member implicit rule for '%s'.\n")); - if (pattern_search (file, 1, depth, 0)) + if (pattern_search (file, 1, depth, 0, 0)) return 1; DBS (DB_IMPLICIT, (_("No archive-member implicit rule found for '%s'.\n"), @@ -204,7 +206,8 @@ stemlen_compare (const void *v1, const void *v2) static int pattern_search (struct file *file, int archive, - unsigned int depth, unsigned int recursions) + unsigned int depth, unsigned int recursions, + int allow_compat_rules) { /* Filename we are searching for a rule for. */ const char *filename = archive ? strchr (file->name, '(') : file->name; @@ -255,6 +258,7 @@ pattern_search (struct file *file, int archive, int specific_rule_matched = 0; unsigned int ri; /* uninit checks OK */ + int found_compat_rule = 0; struct rule *rule; char *pathdir = NULL; @@ -722,8 +726,8 @@ pattern_search (struct file *file, int archive, { struct file *df; int is_rule = d->name == dep_name (dep); - int explicit; - int exists = -1; + int explicit = 0; + struct dep *dp = 0; if (file_impossible_p (d->name)) { @@ -772,25 +776,56 @@ pattern_search (struct file *file, int archive, /* If the pattern prereq is also explicitly mentioned for FILE, skip all tests below since it must be built no matter which implicit rule we choose. */ - explicit = df || (exists = file_exists_p (d->name)) != 0; - if (!explicit) - for (struct dep *dp = file->deps; dp != 0; dp = dp->next) + if (df && df->is_target) + /* This prerequisite is mentioned explicitly as a target of + some rule. */ + explicit = 1; + else + for (dp = file->deps; dp != 0; dp = dp->next) if (streq (d->name, dep_name (dp))) - { - explicit = 1; - break; - } + break; - if (explicit) + /* If dp is set, this prerequisite is mentioned explicitly as + a prerequisite of the current target. */ + + if (explicit || dp) { (pat++)->name = d->name; - if (exists > 0) - DBS (DB_IMPLICIT, (_("Found '%s'.\n"), d->name)); - else - DBS (DB_IMPLICIT, (_("'%s' ought to exist.\n"), d->name)); + DBS (DB_IMPLICIT, (_("'%s' ought to exist.\n"), d->name)); continue; } + if (file_exists_p (d->name)) + { + (pat++)->name = d->name; + DBS (DB_IMPLICIT, (_("Found '%s'.\n"), d->name)); + continue; + } + + if (df && allow_compat_rules) + { + (pat++)->name = d->name; + DBS (DB_IMPLICIT, + (_("Using compatibility rule '%s' due to '%s'.\n"), + get_rule_defn (rule), d->name)); + continue; + } + + if (df) + { + /* This prerequisite is mentioned explicitly as a + prerequisite on some rule, but it is not a + prerequisite of the current target. Therefore, this + prerequisite does not qualify as ought-to-exist. Keep + note of this rule and continue the search. If a more + suitable rule is not found, then use this rule. */ + DBS (DB_IMPLICIT, + (_("Prerequisite '%s' of rule '%s' does not qualify" + " as ought to exist.\n"), + d->name, get_rule_defn (rule))); + found_compat_rule = 1; + } + /* This code, given FILENAME = "lib/foo.o", dependency name "lib/foo.c", and VPATH=src, searches for "src/lib/foo.c". */ @@ -825,7 +860,8 @@ pattern_search (struct file *file, int archive, if (pattern_search (int_file, 0, depth + 1, - recursions + 1)) + recursions + 1, + allow_compat_rules)) { pat->pattern = int_file->name; int_file->name = d->name; @@ -842,7 +878,12 @@ pattern_search (struct file *file, int archive, free_variable_set (int_file->variables); if (int_file->pat_variables) free_variable_set (int_file->pat_variables); - file_impossible (d->name); + + /* Keep prerequisites explicitly mentioned on unrelated + rules as "possible" to let compatibility search find + such prerequisites. */ + if (df == 0) + file_impossible (d->name); } /* A dependency of this rule does not exist. Therefore, this @@ -938,8 +979,8 @@ pattern_search (struct file *file, int archive, f->pat_searched = imf->pat_searched; f->also_make = imf->also_make; f->is_target = 1; - f->notintermediate = imf->notintermediate; - f->intermediate = !(pat->is_explicit || f->notintermediate); + f->notintermediate |= imf->notintermediate; + f->intermediate |= !(pat->is_explicit || f->notintermediate); f->tried_implicit = 1; imf = lookup_file (pat->pattern); @@ -1065,10 +1106,20 @@ pattern_search (struct file *file, int archive, free (deplist); if (rule) - DBS (DB_IMPLICIT, (_("Found implicit rule '%s' for '%s'.\n"), - get_rule_defn (rule), filename)); - else - DBS (DB_IMPLICIT, (_("No implicit rule found for '%s'.\n"), filename)); + { + DBS (DB_IMPLICIT, (_("Found implicit rule '%s' for '%s'.\n"), + get_rule_defn (rule), filename)); + return 1; + } - return rule != 0; + if (found_compat_rule) + { + DBS (DB_IMPLICIT, (_("Searching for a compatibility rule for '%s'.\n"), + filename)); + assert (allow_compat_rules == 0); + return pattern_search (file, archive, depth, recursions, 1); + } + + DBS (DB_IMPLICIT, (_("No implicit rule found for '%s'.\n"), filename)); + return 0; } diff --git a/tests/scripts/features/implicit_search b/tests/scripts/features/implicit_search new file mode 100644 index 00000000..32c9a716 --- /dev/null +++ b/tests/scripts/features/implicit_search @@ -0,0 +1,427 @@ +# -*-perl-*- + +$description = "Test implicit rule search."; + +$details = ""; + + +# sv 48643 +# Each test has a %.c rule ahead of %.f rule. +# hello.f exists and hello.c is missing. + +unlink('hello.c', 'hello.tsk', 'hello.o', 'hello.x'); + + + +# Run every test with and without a suffix. +my @suffixes = ('', '.o'); +# Run every test with single and double colon rules. +my @rules = ('', ':'); + +touch('hello.f'); + +for my $s (@suffixes) { +for my $r (@rules) { +touch('hello.f'); + + +# Test that make finds the intended implicit rule based on existence of a +# prerequisite in the filesystem. +# +# '%.o: %.c' rule is skipped and '%.o: %.f' rule is chosen. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", '', "hello.f\n#MAKE#: Nothing to be done for 'all'."); + +# Test that make finds the intended implicit rule based on the explicit +# prerequisite of the top goal and despite the existence of a +# prerequisite in the filesystem. +# +# hello.c is an explicit prerequisite of the top target (hello.o or hello). +# hello.c ought to exist. +# hello.c prerequisite causes '%.o: %.c' rule to be chosen. +run_make_test(" +hello$s: hello.c +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", +'', +"#MAKE#: *** No rule to make target 'hello.c', needed by 'hello$s'. Stop.\n", +512); + +# Test that make finds the intended implicit rule when the implicit +# prerequisite matches a target of an unrelated rule and despite the existence +# of a prerequisite of the other rule candidate in the filesystem. +# +# hello.c matches 'hello.c:' rule. This makes hello.c a target and thus ought +# to exist. +# hello.c prerequisite causes '%.o: %.c' rule to be chosen. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +hello.c:; false +", '', "false\n#MAKE#: *** [#MAKEFILE#:5: hello.c] Error 1\n", 512); + +# Test that make finds the intended implicit rule based on existence of a +# prerequisite in the filesystem, even when the prerequisite of another +# candidate rule is mentioned explicitly on an unrelated rule. +# +# '%.o: %.c' rule is skipped and '%.o: %.f' rule is chosen, even though hello.c +# is mentioned explicitly on 'unrelated: hello.c'. +# ought-to-exist does not apply to hello.c. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +unrelated: hello.c +", '', "hello.f\n#MAKE#: Nothing to be done for 'all'."); + +# Test that make finds the intended implicit rule based on existence of a +# prerequisite in the filesystem. +# +# '%.o: %.c' rule is skipped and '%.o: %.f' rule is chosen. +# Despite '%.o: %.c hello.c' rule having explicit prerequisite hello.c. +# ought-to-exist does not apply to hello.c. +run_make_test(" +all: hello$s +%$s:$r %.c hello.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", '', "hello.f\n#MAKE#: Nothing to be done for 'all'."); + +# '%.o: %.c' rule is skipped and '%.o: %.f' rule is chosen. +# '%.o: %.f hello.f' rule has explicit prerequisite hello.f. +# ought-to-exist does not apply to hello.c. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f hello.f; \$(info hello.f) +", '', "hello.f\n#MAKE#: Nothing to be done for 'all'."); + +# Rule '%: %.f' is chosen, because '%: %.f' requires no intermediates. +# '%: %.c', on the other hand, requires intemediate hello.c to be built by the +# default rule. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info \$<) +%$s:$r %.f; \$(info \$<) +.DEFAULT:; true +unrelated: hello.c +", '', "hello.f\n#MAKE#: Nothing to be done for 'all'."); + + +unlink('hello.f'); +# hello.f is missing. +# This time both hello.c and hello.f are missing and both '%: %.c' and '%: %.f' +# require an intermediate. +# The default rule builds intemerdiate hello.c. +# '%: %.c' rule is chosen to build hello. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info \$<) +%$s:$r %.f; \$(info \$<) +.DEFAULT:; false +unrelated: hello.c +", '', "false\n#MAKE#: *** [#MAKEFILE#:5: hello.c] Error 1\n", 512); + +# hello.f is missing. +# No rule is found, because hello.c is not mentioned explicitly. +run_make_test(" +all: hello$s +%$s:$r %.c; \$(info \$<) +%$s:$r %.f; \$(info \$<) +.DEFAULT:; \@echo default making \$\@ && false +", +'', +"default making hello$s\n#MAKE#: *** [#MAKEFILE#:5: hello$s] Error 1\n", +512); + +} +} + +# Almost the same tests as above, but this time an intermediate is built. + +touch('hello.f'); +for my $s (@suffixes) { +for my $r (@rules) { + +my $result = "#MAKE#: *** No rule to make target 'hello.tsk', needed by 'all'. Stop.\n"; +my $rcode = 512; +if ($s or $r) { + $result = "hello.f\nhello.tsk\n#MAKE#: Nothing to be done for 'all'."; + $rcode = 0; +} + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", '', "$result", $rcode); + +run_make_test(" +all: hello.tsk +%.tsk: %$s hello$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", '', $result, $rcode); + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c hello$s; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", '', $result, $rcode); + +} +} + +for my $r (@rules) { + +# Circular dependency hello.o <- hello.tsk is dropped. +run_make_test(" +all: hello.tsk +%.tsk: %.o; \$(info hello.tsk) +%.o:$r %.c; \$(info hello.c) +%.o:$r %.f %.tsk; \$(info hello.f) +", +'-R', +"#MAKE#: Circular hello.o <- hello.tsk dependency dropped.\nhello.f\nhello.tsk\n#MAKE#: Nothing to be done for 'all'."); + +} + + +for my $s (@suffixes) { +for my $r (@rules) { + +run_make_test(" +all: hello.tsk +hello$s: hello.c +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +", +'', +"#MAKE#: *** No rule to make target 'hello.c', needed by 'hello$s'. Stop.\n", +512); +} +} + +for my $s (@suffixes) { +for my $r (@rules) { + +my $result = "#MAKE#: *** No rule to make target 'hello.tsk', needed by 'all'. Stop.\n"; +if ($s or $r) { + $result = "false\n#MAKE#: *** [#MAKEFILE#:6: hello.c] Error 1\n"; +} + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +hello.c:; false +", '', $result, 512); +} +} + + +for my $s (@suffixes) { +for my $r (@rules) { + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +unrelated: hello$s +", '', "hello.f\nhello.tsk\n#MAKE#: Nothing to be done for 'all'."); +} +} + +for my $s (@suffixes) { +for my $r (@rules) { + +my $result = "#MAKE#: *** No rule to make target 'hello.tsk', needed by 'all'. Stop.\n"; +my $rcode = 512; +if ($s or $r) { + $result = "hello.f\nhello.tsk\n#MAKE#: Nothing to be done for 'all'."; + $rcode = 0; +} + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c; \$(info hello.c) +%$s:$r %.f hello.f; \$(info hello.f) +", '', $result, $rcode); +} +} + +# One of the implicit rules has two prerequisites, hello.c and hello.x +# hello.c does not qualify as ought to exit. +# hello.x can be made from hello.z. +# This test exersizes the break, which prevents making hello.x as an +# intermediate from hello.z during compatibility search. +unlink('hello.f'); +touch('hello.z'); +for my $s (@suffixes) { +for my $r (@rules) { + +run_make_test(" +all: hello.tsk +%.tsk: %$s; \$(info hello.tsk) +%$s:$r %.c %.x; \$(info hello.c) +%$s:$r %.f; \$(info hello.f) +unrelated: hello$s +%.x:$r %.z; \$(info hello.z) +", +'', +"#MAKE#: *** No rule to make target 'hello$s', needed by 'hello.tsk'. Stop.\n", +512); +} +} + +touch ('hello.f'); +# Test implicit search of builtin rules. + +# %: %.c (and other builtin rules) are skipped. +# %: %.f is chosen. +run_make_test(q! +all: hello +!, 'FC="@echo f77" OUTPUT_OPTION=', "f77 hello.f -o hello\n"); + +# %.o: %.c (and other builtin rules) are skipped. +# %.o: %.f is chosen. +run_make_test(q! +all: hello.o +!, 'FC="@echo f77" OUTPUT_OPTION=', "f77 -c hello.f\n"); + + +# %: %.c is chosen. +# hello.c is an explicit prerequisite of the top target hello. +# hello.c ought to exist. +# hello.c prerequisite causes '%: %.c' rule to be chosen. +run_make_test(q! +hello: hello.c +!, +'FC="@echo f77" OUTPUT_OPTION=', +"#MAKE#: *** No rule to make target 'hello.c', needed by 'hello'. Stop.\n", +512); + +# %.o: %.c is chosen. +# hello.c is an explicit prerequisite of the top target hello.o. +# hello.c ought to exist. +# hello.c prerequisite causes '%.o: %.c' rule to be chosen. +run_make_test(q! +hello.o: hello.c +!, +'FC="@echo f77" OUTPUT_OPTION=', +"#MAKE#: *** No rule to make target 'hello.c', needed by 'hello.o'. Stop.\n", +512); + +# %: %.c (and other builtin rules) are skipped. +# %: %.f is chosen. +# ought-to-exist does not apply to hello.c. +run_make_test(q! +all: hello +unrelated: hello.c +!, 'FC="@echo f77" OUTPUT_OPTION=', "f77 hello.f -o hello\n"); + +# %.o: %.c (and other builtin rules) are skipped. +# %.o: %.f is chosen. +# ought-to-exist does not apply to hello.c. +run_make_test(q! +all: hello.o +unrelated: hello.c +!, 'FC="@echo f77" OUTPUT_OPTION=', "f77 -c hello.f\n"); + +# builtin rule %.o: %.f is removed. +# %.o: %.c (and other builtin rules) are skipped, because hello.c is missing. +# ought-to-exist does not apply to hello.c. +# %.o: %.c is chosen as a compatibility rule, because of hello.c. +run_make_test(q! +all: hello.o +unrelated: hello.c +%.o: %.f +!, +'', +"#MAKE#: *** No rule to make target 'hello.c', needed by 'hello.o'. Stop.\n", +512); + + +# sv 17752. +# In this test the builtin match-anything rule '%: %.f' is used to build +# intermediate hello from hello.f, because hello is mentioned explicitly in +# the makefile. +run_make_test(q! +all: hello.tsk +%.tsk: %; $(info $@ from $<) +unrelated: hello +!, +'FC="@echo f77" OUTPUT_OPTION=', +"f77 hello.f -o hello\nhello.tsk from hello\n"); + +# In this test the builtin match-anything rule %: %.f cannot be used to build +# intermediate hello from hello.f, because hello is not mentioned explicitly in +# the makefile. +run_make_test(q! +all: hello.tsk +%.tsk: %; $(info $@ from $<) +!, +'FC="@echo f77" OUTPUT_OPTION=', +"#MAKE#: *** No rule to make target 'hello.tsk', needed by 'all'. Stop.\n", +512); + +# This is just like the one above, but compatibility rule '%.tsk: % %.x' has 2 +# prerequisites, '%' and '%.x'. +# '%' expands to 'hello' and matches the explicit 'hello' on the unrelated rule. +# '%.x' is an intermediate built from 'hello.xx' by rule '%.x: %.xx' during the +# second pass (intermed_ok == 1) of compatibility search. +# This test validates that compatibility search performs both intermed_ok == 0 +# and intermed_ok == 1 passes. +unlink('hello.x'); +touch('hello.xx'); +run_make_test(q! +all: hello.tsk +%.tsk: % %.x; $(info $@ from $^) +unrelated: hello +%.x: %.xx; $(info $@ from $<) +!, +'FC="@echo f77" OUTPUT_OPTION=', +"f77 hello.f -o hello\nhello.x from hello.xx\nhello.tsk from hello hello.x\n"); + +unlink('bye.o', 'bye.tsk', 'bye.x'); +# sv 21670. +# Default recipe is used to build bye.o. +run_make_test(q! +all: bye.tsk +%.tsk: %.o; $(info $@ from $<) +.DEFAULT:; $(info bye.o) +unrelated: bye.o +!, '', "bye.o\nbye.tsk from bye.o\n#MAKE#: Nothing to be done for 'all'."); + +touch('bye.xx'); +# This is just like the one above, but compatibility rule '%.tsk: %.o %.x' has 2 +# prerequisites, '%.o' and '%.x'. +# '%.o' expands to 'bye.o' and matches the explicit 'bye.o' on the unrelated rule. +# '%.x' is an intermediate built from 'bye.xx' by rule '%.x: %.xx' during the +# second pass (intermed_ok == 1) of compatibility search. +# This test validates that compatibility search performs both intermed_ok == 0 +# and intermed_ok == 1 passes. +run_make_test(q! +all: bye.tsk +%.tsk: %.o %.x; $(info $@ from $^) +.DEFAULT:; $(info bye.o) +unrelated: bye.o +%.x: %.xx; $(info $@ from $<) +!, '', +"bye.o\nbye.x from bye.xx\nbye.tsk from bye.o bye.x\n#MAKE#: Nothing to be done for 'all'."); + +unlink('hello.f', 'hello.z', 'hello.xx', 'bye.xx'); + + +# This tells the test driver that the perl test script executed properly. +1;