[SV 64571] Add --print-targets option

Add an option to print a list of targets defined in the makefiles.
Don't print targets of implicit rules, or special targets.  To
support this remember which files are deemed suffix rule targets.

Add a missing warning for single-suffix targets with prerequisites.

Suggested by many.  Sample implementation by Tim <tdhutt@gmail.com>.

* NEWS: Announce the new option and single-suffix warning.
* doc/make.1: Add --print-targets to the man page.
* doc/make.texi: Add --print-targets to the documentation.  Clean up
the text around the definition of suffix rules.
* src/main.c (print_targets_flag): New variable for --print-targets.
(switches): Add a new long option --print-targets.
(main): If the option was provided call print_targets() and exit.
* src/filedef.h (struct file): Add a "suffix" boolean value.  Remove
print_prereqs() since it's static.  Add new print_targets().
* src/file.c (rehash_file): Merge the new suffix value.
(print_prereqs): Used only locally: change to static.
(print_target): Print targets which are not suffix rule targets and
are not special targets.
(print_targets): Call print_target() on each file.
* src/rule.c (convert_to_pattern): Make maxsuffix local; it doesn't
need to be static.  Emit ignoring prerequisites for single-suffix
rules as well as double-suffix rules.  Remember which files are
actually suffix rules.
* tests/scripts/features/suffixrules: Test single-suffix behavior.
* tests/scripts/options/print-targets: Add tests for --print-targets.
This commit is contained in:
Paul Smith 2024-01-08 23:14:57 -05:00
parent 1ff728bff4
commit 31036e648f
9 changed files with 181 additions and 48 deletions

9
NEWS
View File

@ -37,6 +37,10 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
things like "ifeq ((foo,bar),)" are now syntax errors. Use a variable to things like "ifeq ((foo,bar),)" are now syntax errors. Use a variable to
hide the comma if needed: "COMMA = ," / "ifeq ((foo$(COMMA)bar),)". hide the comma if needed: "COMMA = ," / "ifeq ((foo$(COMMA)bar),)".
* NOTE: Deprecated behavior.
The check in GNU Make 4.3 for suffix rules with prerequisites didn't check
single-suffix rules, only double-suffix rules. Add the missing check.
* New feature: Unload function for loaded objects * New feature: Unload function for loaded objects
When a loaded object needs to be unloaded by GNU Make, it will invoke an When a loaded object needs to be unloaded by GNU Make, it will invoke an
unload function (if one is defined) beforehand that allows the object to unload function (if one is defined) beforehand that allows the object to
@ -56,6 +60,11 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
invoked recursively, warnings can be controlled only for the current invoked recursively, warnings can be controlled only for the current
instance of make using the .WARNINGS variable. instance of make using the .WARNINGS variable.
* New feature: Printing targets defined by the makefile
A new option "--print-targets" will print all explicit, non-special targets
defined in the makefiles, one per line, then exit with success. No recipes
are invoked and no makefiles are re-built.
* 'make --print-data-base' (or 'make -p') now outputs time of day * 'make --print-data-base' (or 'make -p') now outputs time of day
using the same form as for file timestamps, e.g., "2023-05-10 using the same form as for file timestamps, e.g., "2023-05-10
10:43:57.570558743". Previously it used the form "Wed May 10 10:43:57.570558743". Previously it used the form "Wed May 10

View File

@ -270,10 +270,15 @@ reading the makefiles; then execute as usual or as otherwise
specified. specified.
This also prints the version information given by the This also prints the version information given by the
.B \-v .B \-v
switch (see below). switch (see below). To print the built-in data base only, use
To print the data base without trying to remake any files, use
.IR "make \-p \-f/dev/null" . .IR "make \-p \-f/dev/null" .
.TP 0.5i .TP 0.5i
\fB\-\-print\-targets\fR
Print each target defined as a result of reading the makefiles, one target per
line, then exit with success. Implicit rule targets are not printed, nor are
special targets (target names that consist of "." followed by all upper-case
letters). No recipe commands are invoked and no makefiles are rebuilt.
.TP 0.5i
\fB\-q\fR, \fB\-\-question\fR \fB\-q\fR, \fB\-\-question\fR
``Question mode''. ``Question mode''.
Do not run any commands, or print anything; just return an exit status Do not run any commands, or print anything; just return an exit status

