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;