[SV 61042] Enhance logging of implicit rule search

Logging of implicit rule search gives limited information as to why a
given implicit rule was rejected, and if no implicit rule is found we
get the confusing "No rule to make target" result when the real issue
is that some prerequisite of some implicit rule could not be built.

Enhance logging around implicit rule search as follows:
1. The messages which refer to a rule print a description (the targets
   and prerequisites) of the rule.
2. A new message tells when a rule is rejected, along with the reason.
3. The 'Looking for an implicit rule...' message is printed for every
   prerequisite, not just the top-level target.
4. "Trying harder" message is printed, when intermediate prerequisites
   are going to be searched.
5. The 'No rule found...' and 'Found implicit rule...' messages are
   printed for every prerequisite, not just the top-level target.
6. "Ought to exist...", "Found..." or "Not found..." message is
   printed for each prerequisite.

* src/rule.h (struct rule): Remember the definition of the rule.
* src/rule.c (get_rule_defn): Compute the definition of a rule.
(install_pattern_rule): Initialize the definition to empty.
(create_pattern_rule): Ditto.
(freerule): Free the definition.
(print_rule): Use the definition when printing rules.
* src/remake.c (update_file_1): Push debug output down into
try_implicit_rule().
* src/implicit.c (try_implicit_rule): Add debugging
(pattern_search): Show the rule definition in various debug output.
Add new debug messages for implicit rule search.

Additional changes by Paul Smith <psmith@gnu.org>:

Since we usually don't need the rule definition, defer computing it
until we do.

* bootstrap.conf: Include the mempcpy Gnulib module.
* src/makeint.h (mempcpy): Declare mempcpy if not available.
* src/misc.c (mempcpy): Define mempcpy if not available.
* src/config.h-vms.template: Don't set HAVE_MEMPCPY.
* src/config.h.W32.template: Ditto.
* src/rule.h (get_rule_defn): Return the definition of a rule.
* src/rule.c (get_rule_defn): If we don't have a definition compute
it; either way return it.
* src/implicit.c (pattern_search): Rework the handling of explicit
prerequisites to pattern rules to be more clear.  There is no change
in behavior.
This commit is contained in:
Dmitry Goncharov 2021-10-17 18:03:04 -04:00 committed by Paul Smith
parent f5af979357
commit f8f9d371ff
10 changed files with 146 additions and 59 deletions

View File

@ -49,5 +49,6 @@ fdl
findprog-in findprog-in
getloadavg getloadavg
host-cpu-c-abi host-cpu-c-abi
mempcpy
strerror strerror
make-glob" make-glob"

View File

@ -324,6 +324,9 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
/* Define to 1 if you have the <memory.h> header file. */ /* Define to 1 if you have the <memory.h> header file. */
/* #undef HAVE_MEMORY_H */ /* #undef HAVE_MEMORY_H */
/* Define to 1 if you have the `mempcpy' function. */
/* #undef HAVE_MEMPCPY */
/* Define to 1 if you have the <ndir.h> header file. */ /* Define to 1 if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */ /* #undef HAVE_NDIR_H */

View File

@ -177,6 +177,9 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
/* Define to 1 if you have the <memory.h> header file. */ /* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1 #define HAVE_MEMORY_H 1
/* Define to 1 if you have the `mempcpy' function. */
/* #undef HAVE_MEMPCPY */
/* Define to 1 if you have the 'mkstemp' function. */ /* Define to 1 if you have the 'mkstemp' function. */
/* #undef HAVE_MKSTEMP */ /* #undef HAVE_MKSTEMP */

View File