View File

@ -9677,15 +9677,28 @@ performed. @xref{Parallel Output, ,Output During Parallel Execution}.
@cindex @code{--print-data-base} @cindex @code{--print-data-base}
@cindex data base of @code{make} rules @cindex data base of @code{make} rules
@cindex predefined rules and variables, printing @cindex predefined rules and variables, printing
Print the data base (rules and variable values) that results from Print the data base (rules and variable values) that results from reading the
reading the makefiles; then execute as usual or as otherwise makefiles; then execute as usual or as otherwise specified. This also prints
specified. This also prints the version information given by the the version information given by the @samp{-v} switch (see below). To print
@samp{-v} switch (see below). To print the data base without trying the data base without trying to remake any files, use @w{@samp{make -qp}}. To
to remake any files, use @w{@samp{make -qp}}. To print the data base print the data base of predefined rules and variables, use @w{@samp{make -p -f
of predefined rules and variables, use @w{@samp{make -p -f /dev/null}}. /dev/null}}. The data base output contains file name and line number
The data base output contains file name and line number information for information for recipe and variable definitions, so it can be a useful
recipe and variable definitions, so it can be a useful debugging tool debugging tool in complex environments.
in complex environments.
@item --print-targets
@cindex @code{--print-targets}
@cindex print @file{makefile} targets
@cindex targets, printing
Print all the targets defined by reading the makefiles, one target per line,
then exit immediately with success. No implicit targets are printed. No
special targets (target names consisting of ``.'' followed by all upper-case
letters) are printed.
No commands are run, including commands that would rebuild makefiles
(@pxref{Remaking Makefiles, ,How Makefiles Are Remade}); if makefiles need to
be rebuilt then the targets listed might be outdated. Also, @code{make} will
not generate any errors or warnings for missing @code{include} files.
@item -q @item -q
@cindex @code{-q} @cindex @code{-q}
@ -11237,26 +11250,26 @@ You can use a last-resort rule to override part of another makefile.
@section Old-Fashioned Suffix Rules @section Old-Fashioned Suffix Rules
@cindex old-fashioned suffix rules @cindex old-fashioned suffix rules
@cindex suffix rule @cindex suffix rule
@cindex inference rule
@dfn{Suffix rules} are the old-fashioned way of defining implicit rules for @dfn{Suffix rules} (called @dfn{inference rules} in POSIX) are an
@code{make}. Suffix rules are obsolete because pattern rules are more old-fashioned way of defining implicit rules for @code{make}. Suffix rules
general and clearer. They are supported in GNU @code{make} for are less powerful and harder to use than pattern rules (@pxref{Pattern Rules,
compatibility with old makefiles. They come in two kinds: ,Defining and Redefining Pattern Rules}); they are supported by GNU Make
@dfn{double-suffix} and @dfn{single-suffix}. primarily for POSIX compatibility. They come in two flavors:
@dfn{single-suffix} and @dfn{double-suffix}.
A double-suffix rule is defined by a pair of suffixes: the target
suffix and the source suffix. It matches any file whose name ends
with the target suffix. The corresponding implicit prerequisite is
made by replacing the target suffix with the source suffix in the file
name. A two-suffix rule @samp{.c.o} (whose target and source suffixes
are @samp{.o} and @samp{.c}) is equivalent to the pattern rule
@samp{%.o : %.c}.
A single-suffix rule is defined by a single suffix, which is the source A single-suffix rule is defined by a single suffix, which is the source
suffix. It matches any file name, and the corresponding implicit suffix. It matches any file name, and the corresponding implicit prerequisite
prerequisite name is made by appending the source suffix. A single-suffix name is made by appending the source suffix. A single-suffix rule whose
rule whose source suffix is @samp{.c} is equivalent to the pattern rule source suffix is @samp{.c} is equivalent to the pattern rule @samp{% : %.c}.
@samp{% : %.c}.
A double-suffix rule is defined by a pair of suffixes: the source suffix and
the target suffix. It matches any file whose name ends with the target
suffix. The corresponding implicit prerequisite is made by replacing the
target suffix with the source suffix in the file name. A two-suffix rule
@samp{.c.o} has a source suffix @samp{.c} and a target suffix @samp{.o}, and
is equivalent to the pattern rule @samp{%.o : %.c}.
Suffix rule definitions are recognized by comparing each rule's target Suffix rule definitions are recognized by comparing each rule's target
against a defined list of known suffixes. When @code{make} sees a rule against a defined list of known suffixes. When @code{make} sees a rule

