mirror of
https://github.com/mirror/make.git
synced 2025-01-13 22:00:08 +08:00
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.
This commit is contained in:
parent
9db74434cd
commit
2611e1991f
@ -37,7 +37,7 @@ make_SRCS = src/ar.c src/arscan.c src/commands.c src/commands.h \
|
||||
src/mkcustom.h src/os.h src/output.c src/output.h src/read.c \
|
||||
src/remake.c src/rule.c src/rule.h src/shuffle.h src/shuffle.c \
|
||||
src/signame.c src/strcache.c src/variable.c src/variable.h \
|
||||
src/version.c src/vpath.c
|
||||
src/version.c src/vpath.c src/warning.h
|
||||
|
||||
w32_SRCS = src/w32/pathstuff.c src/w32/w32os.c src/w32/compat/dirent.c \
|
||||
src/w32/compat/posixfcn.c src/w32/include/dirent.h \
|
||||
|
9
NEWS
9
NEWS
@ -18,7 +18,14 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
|
||||
|
||||
* WARNING: Removed AmigaOS support!
|
||||
This version of GNU Make no longer supports AmigaOS. If you need support
|
||||
for AmigaOS please use one of the older versions.
|
||||
for AmigaOS please use one of the older versions of GNU Make.
|
||||
|
||||
* New feature: Makefile warning reporting control
|
||||
A new option "--warn" controls reporting of warnings for makefiles. Actions
|
||||
can be set to "ignore", "warn", or "error". The existing warning for
|
||||
undefined variables is implemented and defaults to "ignore".
|
||||
"--warn-undefined-variables" is deprecated, and is translated to
|
||||
"--warn=undefined-vars" internally.
|
||||
|
||||
|
||||
Version 4.4.1 (26 Feb 2023)
|
||||
|
30
doc/make.1
30
doc/make.1
@ -364,8 +364,36 @@ command on the given file before running
|
||||
except that the modification time is changed only in the imagination of
|
||||
.BR make .
|
||||
.TP 0.5i
|
||||
\fB\-\-warn\fR[=\fIARG[\fR,\fIARG\fR]]
|
||||
Control warning reporting for makefiles. This option can appear multiple times.
|
||||
In case of conflicts, later settings override earlier settings.
|
||||
.I ARG
|
||||
can be an action; one of
|
||||
.IR ignore ,
|
||||
.IR warn ,
|
||||
or
|
||||
.I error
|
||||
to set the default action for all warnings, or it can be a specific warning:
|
||||
.I undefined-var
|
||||
(referencing an undefined variable). The behavior of each warning can be set
|
||||
by adding
|
||||
.BI : action
|
||||
after the warning name. If an action is not specified the default is
|
||||
.IR warn .
|
||||
If no
|
||||
.I ARG
|
||||
is provided the action for all warnings is
|
||||
.IR warn .
|
||||
If no
|
||||
.B \-\-warn
|
||||
option is provided the default action for
|
||||
.I undefined-var
|
||||
is
|
||||
.IR ignore .
|
||||
.TP 0.5i
|
||||
.B \-\-warn\-undefined\-variables
|
||||
Warn when an undefined variable is referenced.
|
||||
A deprecated alternative for
|
||||
.BR \-\-warn=undefined-var .
|
||||
.SH "EXIT STATUS"
|
||||
GNU Make exits with a status of zero if all makefiles were successfully parsed
|
||||
and no targets that were built failed. A status of one will be returned
|
||||
|
@ -310,6 +310,7 @@ How to Run @code{make}
|
||||
an alternate compiler and other things.
|
||||
* Testing:: How to proceed past some errors, to
|
||||
test compilation.
|
||||
* Warnings:: How to control reporting of makefile issues.
|
||||
* Temporary Files:: Where @code{make} keeps its temporary files.
|
||||
* Options Summary:: Summary of Options
|
||||
|
||||
@ -5170,7 +5171,7 @@ compatibility. It has the same value as @code{MAKEFLAGS} except that it
|
||||
does not contain the command line variable definitions, and it always
|
||||
begins with a hyphen unless it is empty (@code{MAKEFLAGS} begins with a
|
||||
hyphen only when it begins with an option that has no single-letter
|
||||
version, such as @samp{--warn-undefined-variables}). @code{MFLAGS} was
|
||||
version, such as @samp{--no-print-directory}). @code{MFLAGS} was
|
||||
traditionally used explicitly in the recursive @code{make} command, like
|
||||
this:
|
||||
|
||||
@ -8873,6 +8874,7 @@ determines that some target is not already up to date.
|
||||
an alternate compiler and other things.
|
||||
* Testing:: How to proceed past some errors, to
|
||||
test compilation.
|
||||
* Warnings:: How to control reporting of makefile issues.
|
||||
* Temporary Files:: Where @code{make} keeps its temporary files.
|
||||
* Options Summary:: Summary of Options
|
||||
@end menu
|
||||
@ -9265,7 +9267,7 @@ overridden. This is to use the @code{override} directive, which is a line
|
||||
that looks like this: @samp{override @var{variable} = @var{value}}
|
||||
(@pxref{Override Directive, ,The @code{override} Directive}).
|
||||
|
||||
@node Testing, Temporary Files, Overriding, Running
|
||||
@node Testing, Warnings, Overriding, Running
|
||||
@section Testing the Compilation of a Program
|
||||
@cindex testing compilation
|
||||
@cindex compilation, testing
|
||||
@ -9303,7 +9305,68 @@ program, perhaps to find several independent problems so that you can
|
||||
correct them all before the next attempt to compile. This is why Emacs'
|
||||
@kbd{M-x compile} command passes the @samp{-k} flag by default.
|
||||
|
||||
@node Temporary Files, Options Summary, Testing, Running
|
||||
@node Warnings, Temporary Files, Testing, Running
|
||||
@section Makefile Warnings
|
||||
@cindex warnings
|
||||
|
||||
GNU Make can detect some types of incorrect usage in makefiles and show
|
||||
warnings about them. Currently these issues can be detected:
|
||||
|
||||
@table @samp
|
||||
@item undefined-var
|
||||
Referencing a variable that has not been defined.
|
||||
@end table
|
||||
|
||||
When one of these incorrect usages is detected, GNU Make can perform one of
|
||||
these actions:
|
||||
|
||||
@table @samp
|
||||
@item ignore
|
||||
Ignore the usage.
|
||||
|
||||
@item warn
|
||||
Show a warning about the usage and continue processing the makefile.
|
||||
|
||||
@item error
|
||||
Show an error for the usage and immediately stop processing the makefile.
|
||||
@end table
|
||||
|
||||
The default action when no warning control options are provided for
|
||||
@samp{undefined-var} is @samp{warn}.
|
||||
|
||||
To modify this default behavior, you can use the @code{--warn} option. This
|
||||
option can be specified on the command line, or by adding it to the
|
||||
@code{MAKEFLAGS} variable (@pxref{Recursion, ,Recursive Use of @code{make}}).
|
||||
Settings added to @code{MAKEFLAGS} are only affect after the assignment
|
||||
statement.
|
||||
|
||||
The @code{--warn} option can be provided multiple times: the effects are
|
||||
cumulative with later options overriding over earlier options. When GNU Make
|
||||
provides warning settings to sub-makes, they are all combined into a single
|
||||
@code{--warn} option in @code{MAKEFLAGS}.
|
||||
|
||||
If @code{--warn} is provided with no arguments then all issues are detected
|
||||
and reported at the @samp{warn} level unless otherwise specified.
|
||||
|
||||
If one of the actions (@samp{ignore}, @samp{warn}, @samp{error}) is provided
|
||||
as an argument to @code{--warn}, then this action becomes the default for all
|
||||
warning types. For example all warnings can be disabled by using
|
||||
@code{--warn=ignore}, or all warnings can be considered fatal errors by using
|
||||
@code{--warn=error}.
|
||||
|
||||
Additionally, warning types can be specified. If the warning is listed alone,
|
||||
then that warning is enabled with the @code{warn} action: e.g.,
|
||||
@code{--warn=undefined-var} will enable @samp{undefined-var} warnings with the
|
||||
@samp{warn} action. Alternatively an action can be provided after the warning
|
||||
type, separated by a @code{:}. So @code{--warn=undefined-var:error} enables
|
||||
@samp{undefined-var} warnings with an action of @samp{error}.
|
||||
|
||||
More specific settings take precedence over the global setting. For example,
|
||||
an option @code{--warn=undefined-var:error,ignore} will set the action for
|
||||
@samp{undefined-var} warnings to @samp{error}, and the action for all other
|
||||
warnings to @samp{ignore}.
|
||||
|
||||
@node Temporary Files, Options Summary, Warnings, Running
|
||||
@section Temporary Files
|
||||
@cindex temporary files
|
||||
|
||||
@ -9756,13 +9819,19 @@ running a @code{touch} command on the given file before running
|
||||
imagination of @code{make}.
|
||||
@xref{Instead of Execution, ,Instead of Executing Recipes}.
|
||||
|
||||
|
||||
@item --warn[=@var{arg}[,@var{arg}]]
|
||||
@cindex @code{--warn}
|
||||
@cindex warnings
|
||||
Specify the handling of @ref{Warnings, ,Makefile Warnings} detected in
|
||||
makefiles.
|
||||
|
||||
@item --warn-undefined-variables
|
||||
@cindex @code{--warn-undefined-variables}
|
||||
@cindex variables, warning for undefined
|
||||
@cindex undefined variables, warning message
|
||||
Issue a warning message whenever @code{make} sees a reference to an
|
||||
undefined variable. This can be helpful when you are trying to debug
|
||||
makefiles which use variables in complex ways.
|
||||
A deprecated name for @code{--warn=undefined-var}. @xref{Warnings,
|
||||
,Makefile Warnings}.
|
||||
@end table
|
||||
|
||||
@node Implicit Rules, Archives, Running, Top
|
||||
|
@ -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,
|
||||
|
195
src/main.c
195
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:
|
||||
|
@ -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;
|
||||
|
10
src/read.c
10
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';
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
60
src/warning.h
Normal file
60
src/warning.h
Normal file
@ -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)
|
86
tests/scripts/options/warn
Normal file
86
tests/scripts/options/warn
Normal file
@ -0,0 +1,86 @@
|
||||
# -*-perl-*-
|
||||
|
||||
$description = "Test the --warn option.";
|
||||
|
||||
my %warn_test = (
|
||||
'--warn' => '', '--warn=warn' => '', '--warn=error --warn=warn' => '',
|
||||
'--warn --warn=error' => '=error',
|
||||
'--warn=ignore --warn=error --warn=ignore --warn=undefined-var' => '=ignore,undefined-var',
|
||||
'--warn=undefined-var:error --warn' => '=warn,undefined-var:error'
|
||||
);
|
||||
|
||||
# Verify the deprecated --warn-undefined-variables option
|
||||
run_make_test(q!
|
||||
$(info MF=$(MAKEFLAGS))
|
||||
all:; @#HELPER# env MAKEFLAGS
|
||||
!,
|
||||
'--warn-undefined-variables', "MF= --warn=undefined-var\nMAKEFLAGS= --warn=undefined-var");
|
||||
|
||||
# Verify parsing of --warn in various forms.
|
||||
|
||||
while (my ($f, $r) = each %warn_test) {
|
||||
run_make_test(undef, $f, "MF= --warn$r\nMAKEFLAGS= --warn$r");
|
||||
}
|
||||
|
||||
# Verify that values set in MAKEFLAGS take effect
|
||||
|
||||
while (my ($f, $r) = each %warn_test) {
|
||||
run_make_test(qq!
|
||||
MAKEFLAGS += $f
|
||||
\$(info MF=\$(MAKEFLAGS))
|
||||
all:; \@#HELPER# env MAKEFLAGS
|
||||
!,
|
||||
'', "MF= --warn$r\nMAKEFLAGS= --warn$r");
|
||||
}
|
||||
|
||||
# Verify that make's special variables don't warn even if they're not set
|
||||
run_make_test(q!
|
||||
vars := $(.VARIABLES) $(MAKECMDGOALS) $(MAKE_RESTARTS) $(CURDIR)
|
||||
vars += $(GNUMAKEFLAGS) $(MAKEFLAGS) $(MFLAGS) $(MAKE_COMMAND) $(MAKE)
|
||||
vars += $(MAKEFILE_LIST) $(MAKEOVERRIDES) $(-*-command-variables-*-)
|
||||
vars += $(.RECIPEPREFIX) $(.LOADED) $(.FEATURES)
|
||||
vars += $(SHELL) $(.SHELLFLAGS) $(MAKE_TERMOUT) $(MAKE_TERMERR)
|
||||
vars += $(.DEFAULT) $(.DEFAULT_GOAL) $(-*-eval-flags-*-) $(SUFFIXES)
|
||||
vars += $(VPATH) $(GPATH)
|
||||
all:;
|
||||
!,
|
||||
'--warn=undefined-var', "#MAKE#: 'all' is up to date.");
|
||||
|
||||
# sv 63609.
|
||||
# Test for buffer overrun in warn_undefined.
|
||||
run_make_test(q!
|
||||
all:;
|
||||
X := $(averyveryveryloooooooooooooooooooooooooooongvariablename)
|
||||
!,
|
||||
'--warn=undefined-var',
|
||||
"#MAKEFILE#:3: reference to undefined variable 'averyveryveryloooooooooooooooooooooooooooongvariablename'
|
||||
#MAKE#: 'all' is up to date.\n"
|
||||
);
|
||||
|
||||
# Check undefined variable warnings
|
||||
|
||||
# With no options or with ignore, nothing should happen
|
||||
run_make_test('
|
||||
EMPTY =
|
||||
EREF = $(EMPTY)
|
||||
UREF = $(UNDEFINED)
|
||||
|
||||
SEREF := $(EREF)
|
||||
SUREF := $(UREF)
|
||||
|
||||
all: ; @echo ref $(EREF) $(UREF)',
|
||||
'', 'ref');
|
||||
|
||||
run_make_test(undef, '--warn=undefined-var:ignore', 'ref');
|
||||
|
||||
# Check warnings
|
||||
run_make_test(undef, '--warn=undefined-var',
|
||||
"#MAKEFILE#:7: reference to undefined variable 'UNDEFINED'
|
||||
#MAKEFILE#:9: reference to undefined variable 'UNDEFINED'
|
||||
ref");
|
||||
|
||||
# Check and errors
|
||||
run_make_test(undef, '--warn=undefined-var:error',
|
||||
"#MAKEFILE#:7: *** reference to undefined variable 'UNDEFINED'. Stop.", 512);
|
||||
|
||||
1;
|
@ -1,49 +0,0 @@
|
||||
# -*-perl-*-
|
||||
|
||||
$description = "Test the --warn-undefined-variables option.";
|
||||
|
||||
$details = "Verify that warnings are printed for referencing undefined variables.";
|
||||
|
||||
# Verify that make's special variables don't warn even if they're not set
|
||||
run_make_test(q!
|
||||
vars := $(.VARIABLES) $(MAKECMDGOALS) $(MAKE_RESTARTS) $(CURDIR)
|
||||
vars += $(GNUMAKEFLAGS) $(MAKEFLAGS) $(MFLAGS) $(MAKE_COMMAND) $(MAKE)
|
||||
vars += $(MAKEFILE_LIST) $(MAKEOVERRIDES) $(-*-command-variables-*-)
|
||||
vars += $(.RECIPEPREFIX) $(.LOADED) $(.FEATURES)
|
||||
vars += $(SHELL) $(.SHELLFLAGS) $(MAKE_TERMOUT) $(MAKE_TERMERR)
|
||||
vars += $(.DEFAULT) $(.DEFAULT_GOAL) $(-*-eval-flags-*-) $(SUFFIXES)
|
||||
vars += $(VPATH) $(GPATH)
|
||||
all:;
|
||||
!,
|
||||
'--warn-undefined-variables', "#MAKE#: 'all' is up to date.");
|
||||
|
||||
# Without --warn-undefined-variables, nothing should happen
|
||||
run_make_test('
|
||||
EMPTY =
|
||||
EREF = $(EMPTY)
|
||||
UREF = $(UNDEFINED)
|
||||
|
||||
SEREF := $(EREF)
|
||||
SUREF := $(UREF)
|
||||
|
||||
all: ; @echo ref $(EREF) $(UREF)',
|
||||
'', 'ref');
|
||||
|
||||
# With --warn-undefined-variables, it should warn me
|
||||
run_make_test(undef, '--warn-undefined-variables',
|
||||
"#MAKEFILE#:7: warning: undefined variable 'UNDEFINED'
|
||||
#MAKEFILE#:9: warning: undefined variable 'UNDEFINED'
|
||||
ref");
|
||||
|
||||
# sv 63609.
|
||||
# Test for buffer overrun in warn_undefined.
|
||||
run_make_test(q!
|
||||
all:;
|
||||
X := $(averyveryverylongvariablename)
|
||||
!,
|
||||
'--warn-undefined-variables',
|
||||
"#MAKEFILE#:3: warning: undefined variable 'averyveryverylongvariablename'
|
||||
#MAKE#: 'all' is up to date.\n"
|
||||
);
|
||||
|
||||
1;
|
@ -99,7 +99,7 @@ all:; \$(info makeflags='\$(MAKEFLAGS)')
|
||||
|
||||
# Long options which take no arguments.
|
||||
# sv 62514.
|
||||
@opts = (' --no-print-directory', ' --warn-undefined-variables', ' --trace');
|
||||
@opts = (' --no-print-directory', ' --warn=undefined-var', ' --trace');
|
||||
for my $fl (@flavors) {
|
||||
for my $opt (@opts) {
|
||||
run_make_test("
|
||||
@ -139,19 +139,19 @@ all:; \$(info makeflags='\$(MAKEFLAGS)')
|
||||
# A mix of multiple flags from env, the makefile and command line.
|
||||
# Skip -L since it's not available everywhere
|
||||
for my $fl (@flavors) {
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables --trace';
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn=undefined-var --trace';
|
||||
run_make_test("
|
||||
MAKEFLAGS${fl}iknqrswd -Ilocaltmp -Ilocaltmp -Onone -Onone -l2.5 -l2.5
|
||||
all:; \$(info makeflags='\$(MAKEFLAGS)')
|
||||
",
|
||||
'-Onone -l2.5 -l2.5 -Onone -Ilocaltmp -iknqrswd -i -n -s -k -Ilocaltmp',
|
||||
"/makeflags='Bdiknqrsw -Ilocaltmp -l2.5 -Onone --trace --warn-undefined-variables'/");
|
||||
"/makeflags='Bdiknqrsw -Ilocaltmp -l2.5 -Onone --trace --warn=undefined-var'/");
|
||||
}
|
||||
|
||||
# Verify MAKEFLAGS are all available to shell function at parse time.
|
||||
for my $fl (@flavors) {
|
||||
my $answer = 'Biknqrs -Ilocaltmp -l2.5 -Onone --no-print-directory --warn-undefined-variables';
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables';
|
||||
my $answer = 'Biknqrs -Ilocaltmp -l2.5 -Onone --no-print-directory --warn=undefined-var';
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn=undefined-var';
|
||||
run_make_test("
|
||||
MAKEFLAGS${fl}iknqrsw -Ilocaltmp -Ilocaltmp -Onone -Onone -l2.5 -l2.5 --no-print-directory
|
||||
\$(info at parse time '\$(MAKEFLAGS)')
|
||||
@ -165,8 +165,8 @@ at build time makeflags='$answer'");
|
||||
|
||||
# Verify MAKEFLAGS and command line definitions are all available to shell function at parse time.
|
||||
for my $fl (@flavors) {
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables';
|
||||
my $answer = 'Biknqrs -Ilocaltmp -l2.5 -Onone --no-print-directory --warn-undefined-variables -- hello=world';
|
||||
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn=undefined-var';
|
||||
my $answer = 'Biknqrs -Ilocaltmp -l2.5 -Onone --no-print-directory --warn=undefined-var -- hello=world';
|
||||
run_make_test("
|
||||
MAKEFLAGS${fl}iknqrsw -Ilocaltmp -Ilocaltmp -Onone -Onone -l2.5 -l2.5 --no-print-directory
|
||||
\$(info at parse time '\$(MAKEFLAGS)')
|
||||
|
Loading…
Reference in New Issue
Block a user