summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2023-02-26 18:04:14 -0500
committerPaul Smith <psmith@gnu.org>2023-04-01 11:13:12 -0400
commit2611e1991fabe2a3ae929c6ebd4afbd4f550f306 (patch)
tree0733d2ff7f545c8ba0b4c032a309cde459dfdfce /src
parent9db74434cd34b2b875b3f9bfbab4f1e0b682e27c (diff)
downloadmake-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.c9
-rw-r--r--src/main.c195
-rw-r--r--src/makeint.h4
-rw-r--r--src/read.c10
-rw-r--r--src/variable.c11
-rw-r--r--src/warning.h60
6 files changed, 256 insertions, 33 deletions
diff --git a/src/job.c b/src/job.c
index d7b584f5..b26cb2ca 100644
--- a/src/job.c
+++ b/src/job.c
@@ -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,
diff --git a/src/main.c b/src/main.c
index bf74ab94..221870ab 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
diff --git a/src/read.c b/src/read.c
index 2d25be6a..6913928f 100644
--- a/src/read.c
+++ b/src/read.c
@@ -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)