View File

@ -26,6 +26,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */
#include "debug.h" #include "debug.h"
#include "hash.h" #include "hash.h"
#include "shuffle.h" #include "shuffle.h"
#include "rule.h"
/* Remember whether snap_deps has been invoked: we need this to be sure we /* Remember whether snap_deps has been invoked: we need this to be sure we
@ -334,6 +335,7 @@ rehash_file (struct file *from_file, const char *to_hname)
MERGE (notintermediate); MERGE (notintermediate);
MERGE (ignore_vpath); MERGE (ignore_vpath);
MERGE (snapped); MERGE (snapped);
MERGE (suffix);
#undef MERGE #undef MERGE
to_file->builtin = 0; to_file->builtin = 0;
@ -1050,7 +1052,7 @@ file_timestamp_sprintf (char *p, FILE_TIMESTAMP ts)
/* Print the data base of files. */ /* Print the data base of files. */
void static void
print_prereqs (const struct dep *deps) print_prereqs (const struct dep *deps)
{ {
const struct dep *ood = 0; const struct dep *ood = 0;
@ -1201,6 +1203,34 @@ print_file_data_base (void)
fputs (_("\n# files hash-table stats:\n# "), stdout); fputs (_("\n# files hash-table stats:\n# "), stdout);
hash_print_stats (&files, stdout); hash_print_stats (&files, stdout);
} }
static void
print_target (const void *item)
{
const struct file *f = item;
if (!f->is_target || f->suffix)
return;
/* Ignore any special targets, as defined by POSIX. */
if (f->name[0] == '.' && isupper ((unsigned char)f->name[1]))
{
const char *cp = f->name + 1;
while (*(++cp) != '\0')
if (!isupper ((unsigned char)*cp))
break;
if (*cp == '\0')
return;
}
puts (f->name);
}
void
print_targets (void)
{
hash_map (&files, print_target);
}
/* Verify the integrity of the data base of files. */ /* Verify the integrity of the data base of files. */

View File

@ -113,6 +113,7 @@ struct file
--shuffle passes through the graph. */ --shuffle passes through the graph. */
unsigned int snapped:1; /* True if the deps of this file have been unsigned int snapped:1; /* True if the deps of this file have been
secondary expanded. */ secondary expanded. */
unsigned int suffix:1; /* True if this is a suffix rule. */
}; };
@ -134,8 +135,8 @@ void notice_finished_file (struct file *file);
void init_hash_files (void); void init_hash_files (void);
void verify_file_data_base (void); void verify_file_data_base (void);
char *build_target_list (char *old_list); char *build_target_list (char *old_list);
void print_prereqs (const struct dep *deps);
void print_file_data_base (void); void print_file_data_base (void);
void print_targets (void);
int try_implicit_rule (struct file *file, unsigned int depth); int try_implicit_rule (struct file *file, unsigned int depth);
int stemlen_compare (const void *v1, const void *v2); int stemlen_compare (const void *v1, const void *v2);

View File

