]> git.ipfire.org Git - thirdparty/make.git/commitdiff
[SV 64185] Clarify handling of directive lines starting with TAB
authorPaul Smith <psmith@gnu.org>
Tue, 26 Aug 2025 03:20:16 +0000 (23:20 -0400)
committerPaul Smith <psmith@gnu.org>
Tue, 26 Aug 2025 12:13:55 +0000 (08:13 -0400)
It's clear that this change causes too many problems to be made
without warning.  Revert the change disallowing conditional lines to
start with TAB.

Instead, generate warnings whenever a directive line begins with a
TAB character.  Make this change for all directives, not just
conditional directives: define, undefine, export, unexport, vpath,
load, include, etc.

* NEWS: Update the backward-compatibility warning.
* src/read.c (eval): Track whether the line starts with a TAB.
If so then whenever we recognize a directive, emit a warning.
Revert the previous change for this bug.
(parse_var_assignment): Accept a file location if the line begins
with TAB; show a warning if we discover a directive.
(conditional_line): Warn about lines starting with TAB.
* tests/scripts/...: Add tests to verify warnings for initial TAB.

NEWS
doc/make.texi
src/read.c
tests/scripts/features/conditionals
tests/scripts/features/include
tests/scripts/features/load
tests/scripts/features/override
tests/scripts/features/vpath
tests/scripts/variables/define
tests/scripts/variables/private
tests/scripts/variables/undefine

diff --git a/NEWS b/NEWS
index 3593b6e5b7b7fac811c34abe814a2f54a1d82008..fa7625ab054981ec53a2c7d50e1f29e876bd87d1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -47,6 +47,16 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
   when combined with command-line settings or -e overrides.
   See https://savannah.gnu.org/bugs/index.php?64822
 
+* WARNING: Backward-incompatibility!
+  The documentation has always stated that makefile directive lines cannot
+  begin with a TAB character.  However, the parser was lenient and allowed
+  initial TAB characters outside of a recipe context.  Unfortunately this
+  leads to unresolvable parsing errors in some situations.
+  This version of GNU Make will continue to parse directives as before, but
+  will print a warning whenever a makefile directive line begins with a TAB.
+  In a future version of GNU Make a line beginning with a TAB character will
+  never be allowed to contain a makefile directive.
+
 * NOTE: Deprecated behavior.
   The check in GNU Make 4.3 for suffix rules with prerequisites didn't check
   single-suffix rules, only double-suffix rules.  Add the missing check.
@@ -91,10 +101,6 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
   10:43:57.570558743".  Previously it used the form "Wed May 10
   10:43:57 2023", which has less detail and is harder to compare.
 
-* Conditional statements starting with the recipe prefix were sometimes
-  interpreted in previous versions.  As per the documentation, lines starting
-  with the recipe prefix are now never considered conditional statements.
-
 * Tests in the regression test suite now are run in their own directory to
   avoid cross-contamination and allow cleanup if the tests are interrupted.
   More information is printed about failing tests.
index 8408a94eeff09c73f309b6ae1cabfa4015a97c7d..58aac28fdfb8e21f65841d6fa148a1a27f35d121 100644 (file)
@@ -1265,15 +1265,13 @@ include @var{filenames}@dots{}
 @cindex shell wildcards (in @code{include})
 @cindex wildcard, in @code{include}
 
