summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2005-05-13 12:45:30 +0000
committerPaul Smith <psmith@gnu.org>2005-05-13 12:45:30 +0000
commite50e0fdf8856fada821393af3dbd268db09c3b47 (patch)
treefde9bf944a5192b729a8faf18d5ef0085d49e22e
parent26d8d00cb77f0d71f72d4f61e7f38009dbef9715 (diff)
downloadgunmake-e50e0fdf8856fada821393af3dbd268db09c3b47.tar.gz
Implement new "if... else if... endif" semantics.
-rw-r--r--ChangeLog18
-rw-r--r--NEWS6
-rw-r--r--doc/make.texi26
-rw-r--r--read.c195
-rw-r--r--tests/ChangeLog5
-rw-r--r--tests/scripts/features/conditionals105
6 files changed, 240 insertions, 115 deletions
diff --git a/ChangeLog b/ChangeLog
index 9712532..5bd7bea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2005-05-13 Paul D. Smith <psmith@gnu.org>
+
+ Implement "if... else if... endif" syntax.
+
+ * read.c (eval): Push all checks for conditional words ("ifeq",
+ "else", etc.) down into the conditional_line() function.
+ (conditional_line): Rework to allow "else if..." clause. New
+ return value -2 for lines which are not conditionals. The
+ ignoring flag can now also be 2, which means "already parsed a
+ true branch". If that value is seen no other branch of this
+ conditional can be considered true. In the else parsing if there
+ is extra text after the else, invoke conditional_line()
+ recursively to see if it's another conditional. If not, it's an
+ error. If so, raise the conditional value to this level instead
+ of creating a new conditional nesting level. Special check for
+ "else" and "endif", which aren't allowed on the "else" line.
+ * doc/make.texi (Conditional Syntax): Document the new syntax.
+
2005-05-09 Paul D. Smith <psmith@gnu.org>
* Makefile.am (EXTRA_make_SOURCES): Add vmsjobs.c
diff --git a/NEWS b/NEWS
index 5ee75ac..7b863a5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
GNU make NEWS -*-indented-text-*-
History of user-visible changes.
- 3 March 2005
+ 13 May 2005
Copyright (C) 2002,2003,2004,2005 Free Software Foundation, Inc.
See the end for copying conditions.
@@ -34,6 +34,10 @@ Version 3.81beta3
used to resolve target files. The default behavior remains as it
always has: use the modification time of the actual target file only.
+* The "else" conditional line can now be followed by any other legal
+ conditional on the same line: this does not increase the depth of the
+ conditional nesting.
+
* All pattern-specific variables that match a given target are now used
(previously only the first match was used).
diff --git a/doc/make.texi b/doc/make.texi
index fb40d29..4ffefb9 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -5723,14 +5723,30 @@ else
endif
@end example
+or:
+
+@example
+@var{conditional-directive}
+@var{text-if-one-is-true}
+else @var{conditional-directive}
+@var{text-if-true}
+else
+@var{text-if-false}
+endif
+@end example
+
@noindent
-If the condition is true, @var{text-if-true} is used; otherwise,
-@var{text-if-false} is used instead. The @var{text-if-false} can be any
-number of lines of text.
+There can be as many ``@code{else} @var{conditional-directive}''
+clauses as necessary. Once a given condition is true,
+@var{text-if-true} is used and no other clause is used; if no
+condition is true then @var{text-if-false} is used. The
+@var{text-if-true} and @var{text-if-false} can be any number of lines
+of text.
The syntax of the @var{conditional-directive} is the same whether the
-conditional is simple or complex. There are four different directives that
-test different conditions. Here is a table of them:
+conditional is simple or complex; after an @code{else} or not. There
+are four different directives that test different conditions. Here is
+a table of them:
@table @code
@item ifeq (@var{arg1}, @var{arg2})
diff --git a/read.c b/read.c
index 404c803..40ff986 100644
--- a/read.c
+++ b/read.c
@@ -78,7 +78,9 @@ struct conditionals
{
unsigned int if_cmds; /* Depth of conditional nesting. */
unsigned int allocated; /* Elts allocated in following arrays. */
- char *ignoring; /* Are we ignoring or interepreting? */
+ char *ignoring; /* Are we ignoring or interpreting?
+ 0=interpreting, 1=not yet interpreted,
+ 2=already interpreted */
char *seen_else; /* Have we already seen an `else'? */
};
@@ -130,7 +132,7 @@ static long readline PARAMS ((struct ebuffer *ebuf));
static void do_define PARAMS ((char *name, unsigned int namelen,
enum variable_origin origin,
struct ebuffer *ebuf));
-static int conditional_line PARAMS ((char *line, const struct floc *flocp));
+static int conditional_line PARAMS ((char *line, int len, const struct floc *flocp));
static void record_files PARAMS ((struct nameseq *filenames, char *pattern, char *pattern_percent,
struct dep *deps, unsigned int cmds_started, char *commands,
unsigned int commands_idx, int two_colon,
@@ -613,17 +615,17 @@ eval (struct ebuffer *ebuf, int set_default)
ignoring anything, since they control what we will do with
following lines. */
- if (!in_ignored_define
- && (word1eq ("ifdef") || word1eq ("ifndef")
- || word1eq ("ifeq") || word1eq ("ifneq")
- || word1eq ("else") || word1eq ("endif")))
+ if (!in_ignored_define)
{
- int i = conditional_line (p, fstart);
- if (i < 0)
- fatal (fstart, _("invalid syntax in conditional"));
+ int i = conditional_line (p, len, fstart);
+ if (i != -2)
+ {
+ if (i == -1)
+ fatal (fstart, _("invalid syntax in conditional"));
- ignoring = i;
- continue;
+ ignoring = i;
+ continue;
+ }
}
if (word1eq ("endef"))
@@ -1420,67 +1422,108 @@ do_define (char *name, unsigned int namelen,
FILENAME and LINENO are the filename and line number in the
current makefile. They are used for error messages.
- Value is -1 if the line is invalid,
+ Value is -2 if the line is not a conditional at all,
+ -1 if the line is an invalid conditional,
0 if following text should be interpreted,
1 if following text should be ignored. */
static int
-conditional_line (char *line, const struct floc *flocp)
+conditional_line (char *line, int len, const struct floc *flocp)
{
- int notdef;
char *cmdname;
- register unsigned int i;
-
- if (*line == 'i')
- {
- /* It's an "if..." command. */
- notdef = line[2] == 'n';
- if (notdef)
- {
- cmdname = line[3] == 'd' ? "ifndef" : "ifneq";
- line += cmdname[3] == 'd' ? 7 : 6;
- }
- else
- {
- cmdname = line[2] == 'd' ? "ifdef" : "ifeq";
- line += cmdname[2] == 'd' ? 6 : 5;
- }
- }
+ enum { c_ifdef, c_ifndef, c_ifeq, c_ifneq, c_else, c_endif } cmdtype;
+ unsigned int i;
+ unsigned int o;
+
+ /* Compare a word, both length and contents. */
+#define word1eq(s) (len == sizeof(s)-1 && strneq (s, line, sizeof(s)-1))
+#define chkword(s, t) if (word1eq (s)) { cmdtype = (t); cmdname = (s); }
+
+ /* Make sure this line is a conditional. */
+ chkword ("ifdef", c_ifdef)
+ else chkword ("ifndef", c_ifndef)
+ else chkword ("ifeq", c_ifeq)
+ else chkword ("ifneq", c_ifneq)
+ else chkword ("else", c_else)
+ else chkword ("endif", c_endif)
else
- {
- /* It's an "else" or "endif" command. */
- notdef = line[1] == 'n';
- cmdname = notdef ? "endif" : "else";
- line += notdef ? 5 : 4;
- }
+ return -2;
+
+ /* Found one: skip past it and any whitespace after it. */
+ line = next_token (line + len);
- line = next_token (line);
+#define EXTRANEOUS() error (flocp, _("Extraneous text after `%s' directive"), cmdname)
- if (*cmdname == 'e')
+ /* An 'endif' cannot contain extra text, and reduces the if-depth by 1 */
+ if (cmdtype == c_endif)
{
if (*line != '\0')
- error (flocp, _("Extraneous text after `%s' directive"), cmdname);
- /* "Else" or "endif". */
- if (conditionals->if_cmds == 0)
+ EXTRANEOUS ();
+
+ if (!conditionals->if_cmds)
+ fatal (flocp, _("extraneous `%s'"), cmdname);
+
+ --conditionals->if_cmds;
+
+ goto DONE;
+ }
+
+ /* An 'else' statement can either be simple, or it can have another
+ conditional after it. */
+ if (cmdtype == c_else)
+ {
+ const char *p;
+
+ if (!conditionals->if_cmds)
fatal (flocp, _("extraneous `%s'"), cmdname);
- /* NOTDEF indicates an `endif' command. */
- if (notdef)
- --conditionals->if_cmds;
- else if (conditionals->seen_else[conditionals->if_cmds - 1])
- fatal (flocp, _("only one `else' per conditional"));
+
+ o = conditionals->if_cmds - 1;
+
+ if (conditionals->seen_else[o])
+ fatal (flocp, _("only one `else' per conditional"));
+
+ /* Change the state of ignorance. */
+ switch (conditionals->ignoring[o])
+ {
+ case 0:
+ /* We've just been interpreting. Never do it again. */
+ conditionals->ignoring[o] = 2;
+ break;
+ case 1:
+ /* We've never interpreted yet. Maybe this time! */
+ conditionals->ignoring[o] = 0;
+ break;
+ }
+
+ /* It's a simple 'else'. */
+ if (*line == '\0')
+ {
+ conditionals->seen_else[o] = 1;
+ goto DONE;
+ }
+
+ /* The 'else' has extra text. That text must be another conditional
+ and cannot be an 'else' or 'endif'. */
+
+ /* Find the length of the next word. */
+ for (p = line+1; *p != '\0' && !isspace ((unsigned char)*p); ++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)
+ EXTRANEOUS ();
else
- {
- /* Toggle the state of ignorance. */
- conditionals->ignoring[conditionals->if_cmds - 1]
- = !conditionals->ignoring[conditionals->if_cmds - 1];
- /* Record that we have seen an `else' in this conditional.
- A second `else' will be erroneous. */
- conditionals->seen_else[conditionals->if_cmds - 1] = 1;
- }
- for (i = 0; i < conditionals->if_cmds; ++i)
- if (conditionals->ignoring[i])
- return 1;
- return 0;
+ {
+ /* conditional_line() created a new level of conditional.
+ Raise it back to this level. */
+ if (conditionals->ignoring[o] < 2)
+ conditionals->ignoring[o] = conditionals->ignoring[o+1];
+ --conditionals->if_cmds;
+ }
+
+ goto DONE;
}
if (conditionals->allocated == 0)
@@ -1490,7 +1533,7 @@ conditional_line (char *line, const struct floc *flocp)
conditionals->seen_else = (char *) xmalloc (conditionals->allocated);
}
- ++conditionals->if_cmds;
+ o = conditionals->if_cmds++;
if (conditionals->if_cmds > conditionals->allocated)
{
conditionals->allocated += 5;
@@ -1501,25 +1544,24 @@ conditional_line (char *line, const struct floc *flocp)
}
/* Record that we have seen an `if...' but no `else' so far. */
- conditionals->seen_else[conditionals->if_cmds - 1] = 0;
+ conditionals->seen_else[o] = 0;
/* Search through the stack to see if we're already ignoring. */
- for (i = 0; i < conditionals->if_cmds - 1; ++i)
+ for (i = 0; i < o; ++i)
if (conditionals->ignoring[i])
{
- /* We are already ignoring, so just push a level
- to match the next "else" or "endif", and keep ignoring.
- We don't want to expand variables in the condition. */
- conditionals->ignoring[conditionals->if_cmds - 1] = 1;
+ /* We are already ignoring, so just push a level to match the next
+ "else" or "endif", and keep ignoring. We don't want to expand
+ variables in the condition. */
+ conditionals->ignoring[o] = 1;
return 1;
}
- if (cmdname[notdef ? 3 : 2] == 'd')
+ if (cmdtype == c_ifdef || cmdtype == c_ifndef)
{
- /* "Ifdef" or "ifndef". */
char *var;
struct variable *v;
- register char *p;
+ char *p;
/* Expand the thing we're looking up, so we can use indirect and
constructed variable names. */
@@ -1533,9 +1575,10 @@ conditional_line (char *line, const struct floc *flocp)
return -1;
var[i] = '\0';
- v = lookup_variable (var, strlen (var));
- conditionals->ignoring[conditionals->if_cmds - 1]
- = (v != 0 && *v->value != '\0') == notdef;
+ v = lookup_variable (var, i);
+
+ conditionals->ignoring[o] =
+ ((v != 0 && *v->value != '\0') == (cmdtype == c_ifndef));
free (var);
}
@@ -1553,7 +1596,7 @@ conditional_line (char *line, const struct floc *flocp)
/* Find the end of the first string. */
if (termin == ',')
{
- register int count = 0;
+ int count = 0;
for (; *line != '\0'; ++line)
if (*line == '(')
++count;
@@ -1627,13 +1670,13 @@ conditional_line (char *line, const struct floc *flocp)
*line = '\0';
line = next_token (++line);
if (*line != '\0')
- error (flocp, _("Extraneous text after `%s' directive"), cmdname);
+ EXTRANEOUS ();
s2 = variable_expand (s2);
- conditionals->ignoring[conditionals->if_cmds - 1]
- = streq (s1, s2) == notdef;
+ conditionals->ignoring[o] = (streq (s1, s2) == (cmdtype == c_ifneq));
}
+ DONE:
/* Search through the stack to see if we're ignoring. */
for (i = 0; i < conditionals->if_cmds; ++i)
if (conditionals->ignoring[i])
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 2a19918..914a67f 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,8 @@
+2005-05-13 Paul D. Smith <psmith@gnu.org>
+
+ * scripts/features/conditionals: Add tests for the new if... else
+ if... endif syntax.
+
2005-05-03 Paul D. Smith <psmith@gnu.org>
* scripts/variables/DEFAULT_GOAL: Rename DEFAULT_TARGET to
diff --git a/tests/scripts/features/conditionals b/tests/scripts/features/conditionals
index 36cba23..2ece60b 100644
--- a/tests/scripts/features/conditionals
+++ b/tests/scripts/features/conditionals
@@ -3,12 +3,7 @@ $description = "Check GNU make conditionals.";
$details = "Attempt various different flavors of GNU make conditionals.";
-open(MAKEFILE,"> $makefile");
-
-# The Contents of the MAKEFILE ...
-
-print MAKEFILE <<'EOMAKE';
-objects = foo.obj
+run_make_test('
arg1 = first
arg2 = second
arg3 = third
@@ -22,13 +17,13 @@ else
@echo arg1 NOT equal arg2
endif
-ifeq '$(arg2)' "$(arg5)"
+ifeq \'$(arg2)\' "$(arg5)"
@echo arg2 equals arg5
else
@echo arg2 NOT equal arg5
endif
-ifneq '$(arg3)' '$(arg4)'
+ifneq \'$(arg3)\' \'$(arg4)\'
@echo arg3 NOT equal arg4
else
@echo arg3 equal arg4
@@ -43,32 +38,18 @@ ifdef arg4
@echo arg4 is defined
else
@echo arg4 is NOT defined
-endif
-
-EOMAKE
-
-close(MAKEFILE);
-
-&run_make_with_options($makefile,"",&get_logfile,0);
-
-$answer = "arg1 NOT equal arg2
+endif',
+ '',
+ 'arg1 NOT equal arg2
arg2 equals arg5
arg3 NOT equal arg4
variable is undefined
-arg4 is defined
-";
-
-&compare_output($answer,&get_logfile(1));
+arg4 is defined');
# Test expansion of variables inside ifdef.
-$makefile2 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile2");
-
-print MAKEFILE <<'EOF';
-
+run_make_test('
foo = 1
FOO = foo
@@ -92,15 +73,73 @@ ifdef $(call FUNC,DEF)3
DEF3 = yes
endif
-all:; @echo DEF=$(DEF) DEF2=$(DEF2) DEF3=$(DEF3)
+all:; @echo DEF=$(DEF) DEF2=$(DEF2) DEF3=$(DEF3)',
+ '',
+ 'DEF=yes DEF2=yes DEF3=yes');
+
+
+# Test all the different "else if..." constructs
+
+run_make_test('
+arg1 = first
+arg2 = second
+arg3 = third
+arg4 = cc
+arg5 = fifth
-EOF
+result =
-close(MAKEFILE)
+ifeq ($(arg1),$(arg2))
+ result += arg1 equals arg2
+else ifeq \'$(arg2)\' "$(arg5)"
+ result += arg2 equals arg5
+else ifneq \'$(arg3)\' \'$(arg3)\'
+ result += arg3 NOT equal arg4
+else ifndef arg5
+ result += variable is undefined
+else ifdef undefined
+ result += arg4 is defined
+else
+ result += success
+endif
+
+
+all: ; @echo $(result)',
+ '',
+ 'success');
+
+
+# Test some random "else if..." construct nesting
+
+run_make_test('
+arg1 = first
+arg2 = second
+arg3 = third
+arg4 = cc
+arg5 = second
+
+ifeq ($(arg1),$(arg2))
+ $(info failed 1)
+else ifeq \'$(arg2)\' "$(arg2)"
+ ifdef undefined
+ $(info failed 2)
+ else
+ $(info success)
+ endif
+else ifneq \'$(arg3)\' \'$(arg3)\'
+ $(info failed 3)
+else ifdef arg5
+ $(info failed 4)
+else ifdef undefined
+ $(info failed 5)
+else
+ $(info failed 6)
+endif
-&run_make_with_options($makefile2,"",&get_logfile,0);
-$answer = "DEF=yes DEF2=yes DEF3=yes\n";
-&compare_output($answer,&get_logfile(1));
+.PHONY: all
+all: ; @:',
+ '',
+ 'success');
# This tells the test driver that the perl test script executed properly.