diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 195 |
1 files changed, 176 insertions, 19 deletions
@@ -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: |