mirror of
https://github.com/mirror/make.git
synced 2024-12-27 13:20:34 +08:00
Implement new "if... else if... endif" semantics.
This commit is contained in:
parent
26d8d00cb7
commit
e50e0fdf88
18
ChangeLog
18
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
|
||||
|
6
NEWS
6
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).
|
||||
|
||||
|
@ -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})
|
||||
|
193
read.c
193
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;
|
||||
enum { c_ifdef, c_ifndef, c_ifeq, c_ifneq, c_else, c_endif } cmdtype;
|
||||
unsigned int i;
|
||||
unsigned int o;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* 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;
|
||||
|
||||
line = next_token (line);
|
||||
/* Found one: skip past it and any whitespace after it. */
|
||||
line = next_token (line + len);
|
||||
|
||||
if (*cmdname == 'e')
|
||||
#define EXTRANEOUS() error (flocp, _("Extraneous text after `%s' directive"), cmdname)
|
||||
|
||||
/* 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);
|
||||
/* 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"));
|
||||
|
||||
--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);
|
||||
|
||||
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])
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
|
||||
EOF
|
||||
|
||||
close(MAKEFILE)
|
||||
# Test all the different "else if..." constructs
|
||||
|
||||
&run_make_with_options($makefile2,"",&get_logfile,0);
|
||||
$answer = "DEF=yes DEF2=yes DEF3=yes\n";
|
||||
&compare_output($answer,&get_logfile(1));
|
||||
run_make_test('
|
||||
arg1 = first
|
||||
arg2 = second
|
||||
arg3 = third
|
||||
arg4 = cc
|
||||
arg5 = fifth
|
||||
|
||||
result =
|
||||
|
||||
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
|
||||
|
||||
.PHONY: all
|
||||
all: ; @:',
|
||||
'',
|
||||
'success');
|
||||
|
||||
|
||||
# This tells the test driver that the perl test script executed properly.
|
||||
|
Loading…
Reference in New Issue
Block a user