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

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
best we can. */
EINTRLOOP(r, stat(c->cmd_name, &st));
EINTRLOOP(r, stat (c->cmd_name, &st));
if (r < 0)
e = strerror (errno);
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
#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_LINE 1
#define OUTPUT_SYNC_TARGET 2

View File

@ -846,3 +846,11 @@ get_path_max (void)
return value;
}
#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 (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"));
try_implicit_rule (file, depth);
file->tried_implicit = 1;
}
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 (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"));
try_implicit_rule (file, depth);
file->tried_implicit = 1;
}
if (file->cmds == 0 && !file->is_target

View File

@ -59,6 +59,55 @@ struct file *suffix_file;
/* Maximum length of a suffix. */
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
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->suffixes = xmalloc (sizeof (const char *));
r->lens = xmalloc (sizeof (unsigned int));
r->_defn = NULL;
r->lens[0] = (unsigned int) strlen (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->suffixes);
free (rule->lens);
free ((void *) rule->_defn);
/* We can't free the storage for the commands because there
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->suffixes = target_percents;
r->lens = xmalloc (n * sizeof (unsigned int));
r->_defn = NULL;
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. */
print_rule (struct rule *r)
{
unsigned int i;
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);
fputs (get_rule_defn (r), stdout);
putchar ('\n');
if (r->cmds != 0)
print_commands (r->cmds);

View File

@ -25,6 +25,7 @@ struct rule
const char **suffixes; /* Suffixes (after '%') of each target. */
struct dep *deps; /* Dependencies of the rule. */
struct commands *cmds; /* Commands to execute. */
char *_defn; /* Definition of the rule. */
unsigned short num; /* Number of targets. */
char terminal; /* If terminal (double-colon). */
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,
unsigned short num, int terminal, struct dep *deps,
struct commands *commands, int override);
const char *get_rule_defn (struct rule *rule);
void print_rule_data_base (void);