@ -54,6 +54,9 @@ try_implicit_rule (struct file *file, unsigned int depth)
_("Looking for archive-member implicit rule for '%s'.\n")); _("Looking for archive-member implicit rule for '%s'.\n"));
if (pattern_search (file, 1, depth, 0)) if (pattern_search (file, 1, depth, 0))
return 1; return 1;
DBS (DB_IMPLICIT,
(_("No archive-member implicit rule found for '%s'.\n"),
file->name));
} }
#endif #endif
@ -309,7 +312,9 @@ pattern_search (struct file *file, int archive,
don't use it here. */ don't use it here. */
if (rule->in_use) if (rule->in_use)
{ {
DBS (DB_IMPLICIT, (_("Avoiding implicit rule recursion.\n"))); DBS (DB_IMPLICIT,
(_("Avoiding implicit rule recursion for rule '%s'.\n"),
get_rule_defn (rule)));
continue; continue;
} }
@ -432,6 +437,8 @@ pattern_search (struct file *file, int archive,
for (intermed_ok = 0; intermed_ok < 2; ++intermed_ok) for (intermed_ok = 0; intermed_ok < 2; ++intermed_ok)
{ {
pat = deplist; pat = deplist;
if (intermed_ok)
DBS (DB_IMPLICIT, (_("Trying harder.\n")));
/* Try each pattern rule till we find one that applies. If it does, /* Try each pattern rule till we find one that applies. If it does,
expand its dependencies (as substituted) and chain them in DEPS. */ expand its dependencies (as substituted) and chain them in DEPS. */
@ -480,6 +487,10 @@ pattern_search (struct file *file, int archive,
} }
} }
DBS (DB_IMPLICIT,
(_("Trying pattern rule '%s' with stem '%.*s'.\n"),
get_rule_defn (rule), (int) stemlen, stem));
if (stemlen + (check_lastslash ? pathlen : 0) > GET_PATH_MAX) if (stemlen + (check_lastslash ? pathlen : 0) > GET_PATH_MAX)
{ {
DBS (DB_IMPLICIT, (_("Stem too long: '%s%.*s'.\n"), DBS (DB_IMPLICIT, (_("Stem too long: '%s%.*s'.\n"),
@ -488,9 +499,6 @@ pattern_search (struct file *file, int archive,
continue; continue;
} }
DBS (DB_IMPLICIT, (_("Trying pattern rule with stem '%.*s'.\n"),
(int) stemlen, stem));
if (!check_lastslash) if (!check_lastslash)
{ {
memcpy (stem_str, stem, stemlen); memcpy (stem_str, stem, stemlen);
@ -712,9 +720,10 @@ pattern_search (struct file *file, int archive,
/* Go through the nameseq and handle each as a prereq name. */ /* Go through the nameseq and handle each as a prereq name. */
for (d = dl; d != 0; d = d->next) for (d = dl; d != 0; d = d->next)
{ {
struct dep *expl_d; struct file *df;
struct file *f;
int is_rule = d->name == dep_name (dep); int is_rule = d->name == dep_name (dep);
int explicit;
int exists = -1;
if (file_impossible_p (d->name)) if (file_impossible_p (d->name))
{ {
@ -723,9 +732,11 @@ pattern_search (struct file *file, int archive,
second pass either since we know that will fail. */ second pass either since we know that will fail. */
DBS (DB_IMPLICIT, DBS (DB_IMPLICIT,
(is_rule (is_rule
? _("Rejecting impossible rule prerequisite '%s'.\n") ? _("Rejecting rule '%s' due to impossible rule"
: _("Rejecting impossible implicit prerequisite '%s'.\n"), " prerequisite '%s'.\n")
d->name)); : _("Rejecting rule '%s' due to impossible implicit"
" prerequisite '%s'.\n"),
get_rule_defn (rule), d->name));
tryrules[ri].rule = 0; tryrules[ri].rule = 0;
failed = 1; failed = 1;
@ -742,38 +753,41 @@ pattern_search (struct file *file, int archive,
? _("Trying rule prerequisite '%s'.\n") ? _("Trying rule prerequisite '%s'.\n")
: _("Trying implicit prerequisite '%s'.\n"), d->name)); : _("Trying implicit prerequisite '%s'.\n"), d->name));
/* If this prereq is also explicitly mentioned for FILE, df = lookup_file (d->name);
skip all tests below since it must be built no matter
which implicit rule we choose. */
for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next) /* If we found a file for the dep, set its intermediate flag.
if (streq (dep_name (expl_d), d->name)) df->is_explicit is set when the dep file is mentioned
break; explicitly on some other rule. d->is_explicit is set when
if (expl_d != 0) the dep file is mentioned explicitly on this rule. E.g.:
{ %.x : %.y ; ...
(pat++)->name = d->name; then:
continue; one.x:
} one.y: # df->is_explicit
vs.
/* f->is_explicit is set when this file is mentioned one.x: one.y # d->is_explicit
explicitly on some other rule. d->is_explicit is set when */
this file is mentioned explicitly on this rule. */ if (df && !df->is_explicit && !d->is_explicit)
f = lookup_file (d->name); df->intermediate = 1;
if (f && !f->is_explicit && !d->is_explicit)
f->intermediate = 1; /* If the pattern prereq is also explicitly mentioned for
FILE, skip all tests below since it must be built no
/* The DEP->changed flag says that this dependency resides matter which implicit rule we choose. */
in a nonexistent directory. So we normally can skip explicit = df || (exists = file_exists_p (d->name)) != 0;
looking for the file. However, if CHECK_LASTSLASH is if (!explicit)
set, then the dependency file we are actually looking for for (struct dep *dp = file->deps; dp != 0; dp = dp->next)
is in a different directory (the one gotten by prepending if (streq (d->name, dep_name (dp)))
FILENAME's directory), so it might actually exist. */ {
explicit = 1;
/* @@ dep->changed check is disabled. */ break;
if (f /* || ((!dep->changed || check_lastslash) */ }
|| file_exists_p (d->name))
if (explicit)
{ {
(pat++)->name = d->name; (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));
continue; continue;
} }
@ -785,7 +799,7 @@ pattern_search (struct file *file, int archive,
if (vname) if (vname)
{ {
DBS (DB_IMPLICIT, DBS (DB_IMPLICIT,
(_("Found prerequisite '%s' as VPATH '%s'\n"), (_("Found prerequisite '%s' as VPATH '%s'.\n"),
d->name, vname)); d->name, vname));
(pat++)->name = d->name; (pat++)->name = d->name;
continue; continue;
@ -833,6 +847,14 @@ pattern_search (struct file *file, int archive,
/* A dependency of this rule does not exist. Therefore, this /* A dependency of this rule does not exist. Therefore, this
rule fails. */ rule fails. */
if (intermed_ok)
DBS (DB_IMPLICIT,
(_("Rejecting rule '%s' "
"due to impossible prerequisite '%s'.\n"),
get_rule_defn (rule), d->name));
else
DBS (DB_IMPLICIT, (_("Not found '%s'.\n"), d->name));
failed = 1; failed = 1;
break; break;
} }
@ -1042,5 +1064,11 @@ pattern_search (struct file *file, int archive,
free (tryrules); free (tryrules);
free (deplist); 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));
return rule != 0; return rule != 0;
} }

View File

@ -922,7 +922,7 @@ reap_children (int block, int err)
to fork/exec but I don't want to bother with that. Just do the to fork/exec but I don't want to bother with that. Just do the
best we can. */ best we can. */
EINTRLOOP(r, stat(c->cmd_name, &st)); EINTRLOOP(r, stat (c->cmd_name, &st));
if (r < 0) if (r < 0)
e = strerror (errno); e = strerror (errno);
else if (S_ISDIR(st.st_mode) || !(st.st_mode & S_IXUSR)) else if (S_ISDIR(st.st_mode) || !(st.st_mode & S_IXUSR))

View File

@ -671,6 +671,11 @@ int strncasecmp (const char *s1, const char *s2, int n);
# endif # endif
#endif #endif
#if !HAVE_MEMPCPY
/* Create our own, in misc.c */
void *mempcpy (void *dest, const void *src, size_t n);
#endif
#define OUTPUT_SYNC_NONE 0 #define OUTPUT_SYNC_NONE 0
#define OUTPUT_SYNC_LINE 1 #define OUTPUT_SYNC_LINE 1
#define OUTPUT_SYNC_TARGET 2 #define OUTPUT_SYNC_TARGET 2

View File

@ -846,3 +846,11 @@ get_path_max (void)
return value; return value;
} }
#endif #endif
#if !HAVE_MEMPCPY
void *
mempcpy (void *dest, const void *src, size_t n)
{
return (char *) memcpy (dest, src, n) + n;
}
#endif

View File

@ -509,10 +509,7 @@ update_file_1 (struct file *file, unsigned int depth)
if (!file->phony && file->cmds == 0 && !file->tried_implicit) if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{ {
if (try_implicit_rule (file, depth)) try_implicit_rule (file, depth);
DBF (DB_IMPLICIT, _("Found an implicit rule for '%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for '%s'.\n"));
file->tried_implicit = 1; file->tried_implicit = 1;
} }
if (file->cmds == 0 && !file->is_target if (file->cmds == 0 && !file->is_target
@ -1045,10 +1042,7 @@ check_dep (struct file *file, unsigned int depth,
if (!file->phony && file->cmds == 0 && !file->tried_implicit) if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{ {
if (try_implicit_rule (file, depth)) try_implicit_rule (file, depth);
DBF (DB_IMPLICIT, _("Found an implicit rule for '%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for '%s'.\n"));
file->tried_implicit = 1; file->tried_implicit = 1;
} }
if (file->cmds == 0 && !file->is_target if (file->cmds == 0 && !file->is_target

View File

@ -59,6 +59,55 @@ struct file *suffix_file;
/* Maximum length of a suffix. */ /* Maximum length of a suffix. */
static size_t maxsuffix; static size_t maxsuffix;
/* Return the rule definition: space separated rule targets, followed by
either a colon or two colons in the case of a terminal rule, followed by
space separated rule prerequisites, followed by a pipe, followed by
order-only prerequisites, if present. */
const char *get_rule_defn (struct rule *r)
{
if (r->_defn == NULL)
{
unsigned int k;
ptrdiff_t len = 8; // Reserve for ":: ", " | " and the null terminator.
char *p;
const char *sep = "";
const struct dep *dep, *ood = 0;
for (k = 0; k < r->num; ++k)
len += r->lens[k] + 1; // Add one for a space.
for (dep = r->deps; dep; dep = dep->next)
len += strlen (dep_name (dep)) + 1; // Add one for a space.
p = r->_defn = xmalloc (len);
for (k = 0; k < r->num; ++k, sep = " ")
p = mempcpy (mempcpy (p, sep, strlen (sep)), r->targets[k], r->lens[k]);
*p++ = ':';
if (r->terminal)
*p++ = ':';
/* Copy all normal dependencies; note any order-only deps. */
for (dep = r->deps; dep; dep = dep->next)
if (dep->ignore_mtime == 0)
p = mempcpy (mempcpy (p, " ", 1), dep_name (dep),
strlen (dep_name (dep)));
else if (ood == 0)
ood = dep;
/* Copy order-only deps, if we have any. */
for (sep = " | "; ood; ood = ood->next, sep = " ")
if (ood->ignore_mtime)
p = mempcpy (mempcpy (p, sep, strlen (sep)), dep_name (ood),
strlen (dep_name (ood)));
*p = '\0';
assert (p - r->_defn < len);
}
return r->_defn;
}
/* Compute the maximum dependency length and maximum number of dependencies of /* Compute the maximum dependency length and maximum number of dependencies of
all implicit rules. Also sets the subdir flag for a rule when appropriate, all implicit rules. Also sets the subdir flag for a rule when appropriate,
@ -398,6 +447,7 @@ install_pattern_rule (struct pspec *p, int terminal)
r->targets = xmalloc (sizeof (const char *)); r->targets = xmalloc (sizeof (const char *));
r->suffixes = xmalloc (sizeof (const char *)); r->suffixes = xmalloc (sizeof (const char *));
r->lens = xmalloc (sizeof (unsigned int)); r->lens = xmalloc (sizeof (unsigned int));
r->_defn = NULL;
r->lens[0] = (unsigned int) strlen (p->target); r->lens[0] = (unsigned int) strlen (p->target);
r->targets[0] = p->target; r->targets[0] = p->target;
@ -439,6 +489,7 @@ freerule (struct rule *rule, struct rule *lastrule)
free ((void *)rule->targets); free ((void *)rule->targets);
free ((void *)rule->suffixes); free ((void *)rule->suffixes);
free (rule->lens); free (rule->lens);
free ((void *) rule->_defn);
/* We can't free the storage for the commands because there /* We can't free the storage for the commands because there
are ways that they could be in more than one place: are ways that they could be in more than one place:
@ -488,6 +539,7 @@ create_pattern_rule (const char **targets, const char **target_percents,
r->targets = targets; r->targets = targets;
r->suffixes = target_percents; r->suffixes = target_percents;
r->lens = xmalloc (n * sizeof (unsigned int)); r->lens = xmalloc (n * sizeof (unsigned int));
r->_defn = NULL;
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
{ {
@ -505,17 +557,8 @@ create_pattern_rule (const char **targets, const char **target_percents,
static void /* Useful to call from gdb. */ static void /* Useful to call from gdb. */
print_rule (struct rule *r) print_rule (struct rule *r)
{ {
unsigned int i; fputs (get_rule_defn (r), stdout);
putchar ('\n');
for (i = 0; i < r->num; ++i)
{
fputs (r->targets[i], stdout);
putchar ((i + 1 == r->num) ? ':' : ' ');
}
if (r->terminal)
putchar (':');
print_prereqs (r->deps);
if (r->cmds != 0) if (r->cmds != 0)
print_commands (r->cmds); print_commands (r->cmds);

View File

@ -25,6 +25,7 @@ struct rule
const char **suffixes; /* Suffixes (after '%') of each target. */ const char **suffixes; /* Suffixes (after '%') of each target. */
struct dep *deps; /* Dependencies of the rule. */ struct dep *deps; /* Dependencies of the rule. */
struct commands *cmds; /* Commands to execute. */ struct commands *cmds; /* Commands to execute. */
char *_defn; /* Definition of the rule. */
unsigned short num; /* Number of targets. */ unsigned short num; /* Number of targets. */
char terminal; /* If terminal (double-colon). */ char terminal; /* If terminal (double-colon). */
char in_use; /* If in use by a parent pattern_search. */ char in_use; /* If in use by a parent pattern_search. */
@ -54,4 +55,5 @@ void install_pattern_rule (struct pspec *p, int terminal);
void create_pattern_rule (const char **targets, const char **target_percents, void create_pattern_rule (const char **targets, const char **target_percents,
unsigned short num, int terminal, struct dep *deps, unsigned short num, int terminal, struct dep *deps,
struct commands *commands, int override); struct commands *commands, int override);
const char *get_rule_defn (struct rule *rule);
void print_rule_data_base (void); void print_rule_data_base (void);