diff options
author | Paul Smith <psmith@gnu.org> | 2023-03-18 17:24:45 -0400 |
---|---|---|
committer | Paul Smith <psmith@gnu.org> | 2023-04-02 10:02:18 -0400 |
commit | a0d1e76d604df5bd006fd5ed6a69963d3c6b4d7a (patch) | |
tree | 02d739d8168207be28535f359b0a6583f231f047 /src/warning.c | |
parent | 03ecd94488b85adc38746ec3e7c2a297a522598e (diff) | |
download | make-git-a0d1e76d604df5bd006fd5ed6a69963d3c6b4d7a.tar.gz |
Add support for .WARNINGS special variable
Create a new special variable, .WARNINGS, to allow per-makefile
control over warnings. The command line settings will override
this.
Move the handling of warning flags to a new file: src/warning.c.
Allow the decode to work with generic strings, and call it from
decode_switches().
* Makefile.am: Add new file src/warning.c.
* build_w32.bat: Ditto.
* builddos.bat: Ditto.
* po/POTFILES.in: Ditto.
* src/makeint.h: #define for the .WARNINGS variable name.
* src/warning.h: Add declarations for methods moved from main.c.
Rename the enum warning_state to warning_action.
* src/warning.c: New file. Move all warning encode/decode here
from main.c.
* src/main.c: Move methods into warning.c and call those methods
instead.
(main): Set .WARNINGS as a special variable.
* src/job.c (construct_command_argv): Rename to warning_action.
* src/read.c (tilde_expand): Ditto.
* src/variable.c (set_special_var): Update warnings when the
.WARNINGS special variable is set.
* tests/scripts/options/warn: Check invalid warning options.
* tests/scripts/variables/WARNINGS: Add tests for the .WARNINGS
special variable.
Diffstat (limited to 'src/warning.c')
-rw-r--r-- | src/warning.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/warning.c b/src/warning.c new file mode 100644 index 00000000..c295fdc7 --- /dev/null +++ b/src/warning.c @@ -0,0 +1,235 @@ +/* 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/>. */ + +#include "makeint.h" +#include "warning.h" +#include "variable.h" + +/* Current action for each warning. */ +enum warning_action warnings[wt_max]; + +/* The default behavior of warnings. */ +static struct warning_data warn_default; + +/* Warning settings from the .WARNING variable. */ +static struct warning_data warn_variable; + +/* Warning settings from the command line. */ +static struct warning_data warn_flag; + +static const char *w_action_map[w_error+1] = {NULL, "ignore", "warn", "error"}; +static const char *w_name_map[wt_max] = { + "invalid-var", + "invalid-ref", + "undefined-var" + }; + +#define encode_warn_action(_b,_s) \ + variable_buffer_output (_b, w_action_map[_s], strlen (w_action_map[_s])) +#define encode_warn_name(_b,_t) \ + variable_buffer_output (_b, w_name_map[_t], strlen (w_name_map[_t])) + +static void set_warnings () +{ + /* Called whenever any warnings could change; resets the current actions. */ + for (enum warning_type wt = 0; wt < wt_max; ++wt) + warnings[wt] = + warn_flag.actions[wt] != w_unset ? warn_flag.actions[wt] + : warn_flag.global != w_unset ? warn_flag.global + : warn_variable.actions[wt] != w_unset ? warn_variable.actions[wt] + : warn_variable.global != w_unset ? warn_variable.global + : warn_default.actions[wt]; +} + +void +warn_init () +{ + memset (&warn_default, '\0', sizeof (warn_default)); + memset (&warn_variable, '\0', sizeof (warn_variable)); + memset (&warn_flag, '\0', sizeof (warn_flag)); + + /* All warnings must have a default. */ + warn_default.global = w_warn; + warn_default.actions[wt_invalid_var] = w_warn; + warn_default.actions[wt_invalid_ref] = w_warn; + warn_default.actions[wt_undefined_var] = w_ignore; + + set_warnings (); +} + +static void +init_data (struct warning_data *data) +{ + data->global = w_unset; + for (enum warning_type wt = wt_invalid_var; wt < wt_max; ++wt) + data->actions[wt] = w_unset; +} + +static enum warning_action +decode_warn_action (const char *action, size_t length) +{ + for (enum warning_action st = w_ignore; st <= w_error; ++st) + { + size_t len = strlen (w_action_map[st]); + if (length == len && strncasecmp (action, w_action_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_invalid_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; +} + +void +decode_warn_actions (const char *value, const floc *flocp) +{ + struct warning_data *data = &warn_flag; + + NEXT_TOKEN (value); + + if (flocp) + { + data = &warn_variable; + /* When a variable is set to empty, reset everything. */ + if (*value == '\0') + init_data (data); + } + + while (*value != '\0') + { + enum warning_action action; + + /* Find the end of the next warning definition. */ + const char *ep = value; + while (! STOP_SET (*ep, MAP_BLANK|MAP_COMMA|MAP_NUL)) + ++ep; + + /* If the value is just an action set it globally. */ + action = decode_warn_action (value, ep - value); + if (action != w_unset) + data->global = action; + else + { + enum warning_type type; + const char *cp = memchr (value, ':', ep - value); + if (!cp) + cp = ep; + type = decode_warn_name (value, cp - value); + if (type == wt_max) + { + int l = (int)(cp - value); + if (!flocp) + ONS (fatal, NILF, _("unknown warning '%.*s'"), l, value); + ONS (error, flocp, + _("unknown warning '%.*s': ignored"), l, value); + } + + /* If there's a warning action, decode it. */ + if (cp == ep) + action = w_warn; + else + { + ++cp; + action = decode_warn_action (cp, ep - cp); + if (action == w_unset) + { + int l = (int)(ep - cp); + if (!flocp) + ONS (fatal, NILF, _("unknown warning action '%.*s'"), l, cp); + ONS (error, flocp, + _("unknown warning action '%.*s': ignored"), l, cp); + } + } + data->actions[type] = action; + } + + value = ep; + while (STOP_SET (*value, MAP_BLANK|MAP_COMMA)) + ++value; + } + + set_warnings (); +} + +char * +encode_warn_flag (char *fp) +{ + enum warning_type wt; + char sp = '='; + + /* See if any warning options are set. */ + for (wt = 0; wt < wt_max; ++wt) + if (warn_flag.actions[wt] != w_unset) + break; + if (wt == wt_max && warn_flag.global == w_unset) + return fp; + + /* Something is set so construct a --warn option. */ + fp = variable_buffer_output (fp, STRING_SIZE_TUPLE (" --warn")); + + /* If only a global action set to warn, we're done. */ + if (wt == wt_max && warn_flag.global == w_warn) + return fp; + + /* If a global action is set, add it. */ + if (warn_flag.global > w_unset) + { + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_action (fp, warn_flag.global); + } + + /* Add any specific actions. */ + if (wt != wt_max) + for (wt = 0; wt < wt_max; ++wt) + { + enum warning_action act = warn_flag.actions[wt]; + if (act > w_unset) + { + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_name (fp, wt); + if (act != w_warn) + fp = encode_warn_action (variable_buffer_output (fp, ":", 1), act); + } + } + + return fp; +} + +void +warn_get_vardata (struct warning_data *data) +{ + memcpy (data, &warn_variable, sizeof (warn_variable)); +} + +void +warn_set_vardata (const struct warning_data *data) +{ + memcpy (&warn_variable, data, sizeof (warn_variable)); + set_warnings (); +} |