@ -167,11 +167,16 @@ int env_overrides = 0;
int ignore_errors_flag = 0; int ignore_errors_flag = 0;
/* Nonzero means don't remake anything, just print the data base /* Nonzero means print the data base that results from reading the makefile.
that results from reading the makefile (-p). */ (-p or --print-data-base). */
int print_data_base_flag = 0; int print_data_base_flag = 0;
/* Nonzero means don't remake anything, just print a list of targets defined
by reading the makefile (--print-targets). */
int print_targets_flag = 0;
/* Nonzero means don't remake anything; just return a nonzero status /* Nonzero means don't remake anything; just return a nonzero status
if the specified targets are not up to date (-q). */ if the specified targets are not up to date (-q). */
@ -509,6 +514,7 @@ static struct command_switch switches[] =
{ CHAR_MAX+11, string, &shuffle_mode, 1, 1, 0, 0, "random", 0, "shuffle", 0 }, { CHAR_MAX+11, string, &shuffle_mode, 1, 1, 0, 0, "random", 0, "shuffle", 0 },
{ CHAR_MAX+12, string, &jobserver_style, 1, 0, 0, 0, 0, 0, "jobserver-style", 0 }, { CHAR_MAX+12, string, &jobserver_style, 1, 0, 0, 0, 0, 0, "jobserver-style", 0 },
{ WARN_OPT, strlist, &warn_flags, 1, 1, 0, 0, "warn", NULL, "warn", NULL }, { WARN_OPT, strlist, &warn_flags, 1, 1, 0, 0, "warn", NULL, "warn", NULL },
{ CHAR_MAX+14, flag, &print_targets_flag, 1, 1, 0, 0, 0, 0, "print-targets", 0 },
{ 0, 0, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL } { 0, 0, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
}; };
@ -2289,6 +2295,14 @@ main (int argc, char **argv, char **envp)
} }
} }
/* If the user wants to see a list of targets show it now then quit.
We do this before rebuilding makefiles to avoid extraneous output. */
if (print_targets_flag)
{
print_targets ();
die (EXIT_SUCCESS);
}
if (!restarts && new_files != 0) if (!restarts && new_files != 0)
{ {
const char **p; const char **p;

View File

@ -56,10 +56,6 @@ size_t max_pattern_dep_length;
struct file *suffix_file; struct file *suffix_file;
/* Maximum length of a suffix. */
static size_t maxsuffix;
/* Return the rule definition: space separated rule targets, followed by /* 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 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 space separated rule prerequisites, followed by a pipe, followed by
@ -304,7 +300,7 @@ convert_to_pattern (void)
suffixes in the .SUFFIXES target's dependencies and see if it exists. suffixes in the .SUFFIXES target's dependencies and see if it exists.
First find the longest of the suffixes. */ First find the longest of the suffixes. */
maxsuffix = 0; size_t maxsuffix = 0;
for (d = suffix_file->deps; d != 0; d = d->next) for (d = suffix_file->deps; d != 0; d = d->next)
{ {
size_t l = strlen (dep_name (d)); size_t l = strlen (dep_name (d));
@ -317,6 +313,7 @@ convert_to_pattern (void)
for (d = suffix_file->deps; d != 0; d = d->next) for (d = suffix_file->deps; d != 0; d = d->next)
{ {
struct file *f;
size_t slen; size_t slen;
/* Make a rule that is just the suffix, with no deps or commands. /* Make a rule that is just the suffix, with no deps or commands.
@ -327,14 +324,26 @@ convert_to_pattern (void)
/* Record a pattern for this suffix's null-suffix rule. */ /* Record a pattern for this suffix's null-suffix rule. */
convert_suffix_rule ("", dep_name (d), d->file->cmds); convert_suffix_rule ("", dep_name (d), d->file->cmds);
slen = strlen (dep_name (d));
memcpy (rulename, dep_name (d), slen + 1);
f = lookup_file (rulename);
if (f && f->cmds)
{
if (!f->deps)
f->suffix = 1;
else if (!posix_pedantic)
{
O (error, &f->cmds->fileinfo,
_("warning: ignoring prerequisites on suffix rule definition"));
f->suffix = 1;
}
}
/* Add every other suffix to this one and see if it exists as a /* Add every other suffix to this one and see if it exists as a
two-suffix rule. */ two-suffix rule. */
slen = strlen (dep_name (d));
memcpy (rulename, dep_name (d), slen);
for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next) for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
{ {
struct file *f;
size_t s2len; size_t s2len;
s2len = strlen (dep_name (d2)); s2len = strlen (dep_name (d2));
@ -359,10 +368,12 @@ convert_to_pattern (void)
{ {
if (posix_pedantic) if (posix_pedantic)
continue; continue;
error (&f->cmds->fileinfo, 0, O (error, &f->cmds->fileinfo,
_("warning: ignoring prerequisites on suffix rule definition")); _("warning: ignoring prerequisites on suffix rule definition"));
} }
f->suffix = 1;
if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a') if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a')
/* A suffix rule '.X.a:' generates the pattern rule '(%.o): %.X'. /* A suffix rule '.X.a:' generates the pattern rule '(%.o): %.X'.
It also generates a normal '%.a: %.X' rule below. */ It also generates a normal '%.a: %.X' rule below. */

View File

@ -51,12 +51,30 @@ run_make_test(q!
unlink('foo.baz'); unlink('foo.baz');
# SV 40657: Test #4: "Suffix rules" with deps are normal rules # SV 40657: "Suffix rules" with deps are normal rules
my $prewarn = 'warning: ignoring prerequisites on suffix rule definition'; my $prewarn = 'warning: ignoring prerequisites on suffix rule definition';
touch('foo.bar'); touch('foo.bar');
# Verify warnings for single-suffix rules
run_make_test(q!
.SUFFIXES:
.SUFFIXES: .baz
.baz: foo.bar ; @echo make $@ from $<
$X.POSIX:
!,
'X=1 .baz', "#MAKEFILE#:5: $prewarn\nmake .baz from foo.bar\n");
# In POSIX mode we don't get a warning
run_make_test(undef, 'X= .baz', "make .baz from foo.bar\n");
# Test double-suffix rules
run_make_test(q! run_make_test(q!
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .biz .baz .SUFFIXES: .biz .baz
@ -67,25 +85,25 @@ $X.POSIX:
!, !,
'X=1 .baz.biz', "#MAKEFILE#:7: $prewarn\nmake .baz.biz from foo.bar\n"); 'X=1 .baz.biz', "#MAKEFILE#:7: $prewarn\nmake .baz.biz from foo.bar\n");
# SV 40657: Test #5: In POSIX mode we don't get a warning # SV 40657: In POSIX mode we don't get a warning
run_make_test(undef, 'X= .baz.biz', "make .baz.biz from foo.bar\n"); run_make_test(undef, 'X= .baz.biz', "make .baz.biz from foo.bar\n");
unlink('foo.bar'); unlink('foo.bar');
# SV 40657: Test #6: In POSIX mode, no pattern rules should be created # SV 40657: In POSIX mode, no pattern rules should be created
utouch(-20, 'foo.baz'); utouch(-20, 'foo.baz');
run_make_test(undef, run_make_test(undef,
'X= foo.biz', "#MAKE#: *** No rule to make target 'foo.biz'. Stop.\n", 512); 'X= foo.biz', "#MAKE#: *** No rule to make target 'foo.biz'. Stop.\n", 512);
# SV 40657: Test #7: In Non-POSIX mode, a pattern rule is created # SV 40657: In Non-POSIX mode, a pattern rule is created
run_make_test(undef, run_make_test(undef,
'X=1 foo.biz', "#MAKEFILE#:7: $prewarn\nmake foo.biz from foo.baz\n"); 'X=1 foo.biz', "#MAKEFILE#:7: $prewarn\nmake foo.biz from foo.baz\n");
# SV 40657: Test #8: ... but any prerequisites are ignored # SV 40657: ... but any prerequisites are ignored
utouch(-10, 'foo.biz'); utouch(-10, 'foo.biz');
touch('foo.bar'); touch('foo.bar');

View File

@ -0,0 +1,32 @@
# -*-perl-*-
$description = "Test the --print-targets option to GNU Make.";
# Define various things and verify the output
run_make_test(q!
.PHONY: all
all: ;@:
# "special" target
.BOGUS: ;@:
# Check various forms of suffix rule
.SUFFIXES: .q
.q: ;@:
.c.o: ;@:
# Not a suffix rule
.x.z: ;@:
# Verify included files aren't built / don't fail
include badfile
include goodfile
submake: ; $(MAKE) all
always: ; +echo always
goodfile: ; touch goodfile
!,
"--print-targets", "submake\n.x.z\nalways\nall\ngoodfile\n");
1;