From 31036e648f4a92ae0cce215eb3d60a1311a09c60 Mon Sep 17 00:00:00 2001 From: Paul Smith <psmith@gnu.org> Date: Mon, 8 Jan 2024 23:14:57 -0500 Subject: [PATCH] [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. --- NEWS | 9 ++++ doc/make.1 | 9 +++- doc/make.texi | 65 +++++++++++++++++------------ src/file.c | 32 +++++++++++++- src/filedef.h | 3 +- src/main.c | 18 +++++++- src/rule.c | 33 ++++++++++----- tests/scripts/features/suffixrules | 28 ++++++++++--- tests/scripts/options/print-targets | 32 ++++++++++++++ 9 files changed, 181 insertions(+), 48 deletions(-) create mode 100644 tests/scripts/options/print-targets diff --git a/NEWS b/NEWS index 56358ec4..7d296ac7 100644 --- a/NEWS +++ b/NEWS @@ -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 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 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 @@ -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 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 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 diff --git a/doc/make.1 b/doc/make.1 index 342807fe..bb394bde 100644 --- a/doc/make.1 +++ b/doc/make.1 @@ -270,10 +270,15 @@ reading the makefiles; then execute as usual or as otherwise specified. This also prints the version information given by the .B \-v -switch (see below). -To print the data base without trying to remake any files, use +switch (see below). To print the built-in data base only, use .IR "make \-p \-f/dev/null" . .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 ``Question mode''. Do not run any commands, or print anything; just return an exit status diff --git a/doc/make.texi b/doc/make.texi index 90ecb74e..21bee987 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -9677,15 +9677,28 @@ performed. @xref{Parallel Output, ,Output During Parallel Execution}. @cindex @code{--print-data-base} @cindex data base of @code{make} rules @cindex predefined rules and variables, printing -Print the data base (rules and variable values) that results from -reading the makefiles; then execute as usual or as otherwise -specified. This also prints the version information given by the -@samp{-v} switch (see below). To print the data base without trying -to remake any files, use @w{@samp{make -qp}}. To print the data base -of predefined rules and variables, use @w{@samp{make -p -f /dev/null}}. -The data base output contains file name and line number information for -recipe and variable definitions, so it can be a useful debugging tool -in complex environments. +Print the data base (rules and variable values) that results from reading the +makefiles; then execute as usual or as otherwise specified. This also prints +the version information given by the @samp{-v} switch (see below). To print +the data base without trying to remake any files, use @w{@samp{make -qp}}. To +print the data base of predefined rules and variables, use @w{@samp{make -p -f +/dev/null}}. The data base output contains file name and line number +information for recipe and variable definitions, so it can be a useful +debugging tool 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 @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 @cindex old-fashioned suffix rules @cindex suffix rule +@cindex inference rule -@dfn{Suffix rules} are the old-fashioned way of defining implicit rules for -@code{make}. Suffix rules are obsolete because pattern rules are more -general and clearer. They are supported in GNU @code{make} for -compatibility with old makefiles. They come in two kinds: -@dfn{double-suffix} and @dfn{single-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}. +@dfn{Suffix rules} (called @dfn{inference rules} in POSIX) are an +old-fashioned way of defining implicit rules for @code{make}. Suffix rules +are less powerful and harder to use than pattern rules (@pxref{Pattern Rules, +,Defining and Redefining Pattern Rules}); they are supported by GNU Make +primarily for POSIX compatibility. They come in two flavors: +@dfn{single-suffix} and @dfn{double-suffix}. A single-suffix rule is defined by a single suffix, which is the source -suffix. It matches any file name, and the corresponding implicit -prerequisite name is made by appending the source suffix. A single-suffix -rule whose source suffix is @samp{.c} is equivalent to the pattern rule -@samp{% : %.c}. +suffix. It matches any file name, and the corresponding implicit prerequisite +name is made by appending the source suffix. A single-suffix rule whose +source suffix is @samp{.c} is equivalent to the pattern rule @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 against a defined list of known suffixes. When @code{make} sees a rule diff --git a/src/file.c b/src/file.c index 6b20cb57..77c328ca 100644 --- a/src/file.c +++ b/src/file.c @@ -26,6 +26,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "debug.h" #include "hash.h" #include "shuffle.h" +#include "rule.h" /* 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 (ignore_vpath); MERGE (snapped); + MERGE (suffix); #undef MERGE to_file->builtin = 0; @@ -1050,7 +1052,7 @@ file_timestamp_sprintf (char *p, FILE_TIMESTAMP ts) /* Print the data base of files. */ -void +static void print_prereqs (const struct dep *deps) { const struct dep *ood = 0; @@ -1201,6 +1203,34 @@ print_file_data_base (void) fputs (_("\n# files hash-table stats:\n# "), 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. */ diff --git a/src/filedef.h b/src/filedef.h index 019d420d..b2ef1a16 100644 --- a/src/filedef.h +++ b/src/filedef.h @@ -113,6 +113,7 @@ struct file --shuffle passes through the graph. */ unsigned int snapped:1; /* True if the deps of this file have been 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 verify_file_data_base (void); char *build_target_list (char *old_list); -void print_prereqs (const struct dep *deps); void print_file_data_base (void); +void print_targets (void); int try_implicit_rule (struct file *file, unsigned int depth); int stemlen_compare (const void *v1, const void *v2); diff --git a/src/main.c b/src/main.c index c9c64b54..d07f561a 100644 --- a/src/main.c +++ b/src/main.c @@ -167,11 +167,16 @@ int env_overrides = 0; int ignore_errors_flag = 0; -/* Nonzero means don't remake anything, just print the data base - that results from reading the makefile (-p). */ +/* Nonzero means print the data base that results from reading the makefile. + (-p or --print-data-base). */ 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 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+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 }, + { 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 } }; @@ -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) { const char **p; diff --git a/src/rule.c b/src/rule.c index f04dd9bf..8da27fcc 100644 --- a/src/rule.c +++ b/src/rule.c @@ -56,10 +56,6 @@ size_t max_pattern_dep_length; 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 @@ -304,7 +300,7 @@ convert_to_pattern (void) suffixes in the .SUFFIXES target's dependencies and see if it exists. First find the longest of the suffixes. */ - maxsuffix = 0; + size_t maxsuffix = 0; for (d = suffix_file->deps; d != 0; d = d->next) { 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) { + struct file *f; size_t slen; /* 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. */ 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 two-suffix rule. */ - slen = strlen (dep_name (d)); - memcpy (rulename, dep_name (d), slen); - for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next) { - struct file *f; size_t s2len; s2len = strlen (dep_name (d2)); @@ -359,10 +368,12 @@ convert_to_pattern (void) { if (posix_pedantic) continue; - error (&f->cmds->fileinfo, 0, - _("warning: ignoring prerequisites on suffix rule definition")); + O (error, &f->cmds->fileinfo, + _("warning: ignoring prerequisites on suffix rule definition")); } + f->suffix = 1; + if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a') /* A suffix rule '.X.a:' generates the pattern rule '(%.o): %.X'. It also generates a normal '%.a: %.X' rule below. */ diff --git a/tests/scripts/features/suffixrules b/tests/scripts/features/suffixrules index 5e969b2a..6c562efc 100644 --- a/tests/scripts/features/suffixrules +++ b/tests/scripts/features/suffixrules @@ -51,12 +51,30 @@ run_make_test(q! 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'; 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! .SUFFIXES: .SUFFIXES: .biz .baz @@ -67,25 +85,25 @@ $X.POSIX: !, '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"); 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'); run_make_test(undef, '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, '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'); touch('foo.bar'); diff --git a/tests/scripts/options/print-targets b/tests/scripts/options/print-targets new file mode 100644 index 00000000..d3f9e65e --- /dev/null +++ b/tests/scripts/options/print-targets @@ -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;