-Extra spaces are allowed and ignored at the beginning of the line, but
-the first character must not be a tab (or the value of
-@code{.RECIPEPREFIX})---if the line begins with a tab, it will be
-considered a recipe line.  Whitespace is required between
-@code{include} and the file names, and between file names; extra
-whitespace is ignored there and at the end of the directive.  A
-comment starting with @samp{#} is allowed at the end of the line.  If
-the file names contain any variable or function references, they are
-expanded.  @xref{Using Variables, ,How to Use Variables}.
+Whitespace is allowed, and ignored, at the beginning of the line, but the
+first character may not be a tab character.  Whitespace is required between
+@code{include} and the file names, and between file names; extra whitespace is
+ignored there and at the end of the line.  A comment starting with @samp{#} is
+allowed at the end of the line.  If the file names contain any variable or
+function references, they are expanded.  @xref{Using Variables, ,How to Use
+Variables}.
 
 For example, if you have three @file{.mk} files, @file{a.mk},
 @file{b.mk}, and @file{c.mk}, and @code{$(bar)} expands to
@@ -2501,6 +2499,9 @@ particular pattern.  Thus you can supply certain search directories for
 one class of file names and other directories (or none) for other file
 names.
 
+A @code{vpath} directive can be preceded by whitespace, but the line cannot
+begin with a tab character.
+
 There are three forms of the @code{vpath} directive:
 
 @table @code
@@ -4992,6 +4993,10 @@ In both of these forms, the arguments to @code{export} and
 @code{unexport} are expanded, and so could be variables or functions
 which expand to a (list of) variable names to be (un)exported.
 
+As with all directives, @code{export} and @code{unexport} can be preceded by
+whitespace, which is ignored, but the first character in the directive line
+may not be a tab character.
+
 GNU Make will add any variable you export to the environment of all child
 processes.  However, be aware that some child processes themselves will not
 accept all types of variables.  For example, many modern shells will clean the
@@ -6582,27 +6587,29 @@ endef
 @cindex variables, defining verbatim
 
 Another way to set the value of a variable is to use the @code{define}
-directive.  This directive has an unusual syntax which allows newline
-characters to be included in the value, which is convenient for
-defining both canned sequences of commands (@pxref{Canned Recipes,
-,Defining Canned Recipes}), and also sections of makefile syntax to
-use with @code{eval} (@pxref{Eval Function}).
-
-The @code{define} directive is followed on the same line by the name
-of the variable being defined and an (optional) assignment operator,
-and nothing more.  The value to give the variable appears on the
-following lines.  The end of the value is marked by a line containing
-just the word @code{endef}.
-
-Aside from this difference in syntax, @code{define} works just like
-any other variable definition.  The variable name may contain function
-and variable references, which are expanded when the directive is read
-to find the actual variable name to use.
-
-The final newline before the @code{endef} is not included in the
-value; if you want your value to contain a trailing newline you must
-include a blank line.  For example in order to define a variable that
-contains a newline character you must use @emph{two} empty lines, not one:
+directive.  This directive allows newline characters to be included in the
+value, which is convenient for defining both canned sequences of commands
+(@pxref{Canned Recipes, ,Defining Canned Recipes}), and also sections of
+makefile syntax to use with @code{eval} (@pxref{Eval Function}).  Aside from
+the difference in syntax, @code{define} behaves like any other variable
+definition.
+
+The @code{define} directive may be preceded by whitespace, which is ignored.
+However, the line @emph{may not} begin with a tab character.  The directive is
+followed on the same line by whitespace then the name of the variable being
+defined, and an (optional) assignment operator.  The variable name may contain
+function and variable references, which are expanded when the directive is
+read to determine the final variable name to use.
+
+The value to be assigned to the variable appears on the subsequent lines.  The
+end of the value is marked by a line containing just the word @code{endef}
+(optionally preceded by whitespace, but again the line cannot begin with a tab
+character).
+
+The final newline before the @code{endef} is not included in the value; if you
+want your value to contain a trailing newline you must include a blank line.
+For example in order to define a variable that contains a newline character
+you must use @emph{two} empty lines, not one:
 
 @example
 define newline
@@ -6611,19 +6618,15 @@ define newline
 endef
 @end example
 
-You may omit the variable assignment operator if you prefer.  If
-omitted, @code{make} assumes it to be @samp{=} and creates a
-recursively-expanded variable (@pxref{Flavors, ,The Two Flavors of Variables}).
-When using a @samp{+=} operator, the value is appended to the previous
-value as with any other append operation: with a single space
+You may omit the variable assignment operator.  If omitted, @code{make}
+assumes it to be @samp{=} and creates a recursively-expanded variable
+(@pxref{Flavors, ,The Two Flavors of Variables}).  When using a @samp{+=}
+operator, the value is appended to the previous value with a single space
 separating the old and new values.
 
-You may nest @code{define} directives: @code{make} will keep track of
-nested directives and report an error if they are not all properly
-closed with @code{endef}.  Note that lines beginning with the recipe
-prefix character are considered part of a recipe, so any @code{define}
-or @code{endef} strings appearing on such a line will not be
-considered @code{make} directives.
+You may nest @code{define} directives: @code{make} will keep track of nested
+directives and report an error if any are not properly closed with
+@code{endef}.
 
 @example
 define two-lines
@@ -7216,12 +7219,16 @@ makefile.  @xref{Warnings, ,Makefile Warnings}.
 @chapter Conditional Parts of Makefiles
 
 @cindex conditionals
-A @dfn{conditional} directive causes part of a makefile to be obeyed
-or ignored depending on the values of variables.  Conditionals can
-compare the value of one variable to another, or the value of a
-variable to a constant string.  Conditionals control what @code{make}
-actually ``sees'' in the makefile, so they @emph{cannot} be used to
-control recipes at the time of execution.
+A @dfn{conditional} directive allows part of a makefile to be parsed or
+ignored based on a condition.  Conditionals can check whether @code{make}
+variables are defined, or compare the results of expanding two strings for
+equality.
+
+Conditionals are evaluated @emph{as the makefile is parsed}, so it cannot
+access values that are only known when a target is being built.  For example,
+conditions cannot be based on automatic variables (@pxref{Automatic
+Variables}), target-specific variables (@pxref{Target-specific,
+,Target-specific Variable Values}), or values set in a recipe.
 
 @menu
 * Conditional Example::         Example of a conditional
@@ -7232,12 +7239,11 @@ control recipes at the time of execution.
 @node Conditional Example
 @section Example of a Conditional
 
-The following example of a conditional tells @code{make} to use one
-set of libraries if the @code{CC} variable is @samp{gcc}, and a
-different set of libraries otherwise.  It works by controlling which
-of two recipe lines will be used for the rule.  The result is that
-@samp{CC=gcc} as an argument to @code{make} changes not only which
-compiler is used but also which libraries are linked.
+The following example causes @code{make} to use one set of libraries if the
+@code{CC} variable is @samp{gcc}, and a different set of libraries otherwise.
+It works by controlling which of two recipe lines will be used for the rule.
+The result is that @samp{CC=gcc} as an argument to @code{make} changes not
+only which compiler is used but also which libraries are linked.
 
 @example
 libs_for_gcc = -lgnu
@@ -7251,8 +7257,8 @@ else
 endif
 @end example
 
-This conditional uses three directives: one @code{ifeq}, one @code{else}
-and one @code{endif}.
+This conditional uses three directives: @code{ifeq}, @code{else}, and
+@code{endif}.
 
 The @code{ifeq} directive begins the conditional, and specifies the
 condition.  It contains two arguments, separated by a comma and surrounded
@@ -7261,10 +7267,10 @@ then they are compared.  The lines of the makefile following the
 @code{ifeq} are obeyed if the two arguments match; otherwise they are
 ignored.
 
-The @code{else} directive causes the following lines to be obeyed if the
-previous conditional failed.  In the example above, this means that the
-second alternative linking command is used whenever the first alternative
-is not used.  It is optional to have an @code{else} in a conditional.
+The @code{else} directive causes the following lines to be active if the
+previous condition was false.  In the example above, this means that the
+second alternative linking command is used whenever the first alternative is
+not used.  It is optional to have an @code{else} in a conditional.
 
 The @code{endif} directive ends the conditional.  Every conditional must
 end with an @code{endif}.  Unconditional makefile text follows.
@@ -7285,14 +7291,13 @@ foo: $(objects)
 
 @noindent
 When the variable @code{CC} has any other value, the effect is this:
-
 @example
 foo: $(objects)
         $(CC) -o foo $(objects) $(normal_libs)
 @end example
 
-Equivalent results can be obtained in another way by conditionally assigning a
-variable and then using the variable unconditionally:
+Equivalent results can be obtained by conditionally assigning a variable and
+then using the variable unconditionally:
 
 @example
 libs_for_gcc = -lgnu
@@ -7317,6 +7322,17 @@ foo: $(objects)
 @findex else
 @findex endif
 
+A conditional section begins with one of the four ``if'' directives @code{ifdef}, @code{ifndef}, @code{ifeq}, or @code{ifneq}.  Each
+conditional section must end with the @code{endif} directive.  A section may
+contain the @code{else} directive to provide an alternative section if the
+original condition is false.  The @code{else} directive can be followed (on
+the same line) by another ``if'' directive introducing a new condition.
+
+Spaces may appear, and are ignored, at the beginning of a conditional
+directive line.  However, conditional directive lines @emph{may not} start
+with a tab character.  The @code{ifeq} and @code{ifneq} directives must be
+followed by whitespace, before the conditional expression.
+
 The syntax of a simple conditional with no @code{else} is as follows:
 
 @example
@@ -7463,13 +7479,6 @@ if any, is effective.  The rules for expansion and testing of
 @var{variable-name} are identical to the @code{ifdef} directive.
 @end table
 
-Extra spaces are allowed and ignored at the beginning of the
-conditional directive line, but a tab is not allowed.  (If the line
-begins with a tab, it will be considered part of a recipe for a rule.)
-Aside from this, extra spaces or tabs may be inserted with no effect
-anywhere except within the directive name or within an argument.  A
-comment starting with @samp{#} may appear at the end of the line.
-
 The other two directives that play a part in a conditional are @code{else}
 and @code{endif}.  Each of these directives is written as one word, with no
 arguments.  Extra spaces are allowed and ignored at the beginning of the
@@ -12159,8 +12168,10 @@ or:
 load @var{object-file}(@var{symbol-name}) @dots{}
 @end example
 
-More than one object file may be loaded with a single @code{load} directive,
-and both forms of @code{load} arguments may be used in the same directive.
+Whitespace can precede the directive and is ignored, but the first character
+may not be a tab character.  More than one object file may be loaded with a
+single @code{load} directive, and both forms of @code{load} arguments may be
+used in the same directive.
 
 The file @var{object-file} is dynamically loaded by GNU @code{make}.  If
 @var{object-file} does not include a directory path then it is first looked
index e7b21275098bc1515fe0cfed786c1d6c2a0bbbc6..75e37743296bf49e2362b343c3718374897c764b 100644 (file)
@@ -137,7 +137,8 @@ static void do_undefine (char *name, enum variable_origin origin,
                          struct ebuffer *ebuf);
 static struct variable *do_define (char *name, enum variable_origin origin,
                                    struct ebuffer *ebuf);
-static int conditional_line (char *line, size_t len, const floc *flocp);
+static int conditional_line (char *line, size_t len, const floc *flocp,
+                             unsigned int initial_tab);
 static void check_specials (struct nameseq *filep, int set_default);
 static void check_special_file (struct file *filep, const floc *flocp);
 static void record_files (struct nameseq *filenames, int are_also_makes,
@@ -477,6 +478,8 @@ eval_buffer (char *buffer, const floc *flocp)
 \f
 /* Check LINE to see if it's a variable assignment or undefine.
 
+   If flocp is not NULL, then the assignment line begins with TAB.
+
    It might use one of the modifiers "export", "override", "private", or it
    might be one of the conditional tokens like "ifdef", "include", etc.
 
@@ -487,7 +490,7 @@ eval_buffer (char *buffer, const floc *flocp)
    based on the modifiers found if any, plus V_ASSIGN is 1.
  */
 static char *
-parse_var_assignment (const char *line, int targvar, struct vmodifiers *vmod)
+parse_var_assignment (const char *line, int targvar, const floc *flocp, struct vmodifiers *vmod)
 {
   const char *p;
   memset (vmod, '\0', sizeof (*vmod));
@@ -524,6 +527,8 @@ parse_var_assignment (const char *line, int targvar, struct vmodifiers *vmod)
         vmod->private_v = 1;
       else if (!targvar && word1eq ("define"))
         {
+          if (flocp)
+            O (error, flocp, _("warning: directive lines cannot start with TAB"));
           /* We can't have modifiers after 'define' */
           vmod->define_v = 1;
           p = next_token (p2);
@@ -531,6 +536,8 @@ parse_var_assignment (const char *line, int targvar, struct vmodifiers *vmod)
         }
       else if (!targvar && word1eq ("undefine"))
         {
+          if (flocp)
+            O (error, flocp, _("warning: directive lines cannot start with TAB"));
           /* We can't have modifiers after 'undefine' */
           vmod->undefine_v = 1;
           p = next_token (p2);
@@ -540,7 +547,14 @@ parse_var_assignment (const char *line, int targvar, struct vmodifiers *vmod)
         /* Not a variable or modifier: this is not a variable assignment.  */
         return (char *) line;
 
-      /* It was a modifier.  Try the next word.  */
+      /* It was a modifier.  Check for TAB and try the next word.  */
+      if (flocp)
+        {
+          O (error, flocp, _("warning: directive lines cannot start with TAB"));
+          /* Only warn about the first directive.  */
+          flocp = NULL;
+        }
+
       p = next_token (p2);
       if (*p == '\0')
         return (char *) line;
@@ -622,6 +636,7 @@ eval (struct ebuffer *ebuf, int set_default)
       char *p;
       char *p2;
       unsigned int is_rule;
+      unsigned int initial_tab;
       struct vmodifiers vmod;
 
       /* At the top of this loop, we are starting a brand new line.  */
@@ -652,30 +667,30 @@ eval (struct ebuffer *ebuf, int set_default)
                 }
             }
         }
+
       /* If this line is empty, skip it.  */
       if (line[0] == '\0')
         continue;
 
+      initial_tab = line[0] == '\t';
+
       linelen = strlen (line);
 
       /* Check for a shell command line first.
          If it is not one, we can stop treating cmd_prefix specially.  */
       if (line[0] == cmd_prefix)
         {
+          /* Ignore recipe lines in a rule with no targets.  */
           if (no_targets)
-            /* Ignore the commands in a rule with no targets.  */
-            continue;
-
-          if (ignoring)
-            /* Yep, this is a shell command, and we don't care.  */
             continue;
 
-          /* If there is no preceding rule line, don't treat this line
-             as a command, even though it begins with a recipe prefix.
-             SunOS 4 make appears to behave this way.  */
-
+          /* Only part of a recipe if it appears in a recipe context.  */
           if (filenames != 0)
             {
+              /* Are we in the un-taken leg of a conditional directive?  */
+              if (ignoring)
+                continue;
+
               if (commands_idx == 0)
                 cmds_started = ebuf->floc.lineno;
 
@@ -689,6 +704,8 @@ eval (struct ebuffer *ebuf, int set_default)
               memcpy (&commands[commands_idx], line + 1, linelen - 1);
               commands_idx += linelen - 1;
               commands[commands_idx++] = '\n';
+
+              /* This line is fully consumed.  */
               continue;
             }
         }
@@ -715,7 +732,7 @@ eval (struct ebuffer *ebuf, int set_default)
 
       /* See if this is a variable assignment.  We need to do this early, to
          allow variables with names like 'ifdef', 'export', 'private', etc.  */
-      p = parse_var_assignment (p, 0, &vmod);
+      p = parse_var_assignment (p, 0, initial_tab ? &ebuf->floc : NULL, &vmod);
       if (vmod.assign_v)
         {
           struct variable *v;
@@ -775,7 +792,7 @@ eval (struct ebuffer *ebuf, int set_default)
 
       /* Check for conditional state changes.  */
       {
-        int i = conditional_line (p, wlen, fstart);
+        int i = conditional_line (p, wlen, fstart, initial_tab);
         if (i != -2)
           {
             if (i == -1)
@@ -796,6 +813,11 @@ eval (struct ebuffer *ebuf, int set_default)
         {
           int exporting = *p == 'u' ? 0 : 1;
 
+          if (initial_tab)
+            OS (error, &ebuf->floc,
+                _("warning: %s lines cannot start with TAB"),
+                exporting ? "export" : "unexport");
+
           /* Export/unexport ends the previous rule.  */
           record_waiting_files ();
 
@@ -833,6 +855,10 @@ eval (struct ebuffer *ebuf, int set_default)
           char *vpat;
           size_t l;
 
+          if (initial_tab)
+            O (error, &ebuf->floc,
+               _("warning: vpath directive lines cannot start with TAB"));
+
           /* vpath ends the previous rule.  */
           record_waiting_files ();
 
@@ -866,6 +892,11 @@ eval (struct ebuffer *ebuf, int set_default)
              exist.  "sinclude" is an alias for this from SGI.  */
           int noerror = (p[0] != 'i');
 
+          if (initial_tab)
+            OS (error, &ebuf->floc,
+                _("warning: %s lines cannot start with TAB"),
+                *p == 'i' ? "include" : *p == '-' ? "-include" : "sinclude");
+
           /* Include ends the previous rule.  */
           record_waiting_files ();
 
@@ -920,6 +951,11 @@ eval (struct ebuffer *ebuf, int set_default)
           struct nameseq *files;
           int noerror = (p[0] == '-');
 
+          if (initial_tab)
+            OS (error, &ebuf->floc,
+                _("warning: %s lines cannot start with TAB"),
+                noerror ? "-load" : "load");
+
           /* Load ends the previous rule.  */
           record_waiting_files ();
 
@@ -1196,7 +1232,7 @@ eval (struct ebuffer *ebuf, int set_default)
             p2 = variable_buffer + l;
           }
 
-        p2 = parse_var_assignment (p2, 1, &vmod);
+        p2 = parse_var_assignment (p2, 1, NULL, &vmod);
         if (vmod.assign_v)
           {
             /* If there was a semicolon found, add it back, plus anything
@@ -1503,7 +1539,7 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
    1 if following text should be ignored.  */
 
 static int
-conditional_line (char *line, size_t len, const floc *flocp)
+conditional_line (char *line, size_t len, const floc *flocp, unsigned int initial_tab)
 {
   const char *cmdname;
   enum { c_ifdef, c_ifndef, c_ifeq, c_ifneq, c_else, c_endif } cmdtype;
@@ -1524,6 +1560,10 @@ conditional_line (char *line, size_t len, const floc *flocp)
   else
     return -2;
 
+  if (initial_tab)
+    O (error, flocp,
+       _("warning: conditional directive lines cannot start with TAB"));
+
   /* Found one: skip past it and any whitespace after it.  */
   line += len;
   NEXT_TOKEN (line);
@@ -1583,13 +1623,13 @@ conditional_line (char *line, size_t len, const floc *flocp)
          and cannot be an 'else' or 'endif'.  */
 
       /* Find the length of the next word.  */
-      for (p = line+1; ! STOP_SET (*p, MAP_SPACE|MAP_NUL); ++p)
+      for (p = line+1; ! STOP_SET (*p, MAP_BLANK|MAP_NUL); ++p)
         ;
       len = p - line;
 
       /* If it's 'else' or 'endif' or an illegal conditional, fail.  */
       if (word1eq ("else") || word1eq ("endif")
-          || conditional_line (line, len, flocp) < 0)
+          || conditional_line (line, len, flocp, 0) < 0)
         EXTRATEXT ();
       else
         {
index 7cef93089d90b2818818a4f90fade39e7ae4731c..b050bf7d8fe85b16d5d7c32a41b9f1f722b92c3b 100644 (file)
@@ -153,7 +153,59 @@ endif
 !,
               '', "one\n");
 
-# SV 64085: Ensure recipe prefixed conditionals are never considered
+# SV 64085: Ensure tab-prefixed conditional directives are warned about
+# In a future release these will become errors: according to the docs this
+# was never allowed, but unfortunately the code didn't check for it.
+my @defcond = ('ifdef', 'ifndef');
+my @eqcond = ('ifeq', 'ifneq');
+
+for my $d (@defcond) {
+    run_make_test(qq!
+blah=1
+#TAB#$d blah
+endif
+all:;
+!,
+    '', "#MAKEFILE#:3: warning: conditional directive lines cannot start with TAB\n#MAKE#: 'all' is up to date.");
+}
+
+for my $d (@eqcond) {
+    run_make_test(qq!
+blah=1
+#TAB#$d (a,b)
+endif
+all:;
+!,
+    '', "#MAKEFILE#:3: warning: conditional directive lines cannot start with TAB\n#MAKE#: 'all' is up to date.");
+}
+
+run_make_test(q!
+ifeq (a, b)
+#TAB## noop
+else
+#TAB#ifeq (a, b)
+#TAB##TAB## noop
+#TAB#endif
+endif
+
+all:
+#TAB#@echo foo
+!,
+              '', "#MAKEFILE#:5: warning: conditional directive lines cannot start with TAB\n#MAKEFILE#:7: warning: conditional directive lines cannot start with TAB\nfoo");
+
+run_make_test(q!
+all:
+
+$(info hello)
+ifdef blah
+junk:
+#TAB#else
+else
+endif
+!,
+              '', "hello\n#MAKEFILE#:7: warning: conditional directive lines cannot start with TAB\n#MAKEFILE#:8: *** only one 'else' per conditional.  Stop.",
+              512);
+
 run_make_test(q!
 blah=1
 ifdef blah
@@ -162,7 +214,8 @@ else
 endif
 all:;
 !,
-    '', "#MAKE#: 'all' is up to date.");
+              '', "#MAKEFILE#:5: warning: conditional directive lines cannot start with TAB\n#MAKEFILE#:5: *** only one 'else' per conditional.  Stop.",
+              512);
 
 run_make_test(q!
 blah=1
@@ -172,7 +225,8 @@ else
 endif
 all:;
 !,
-    '', "#MAKE#: 'all' is up to date.");
+              '', "#MAKEFILE#:5: warning: conditional directive lines cannot start with TAB\n#MAKEFILE#:6: *** extraneous 'endif'.  Stop.",
+              512);
 
 run_make_test(q!
 blah=1
@@ -182,7 +236,8 @@ else
 endif
 all:;
 !,
-    '', "#MAKE#: 'all' is up to date.");
+              '', "#MAKEFILE#:5: warning: conditional directive lines cannot start with TAB\n#MAKEFILE#:8: *** missing 'endif'.  Stop.",
+              512);
 
 run_make_test(q!
 blah=1
index c20e185b0888b1d1d26b21f193a1911688e0284e..d249e740453517e3b5bf689c2f61f94b7b84a0e5 100644 (file)
@@ -489,4 +489,18 @@ test: test.x; touch $@
 
 unlink('test.foo', 'test.x', 'test');
 
+# Verify that include prefixed by TAB gives a warning
+
+my @inctypes = ('include', '-include', 'sinclude');
+
+touch('inc.mk');
+
+for my $inc (@inctypes) {
+    run_make_test(qq!
+#TAB#$inc inc.mk
+all:;\@echo hi
+!,
+                  '', "#MAKEFILE#:2: warning: $inc lines cannot start with TAB\nhi");
+}
+
 1;
index 1c7ec570729c463e3d83be9c7996505c7af8edef..6967a5bd5edb5b757a8692818e78168ecd715d9c 100644 (file)
@@ -166,5 +166,19 @@ load |: ; echo $@
 !,
               "", "all\n");
 
+# Verify that load does not start with TAB
+
+run_make_test(q!
+#TAB#load ./testload.so
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: load lines cannot start with TAB\nhi\n");
+
+run_make_test(q!
+#TAB#-load ./testload.so
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: -load lines cannot start with TAB\nhi\n");
+
 # This tells the test driver that the perl test script executed properly.
 1;
index fff6c4e82733b1d82721b3a69e4fc71d3d213253..7652d02bf4f143cd483a8842ebef9125c2c3ab22 100644 (file)
@@ -41,5 +41,24 @@ all: ; $(foo)
 ',
               'foo=Hello', "First comes the definition.\nThen comes the override.\n");
 
+# Directive lines cannot begin with TAB
+
+run_make_test(q!
+#TAB#override FOO = bar
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nhi");
+
+run_make_test(q!
+#TAB#override#TAB#export FOO = bar
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nhi");
+
+run_make_test(q!
+    override#TAB#export FOO = bar
+all:;@echo hi
+!,
+              '', "hi");
 
 1;
index 3afffc53a3741293e9375c1bf2f6621c171241ed..1552c5ff55039f4f1bab747f395220f69aa1744a 100644 (file)
@@ -113,4 +113,12 @@ vpa/foo.x: ; @echo vpath $@
 
 rmdir("vpa");
 
+# Validate that vpath indented with TAB gives a warning
+
+run_make_test(q!
+#TAB#vpath %.h b3
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: vpath directive lines cannot start with TAB\nhi");
+
 1;
index ad978b9e2ba78ccb5efe4f0be5a9a1e3c18602a5..886ff9c268f5ad6d1648a36fb4f525ebada3619b 100644 (file)
@@ -4,7 +4,7 @@ $description = "Test define/endef variable assignments.";
 
 $details = "";
 
-# TEST 0: old-style basic define/endef
+# old-style basic define/endef
 
 run_make_test('
 define multi
@@ -16,7 +16,7 @@ all: ; $(multi)
 ',
               '', "hi\necho there\nthere\n");
 
-# TEST 1: Various new-style define/endef
+# Various new-style define/endef
 
 run_make_test('
 FOO = foo
@@ -59,7 +59,7 @@ all: ; $(multi)
 ',
               '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n");
 
-# TEST 1a: Various new-style define/endef, with no spaces
+# Various new-style define/endef, with no spaces
 
 run_make_test(q!
 FOO = foo
@@ -107,7 +107,7 @@ all: ; $(multi)
 !,
               '', "echo hi\nhi\nthere\nfoo\nfoo\nfoo\$bar\na\nb\nfirst\n");
 
-# TEST 2: define in true section of conditional (containing conditional)
+# define in true section of conditional (containing conditional)
 
 run_make_test('
 FOO = foo
@@ -126,11 +126,11 @@ all: ; @echo $(FOO)
 ',
               'BOGUS=1', "bar\n");
 
-# TEST 3: define in false section of conditional (containing conditional)
+# define in false section of conditional (containing conditional)
 
 run_make_test(undef, '', "foo\n");
 
-# TEST 4: nested define (supported?)
+# nested define (supported?)
 
 run_make_test('
 define outer
@@ -145,7 +145,7 @@ outer: ; @echo $(inner)
 ',
               '', "A = B\n");
 
-# TEST 5: NEGATIVE: Missing variable name
+# NEGATIVE: Missing variable name
 
 run_make_test('
 NAME =
@@ -156,7 +156,7 @@ all: ; @echo ouch
 ',
               '', "#MAKEFILE#:3: *** empty variable name.  Stop.\n", 512);
 
-# TEST 6: NEGATIVE: extra text after define
+# NEGATIVE: extra text after define
 
 run_make_test('
 NAME =
@@ -167,7 +167,7 @@ all: ; @echo ok
 ',
               '', "#MAKEFILE#:3: extraneous text after 'define' directive\nok\n");
 
-# TEST 7: NEGATIVE: extra text after endef
+# NEGATIVE: extra text after endef
 
 run_make_test('
 NAME =
@@ -178,7 +178,7 @@ all: ; @echo ok
 ',
               '', "#MAKEFILE#:5: extraneous text after 'endef' directive\nok\n");
 
-# TEST 8: NEGATIVE: missing endef
+# NEGATIVE: missing endef
 
 run_make_test('
 NAME =
@@ -215,9 +215,6 @@ endef$(NAME)
 # can't know anymore whether the prefix character came before the variable
 # reference or was included in the first line of the variable reference.
 
-# TEST #5
-# -------
-
 run_make_test('
 define FOO
 $(V1)echo hello
@@ -227,15 +224,9 @@ all: ; @$(FOO)
 ', '', 'hello
 world');
 
-# TEST #6
-# -------
-
 run_make_test(undef, 'V1=@ V2=@', 'hello
 world');
 
-# TEST #7
-# -------
-
 run_make_test('
 define FOO
 $(V1)echo hello
@@ -246,21 +237,13 @@ all: ; $(FOO)
 echo world
 world');
 
-# TEST #8
-# -------
-
 run_make_test(undef, 'V2=@', 'echo hello
 hello
 world');
 
-# TEST #9
-# -------
-
 run_make_test(undef, 'V1=@ V2=@', 'hello
 world');
 
-# TEST #10
-# -------
 # Test the basics; a "@" internally to the variable applies to only one line.
 # A "@" before the variable applies to the entire variable.
 
@@ -302,4 +285,59 @@ foo:;
 !,
     '', "define\n");
 
+# Verify that define indented by TAB is warned about.
+# This will be an error in future versions.
+
+run_make_test(q!
+#TAB#define foo
+  @echo foo
+endef
+
+all:;$(foo)
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nfoo\n");
+
+# The failure doesn't happen at define time for nested defines, it happens
+# at eval time
+
+run_make_test(q!
+foo = echo foo
+
+define bar
+#TAB#define foo
+#TAB#  echo bar
+#TAB#endef
+endef
+
+all:;@$(foo)
+!,
+              '', "foo\n");
+
+run_make_test(q!
+foo = echo foo
+
+define bar
+#TAB#define foo
+#TAB#  echo bar
+#TAB#endef
+endef
+
+$(eval $(bar))
+
+all:;@echo foo
+!,
+              '', "#MAKEFILE#:10: warning: directive lines cannot start with TAB\n#MAKEFILE#:10: *** missing 'endef', unterminated 'define'.  Stop.\n",
+              512);
+
+# endef indented by TAB is an error; this has always been true!
+
+run_make_test(q!
+define foo
+  @echo foo
+#TAB#endef
+
+all:;$(foo)
+!,
+              '', "#MAKEFILE#:2: *** missing 'endef', unterminated 'define'.  Stop.\n", 512);
+
 1;
index 16a34c7d01585e00c7e824e8ab1a51be58d8ecf5..d6af308abb9ba3491a440485205a05f71e6201c1 100644 (file)
@@ -174,4 +174,24 @@ b: ; @#HELPER# env FOO
 !,
               '', 'FOO=<unset>');
 
+# Directive lines cannot begin with TAB
+
+run_make_test(q!
+#TAB#private FOO = bar
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nhi");
+
+run_make_test(q!
+#TAB#private#TAB#export FOO = bar
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nhi");
+
+run_make_test(q!
+    private#TAB#export FOO = bar
+all:;@echo hi
+!,
+              '', "hi");
+
 1;
index 1732351bda751486c1d813a8560a2b8eed939bbd..601e4856a2260d936d3667986e5215a92fb698b8 100644 (file)
@@ -88,4 +88,12 @@ foo:;
 !,
     '', "undefine\n");
 
+# Directive lines cannot begin with TAB
+
+run_make_test(q!
+#TAB#undefine FOO
+all:;@echo hi
+!,
+              '', "#MAKEFILE#:2: warning: directive lines cannot start with TAB\nhi");
+
 1;