diff options
author | Paul Smith <psmith@gnu.org> | 2023-02-26 18:04:14 -0500 |
---|---|---|
committer | Paul Smith <psmith@gnu.org> | 2023-04-01 11:13:12 -0400 |
commit | 2611e1991fabe2a3ae929c6ebd4afbd4f550f306 (patch) | |
tree | 0733d2ff7f545c8ba0b4c032a309cde459dfdfce /src | |
parent | 9db74434cd34b2b875b3f9bfbab4f1e0b682e27c (diff) | |
download | make-git-2611e1991fabe2a3ae929c6ebd4afbd4f550f306.tar.gz |
Introduce a --warn command line option
Replace the singleton --warn-undefined-variables with infrastructure
to manage multiple warnings: the --warn option can take an action
"ignore", "warn", or "error" (which will apply to all warnings), or
a specific warning type and an action for that type. Multiple
options can be provided and are consolidated.
* NEWS: Announce the new option.
* doc/make.1: Document in the man page.
* doc/make.texi (Warnings): Document in the user's manual.
* Makefile.am: Add new header warning.h.
* src/warning.h: Define enum for actions and warning types, and
macros to test whether they are set. Keep the default settings
separate so that we can correctly reconstruct MAKEFLAGS.
* src/makeint.h: Remove deprecated warn_undefined_variables_flag.
* src/main.c: Create global variables to hold warning settings.
(switches): Add a new switch for --warn.
(initialize_warnings): Set the default warning actions.
(main): Call initialize_warnings().
(encode_warning_state, decode_warning_state): Convert warning states
between strings and enums.
(encode_warning_name, decode_warning_name): Convert warning names
between strings and enums.
(decode_warn_flags): Convert a --warn option into enum values. If
deprecated warn_undefined_variables_flag is set convert it to --warn.
(decode_switches): Don't remove duplicates of --warn since order
matters. Call decode_warn_flags() to handle --warn.
(define_makeflags): Special-case handling of --warn options written
to MAKEFLAGS: write out the current settings.
* src/read.c (tilde_expand): Use new warning control macros.
* src/variable.c (warn_undefined): Ditto.
* src/job.c (construct_command_argv): Ditto.
* tests/scripts/options/warn: Rename from warn-undefined-variables
and add tests for --warn.
* tests/scripts/variables/MAKEFLAGS: Expect the new behavior.
Diffstat (limited to 'src')
-rw-r--r-- | src/job.c | 9 | ||||
-rw-r--r-- | src/main.c | 195 | ||||
-rw-r--r-- | src/makeint.h | 4 | ||||
-rw-r--r-- | src/read.c | 10 | ||||
-rw-r--r-- | src/variable.c | 11 | ||||
-rw-r--r-- | src/warning.h | 60 |
6 files changed, 256 insertions, 33 deletions
@@ -27,6 +27,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "os.h" #include "dep.h" #include "shuffle.h" +#include "warning.h" /* Default shell to use. */ #if MK_OS_W32 @@ -3633,9 +3634,9 @@ construct_command_argv (char *line, char **restp, struct file *file, { struct variable *var; - /* Turn off --warn-undefined-variables while we expand SHELL and IFS. */ - int save = warn_undefined_variables_flag; - warn_undefined_variables_flag = 0; + /* Turn off undefined variables warning while we expand HOME. */ + enum warning_state save = warn_get (wt_undefined_var); + warn_set (wt_undefined_var, w_ignore); shell = allocated_expand_variable_for_file (STRING_SIZE_TUPLE ("SHELL"), file); #if MK_OS_W32 @@ -3704,7 +3705,7 @@ construct_command_argv (char *line, char **restp, struct file *file, ifs = allocated_expand_variable_for_file (STRING_SIZE_TUPLE ("IFS"), file); - warn_undefined_variables_flag = save; + warn_set (wt_undefined_var, save); } argv = construct_command_argv_internal (line, restp, shell, shellflags, ifs, @@ -25,6 +25,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "debug.h" #include "getopt.h" #include "shuffle.h" +#include "warning.h" #include <assert.h> #if MK_OS_W32 @@ -272,10 +273,26 @@ static struct stringlist *eval_strings = 0; static int print_usage_flag = 0; +/* The default state of warnings. */ + +enum warning_state default_warnings[wt_max]; + +/* Current state of warnings. */ + +enum warning_state warnings[wt_max]; + +/* Global warning settings. */ + +enum warning_state warn_global; + +/* Command line warning flags. */ + +static struct stringlist *warn_flags = 0; + /* If nonzero, we should print a warning message for each reference to an undefined variable. */ -int warn_undefined_variables_flag; +static int warn_undefined_variables_flag; /* If nonzero, always build all targets, regardless of whether they appear out of date or not. */ @@ -393,7 +410,7 @@ static const char *const usage[] = -W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE\n\ Consider FILE to be infinitely new.\n"), N_("\ - --warn-undefined-variables Warn when an undefined variable is referenced.\n"), + --warn[=CONTROL] Control warnings for makefile issues.\n"), NULL }; @@ -439,7 +456,8 @@ struct command_switch Order matters here: this is the order MAKEFLAGS will be constructed. So be sure all simple flags (single char, no argument) come first. */ -#define TEMP_STDIN_OPT (CHAR_MAX+10) +#define TEMP_STDIN_OPT (CHAR_MAX+10) +#define WARN_OPT (CHAR_MAX+13) static struct command_switch switches[] = { @@ -498,7 +516,8 @@ static struct command_switch switches[] = { TEMP_STDIN_OPT, filename, &makefiles, 0, 0, 0, 0, 0, 0, "temp-stdin", 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 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + { WARN_OPT, strlist, &warn_flags, 1, 1, 0, 0, "warn", NULL, "warn", NULL }, + { 0, 0, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL } }; /* Secondary long names for options. */ @@ -644,6 +663,13 @@ initialize_global_hash_tables (void) hash_init_function_table (); } +static void +initialize_warnings () +{ + /* All warnings must have a default. */ + default_warnings[wt_undefined_var] = w_ignore; +} + /* This character map locate stop chars when parsing GNU makefiles. Each element is true if we should stop parsing on that character. */ @@ -858,6 +884,99 @@ decode_debug_flags (void) debug_flag = 0; } +static const char *w_state_map[w_error+1] = {NULL, "ignore", "warn", "error"}; +static const char *w_name_map[wt_max] = {"undefined-var"}; + +#define encode_warn_state(_b,_s) variable_buffer_output (_b, w_state_map[_s], strlen (w_state_map[_s])) +#define encode_warn_name(_b,_t) variable_buffer_output (_b, w_name_map[_t], strlen (w_name_map[_t])) + +static enum warning_state +decode_warn_state (const char *state, size_t length) +{ + for (enum warning_state st = w_ignore; st <= w_error; ++st) + { + size_t len = strlen (w_state_map[st]); + if (length == len && strncasecmp (state, w_state_map[st], length) == 0) + return st; + } + + return w_unset; +} + +static enum warning_type +decode_warn_name (const char *name, size_t length) +{ + for (enum warning_type wt = wt_undefined_var; wt < wt_max; ++wt) + { + size_t len = strlen (w_name_map[wt]); + if (length == len && strncasecmp (name, w_name_map[wt], length) == 0) + return wt; + } + + return wt_max; +} + +static void +decode_warn_flags () +{ + const char **pp; + + /* */ + if (warn_undefined_variables_flag) + { + warn_set (wt_undefined_var, w_warn); + warn_undefined_variables_flag = 0; + } + + if (warn_flags) + for (pp=warn_flags->list; *pp; ++pp) + { + const char *p = *pp; + + while (*p != '\0') + { + enum warning_state state; + /* See if the value is comma-separated. */ + const char *ep = strchr (p, ','); + if (!ep) + ep = p + strlen (p); + + /* If the value is just a state set it globally. */ + state = decode_warn_state (p, ep - p); + if (state != w_unset) + warn_global = state; + else + { + enum warning_type type; + const char *cp = memchr (p, ':', ep - p); + if (!cp) + cp = ep; + type = decode_warn_name (p, cp - p); + if (type == wt_max) + ONS (fatal, NILF, + _("unknown warning '%.*s'"), (int)(cp - p), p); + + /* If there's a warning state, decode it. */ + if (cp == ep) + state = w_warn; + else + { + ++cp; + state = decode_warn_state (cp, ep - cp); + if (state == w_unset) + ONS (fatal, NILF, + _("unknown warning state '%.*s'"), (int)(ep - cp), cp); + } + warn_set (type, state); + } + + p = ep; + while (*p == ',') + ++p; + } + } +} + static void decode_output_sync_flags (void) { @@ -1198,7 +1317,9 @@ main (int argc, char **argv, char **envp) output_init (&make_sync); - initialize_stopchar_map(); + initialize_stopchar_map (); + + initialize_warnings (); #ifdef SET_STACK_SIZE /* Get rid of any avoidable limit on stack size. */ @@ -3205,8 +3326,9 @@ decode_switches (int argc, const char **argv, enum variable_origin origin) } /* Filter out duplicate options. - * Allow duplicate makefiles for backward compatibility. */ - if (cs->c != 'f') + Order matters for warnings. + Allow duplicate makefiles for backward compatibility. */ + if (cs->c != 'f' && cs->c != WARN_OPT) { unsigned int k; for (k = 0; k < sl->idx; ++k) @@ -3317,6 +3439,7 @@ decode_switches (int argc, const char **argv, enum variable_origin origin) /* If there are any options that need to be decoded do it now. */ decode_debug_flags (); + decode_warn_flags (); decode_output_sync_flags (); /* Perform any special switch handling. */ @@ -3527,20 +3650,54 @@ define_makeflags (int makefile) case filename: case strlist: - { - struct stringlist *sl = *(struct stringlist **) cs->value_ptr; - if (sl != 0) - { - unsigned int i; - for (i = 0; i < sl->idx; ++i) + if (cs->c == WARN_OPT) + { + enum warning_type wt; + char sp = '='; + + /* See if any warning options are set. */ + for (wt = 0; wt < wt_max; ++wt) + if (warnings[wt] != w_unset) + break; + if (wt == wt_max && warn_global == w_unset) + break; + + /* Something is set so construct a --warn option. */ + fp = variable_buffer_output(fp, STRING_SIZE_TUPLE (" --warn")); + if (wt == wt_max && warn_global == w_warn) + break; + + if (warn_global > w_unset) + { + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_state (fp, warn_global); + } + for (wt = 0; wt < wt_max; ++wt) + if (warnings[wt] > w_unset) { - ADD_OPT (cs); - if (!short_option (cs->c)) - fp = variable_buffer_output (fp, "=", 1); - fp = variable_buffer_output (fp, sl->list[i], strlen (sl->list[i])); + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_name (fp, wt); + if (warnings[wt] != w_warn) + fp = encode_warn_state (variable_buffer_output(fp, ":", 1), warnings[wt]); } - } - } + } + else + { + struct stringlist *sl = *(struct stringlist **) cs->value_ptr; + if (sl != 0) + { + unsigned int i; + for (i = 0; i < sl->idx; ++i) + { + ADD_OPT (cs); + if (!short_option (cs->c)) + fp = variable_buffer_output (fp, "=", 1); + fp = variable_buffer_output (fp, sl->list[i], strlen (sl->list[i])); + } + } + } break; default: diff --git a/src/makeint.h b/src/makeint.h index 00f72190..e130e761 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -738,10 +738,10 @@ extern unsigned short stopchar_map[]; extern int just_print_flag, run_silent, ignore_errors_flag, keep_going_flag; extern int print_data_base_flag, question_flag, touch_flag, always_make_flag; extern int env_overrides, no_builtin_rules_flag, no_builtin_variables_flag; -extern int print_version_flag, check_symlink_flag, export_all_variables; -extern int warn_undefined_variables_flag, posix_pedantic; +extern int print_version_flag, check_symlink_flag, posix_pedantic; extern int not_parallel, second_expansion, clock_skew_detected; extern int rebuilding_makefiles, one_shell, output_sync, verify_flag; +extern int export_all_variables; extern unsigned long command_count; extern const char *default_shell; @@ -27,7 +27,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "rule.h" #include "debug.h" #include "hash.h" - +#include "warning.h" #if MK_OS_W32 # include <windows.h> @@ -3067,13 +3067,13 @@ tilde_expand (const char *name) int is_variable; { - /* Turn off --warn-undefined-variables while we expand HOME. */ - int save = warn_undefined_variables_flag; - warn_undefined_variables_flag = 0; + /* Turn off undefined variables warning while we expand HOME. */ + enum warning_state save = warn_get (wt_undefined_var); + warn_set (wt_undefined_var, w_ignore); home_dir = allocated_expand_variable (STRING_SIZE_TUPLE ("HOME")); - warn_undefined_variables_flag = save; + warn_set (wt_undefined_var, save); } is_variable = home_dir[0] != '\0'; diff --git a/src/variable.c b/src/variable.c index e2a529de..f1911759 100644 --- a/src/variable.c +++ b/src/variable.c @@ -30,6 +30,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "pathstuff.h" #endif #include "hash.h" +#include "warning.h" /* Incremented every time we enter target_environment(). */ unsigned long long env_recursion = 0; @@ -1868,15 +1869,19 @@ static const struct defined_vars defined_vars[] = { void warn_undefined (const char *name, size_t len) { - if (warn_undefined_variables_flag) + if (warn_check (wt_undefined_var)) { const struct defined_vars *dp; for (dp = defined_vars; dp->name != NULL; ++dp) if (dp->len == len && memcmp (dp->name, name, len) == 0) return; - error (reading_file, len, _("warning: undefined variable '%.*s'"), - (int)len, name); + if (warn_error (wt_undefined_var)) + fatal (reading_file, len, _("reference to undefined variable '%.*s'"), + (int)len, name); + else + error (reading_file, len, _("reference to undefined variable '%.*s'"), + (int)len, name); } } diff --git a/src/warning.h b/src/warning.h new file mode 100644 index 00000000..778ee407 --- /dev/null +++ b/src/warning.h @@ -0,0 +1,60 @@ +/* Control warning output in GNU Make. +Copyright (C) 2023 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Types of warnings we can show. */ +enum warning_type + { + wt_undefined_var = 0, /* Reference an undefined variable name. */ + wt_max + }; + +/* State of a given warning. */ +enum warning_state + { + w_unset = 0, + w_ignore, + w_warn, + w_error + }; + +/* The default state of warnings. */ +extern enum warning_state default_warnings[wt_max]; + +/* Current state of warnings. */ +extern enum warning_state warnings[wt_max]; + +/* Global warning settings. */ +extern enum warning_state warn_global; + +/* Get the current state of a given warning. */ +#define warn_get(_w) (warnings[_w] != w_unset ? warnings[_w] \ + : warn_global != w_unset ? warn_global \ + : default_warnings[_w]) + +/* Set the current state of a given warning. Can't use w_unset here. */ +#define warn_set(_w,_f) do{ warnings[_w] = (_f); } while (0) + +/* True if we should check for the warning in the first place. */ +#define warn_check(_w) (warn_get (_w) > w_ignore) + +/* Check if the warning is ignored. */ +#define warn_ignored(_w) (warn_get (_w) == w_ignore) + +/* Check if the warning is in "warn" mode. */ +#define warn_warned(_w) (warn_get (_w) == w_warn) + +/* Check if the warning is in "error" mode. */ +#define warn_error(_w) (warn_get (_w) == w_error) |