Add support for "::=" simple assignment operator.

The next POSIX standard will define "::=" to have the same behavior
as GNU make's ":=", so add support for this new operator.
This commit is contained in:
Paul Smith 2012-01-30 00:21:57 +00:00
parent fca11f6039
commit ef6461611b
9 changed files with 208 additions and 79 deletions

View File

@ -1,5 +1,17 @@
2012-01-29 Paul Smith <psmith@gnu.org> 2012-01-29 Paul Smith <psmith@gnu.org>
* variable.c (parse_variable_definition): New POSIX assignment ::=
Take a struct variable to return more information after parsing.
(assign_variable_definition): New parse_variable_definition() call.
* variable.h: New declaration of parse_variable_definition().
* read.c (do_define): New parse_variable_definition() call.
(parse_var_assignment): Ditto.
(get_next_mword): Parse ::= as a variable assignment.
* doc/make.texi (Flavors): Describe the new ::= syntax.
* NEWS: Mention the ::= operator.
* variable.h (struct variable): Rearrange elts to reduce struct size.
* function.c (func_file): Create a new function, $(file ...) * function.c (func_file): Create a new function, $(file ...)
* doc/make.texi (File Function): Document the $(file ..) function. * doc/make.texi (File Function): Document the $(file ..) function.
* NEWS: Announce it. * NEWS: Announce it.

6
NEWS
View File

@ -38,6 +38,12 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
interpreted as shell assignment. Change your assignment to add whitespace interpreted as shell assignment. Change your assignment to add whitespace
between the "!" and "=": "variable! = value" between the "!" and "=": "variable! = value"
* New feature: "::=" simple assignment operator as defined by POSIX in 2012.
This operator has identical functionality to ":=" in GNU make, but will be
portable to any implementation of make conforming to a sufficiently new
version of POSIX (see http://austingroupbugs.net/view.php?id=330). It is
not necessary to define the .POSIX target to access this operator.
* New feature: GNU Guile integration * New feature: GNU Guile integration
This version of GNU make can be compiled with GNU Guile integration. This version of GNU make can be compiled with GNU Guile integration.
GNU Guile serves as an embedded extension language for make. GNU Guile serves as an embedded extension language for make.

View File

@ -1399,6 +1399,7 @@ Variable definitions are parsed as follows:
@var{immediate} = @var{deferred} @var{immediate} = @var{deferred}
@var{immediate} ?= @var{deferred} @var{immediate} ?= @var{deferred}
@var{immediate} := @var{immediate} @var{immediate} := @var{immediate}
@var{immediate} ::= @var{immediate}
@var{immediate} += @var{deferred} or @var{immediate} @var{immediate} += @var{deferred} or @var{immediate}
@var{immediate} != @var{immediate} @var{immediate} != @var{immediate}
@ -1418,6 +1419,10 @@ define @var{immediate} :=
@var{immediate} @var{immediate}
endef endef
define @var{immediate} ::=
@var{immediate}
endef
define @var{immediate} += define @var{immediate} +=
@var{deferred} or @var{immediate} @var{deferred} or @var{immediate}
endef endef
@ -1429,7 +1434,7 @@ endef
For the append operator, @samp{+=}, the right-hand side is considered For the append operator, @samp{+=}, the right-hand side is considered
immediate if the variable was previously set as a simple variable immediate if the variable was previously set as a simple variable
(@samp{:=}), and deferred otherwise. (@samp{:=} or @samp{::=}), and deferred otherwise.
For the shell assignment operator, @samp{!=}, the right-hand side is For the shell assignment operator, @samp{!=}, the right-hand side is
evaluated immediately and handed to the shell. The result is stored in the evaluated immediately and handed to the shell. The result is stored in the
@ -4967,9 +4972,9 @@ all:;echo $(foo)
will echo @samp{Huh?}: @samp{$(foo)} expands to @samp{$(bar)} which will echo @samp{Huh?}: @samp{$(foo)} expands to @samp{$(bar)} which
expands to @samp{$(ugh)} which finally expands to @samp{Huh?}.@refill expands to @samp{$(ugh)} which finally expands to @samp{Huh?}.@refill
This flavor of variable is the only sort supported by other versions of This flavor of variable is the only sort supported by most other
@code{make}. It has its advantages and its disadvantages. An advantage versions of @code{make}. It has its advantages and its disadvantages.
(most would say) is that: An advantage (most would say) is that:
@example @example
CFLAGS = $(include_dirs) -O CFLAGS = $(include_dirs) -O
@ -5005,8 +5010,14 @@ variables, there is another flavor: simply expanded variables.
@cindex simply expanded variables @cindex simply expanded variables
@cindex variables, simply expanded @cindex variables, simply expanded
@cindex := @cindex :=
@cindex ::=
@dfn{Simply expanded variables} are defined by lines using @samp{:=} @dfn{Simply expanded variables} are defined by lines using @samp{:=}
(@pxref{Setting, ,Setting Variables}). or @samp{::=} (@pxref{Setting, ,Setting Variables}). Both forms are
equivalent in GNU @code{make}; however only the @samp{::=} form is
described by the POSIX standard (support for @samp{::=} was added to
the POSIX standard in 2012, so older versions of @code{make} won't
accept this form either).
The value of a simply expanded variable is scanned The value of a simply expanded variable is scanned
once and for all, expanding any references to other variables and once and for all, expanding any references to other variables and
functions, when the variable is defined. The actual value of the simply functions, when the variable is defined. The actual value of the simply
@ -5425,12 +5436,14 @@ Several variables have constant initial values.
@cindex variables, setting @cindex variables, setting
@cindex = @cindex =
@cindex := @cindex :=
@cindex ::=
@cindex ?= @cindex ?=
@cindex != @cindex !=
To set a variable from the makefile, write a line starting with the To set a variable from the makefile, write a line starting with the
variable name followed by @samp{=} or @samp{:=}. Whatever follows the variable name followed by @samp{=} @samp{:=}, or @samp{::=}. Whatever
@samp{=} or @samp{:=} on the line becomes the value. For example, follows the @samp{=}, @samp{:=}, or @samp{::=} on the line becomes the
value. For example,
@example @example
objects = main.o foo.o bar.o utils.o objects = main.o foo.o bar.o utils.o
@ -5440,10 +5453,11 @@ objects = main.o foo.o bar.o utils.o
defines a variable named @code{objects}. Whitespace around the variable defines a variable named @code{objects}. Whitespace around the variable
name and immediately after the @samp{=} is ignored. name and immediately after the @samp{=} is ignored.
Variables defined with @samp{=} are @dfn{recursively expanded} variables. Variables defined with @samp{=} are @dfn{recursively expanded}
Variables defined with @samp{:=} are @dfn{simply expanded} variables; these variables. Variables defined with @samp{:=} or @samp{::=} are
definitions can contain variable references which will be expanded before @dfn{simply expanded} variables; these definitions can contain
the definition is made. @xref{Flavors, ,The Two Flavors of Variables}. variable references which will be expanded before the definition is
made. @xref{Flavors, ,The Two Flavors of Variables}.
The variable name may contain function and variable references, which The variable name may contain function and variable references, which
are expanded when the line is read to find the actual variable name to use. are expanded when the line is read to find the actual variable name to use.
@ -5553,12 +5567,12 @@ explanation of the two flavors of variables.
When you add to a variable's value with @samp{+=}, @code{make} acts When you add to a variable's value with @samp{+=}, @code{make} acts
essentially as if you had included the extra text in the initial essentially as if you had included the extra text in the initial
definition of the variable. If you defined it first with @samp{:=}, definition of the variable. If you defined it first with @samp{:=} or
making it a simply-expanded variable, @samp{+=} adds to that @samp{::=}, making it a simply-expanded variable, @samp{+=} adds to
simply-expanded definition, and expands the new text before appending it that simply-expanded definition, and expands the new text before
to the old value just as @samp{:=} does appending it to the old value just as @samp{:=} does (see
(see @ref{Setting, ,Setting Variables}, for a full explanation of @samp{:=}). @ref{Setting, ,Setting Variables}, for a full explanation of
In fact, @samp{:=} or @samp{::=}). In fact,
@example @example
variable := value variable := value
@ -5898,13 +5912,13 @@ Multiple @var{target} values create a target-specific variable value for
each member of the target list individually. each member of the target list individually.
The @var{variable-assignment} can be any valid form of assignment; The @var{variable-assignment} can be any valid form of assignment;
recursive (@samp{=}), simple (@samp{:=}), appending (@samp{+=}), or recursive (@samp{=}), simple (@samp{:=} or @samp{::=}), appending
conditional (@samp{?=}). All variables that appear within the (@samp{+=}), or conditional (@samp{?=}). All variables that appear
@var{variable-assignment} are evaluated within the context of the within the @var{variable-assignment} are evaluated within the context
target: thus, any previously-defined target-specific variable values of the target: thus, any previously-defined target-specific variable
will be in effect. Note that this variable is actually distinct from values will be in effect. Note that this variable is actually
any ``global'' value: the two variables do not have to have the same distinct from any ``global'' value: the two variables do not have to
flavor (recursive vs.@: simple). have the same flavor (recursive vs.@: simple).
Target-specific variables have the same priority as any other makefile Target-specific variables have the same priority as any other makefile
variable. Variables provided on the command line (and in the variable. Variables provided on the command line (and in the
@ -8463,10 +8477,10 @@ makefile works by changing the variables.
When you override a variable with a command line argument, you can When you override a variable with a command line argument, you can
define either a recursively-expanded variable or a simply-expanded define either a recursively-expanded variable or a simply-expanded
variable. The examples shown above make a recursively-expanded variable. The examples shown above make a recursively-expanded
variable; to make a simply-expanded variable, write @samp{:=} instead variable; to make a simply-expanded variable, write @samp{:=} or
of @samp{=}. But, unless you want to include a variable reference or @samp{::=} instead of @samp{=}. But, unless you want to include a
function call in the @emph{value} that you specify, it makes no variable reference or function call in the @emph{value} that you
difference which kind of variable you create. specify, it makes no difference which kind of variable you create.
There is one way that the makefile can change a variable that you have There is one way that the makefile can change a variable that you have
overridden. This is to use the @code{override} directive, which is a line overridden. This is to use the @code{override} directive, which is a line
@ -11024,6 +11038,7 @@ Here is a summary of the directives GNU @code{make} recognizes:
@item define @var{variable} @item define @var{variable}
@itemx define @var{variable} = @itemx define @var{variable} =
@itemx define @var{variable} := @itemx define @var{variable} :=
@itemx define @var{variable} ::=
@itemx define @var{variable} += @itemx define @var{variable} +=
@itemx define @var{variable} ?= @itemx define @var{variable} ?=
@itemx endef @itemx endef
@ -11479,9 +11494,9 @@ prerequisites, etc., one of them depended on @var{xxx} again.
@item Recursive variable `@var{xxx}' references itself (eventually). Stop. @item Recursive variable `@var{xxx}' references itself (eventually). Stop.
This means you've defined a normal (recursive) @code{make} variable This means you've defined a normal (recursive) @code{make} variable
@var{xxx} that, when it's expanded, will refer to itself (@var{xxx}). @var{xxx} that, when it's expanded, will refer to itself (@var{xxx}).
This is not allowed; either use simply-expanded variables (@code{:=}) or This is not allowed; either use simply-expanded variables (@samp{:=}
use the append operator (@code{+=}). @xref{Using Variables, ,How to Use or @samp{::=}) or use the append operator (@samp{+=}). @xref{Using
Variables}. Variables, ,How to Use Variables}.
@item Unterminated variable reference. Stop. @item Unterminated variable reference. Stop.
This means you forgot to provide the proper closing parenthesis This means you forgot to provide the proper closing parenthesis

37
read.c
View File

@ -495,9 +495,9 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
{ {
int wlen; int wlen;
const char *p2; const char *p2;
enum variable_flavor flavor; struct variable v;
p2 = parse_variable_definition (p, &flavor); p2 = parse_variable_definition (p, &v);
/* If this is a variable assignment, we're done. */ /* If this is a variable assignment, we're done. */
if (p2) if (p2)
@ -1342,33 +1342,33 @@ static struct variable *
do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf) do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
{ {
struct variable *v; struct variable *v;
enum variable_flavor flavor; struct variable var;
struct floc defstart; struct floc defstart;
int nlevels = 1; int nlevels = 1;
unsigned int length = 100; unsigned int length = 100;
char *definition = xmalloc (length); char *definition = xmalloc (length);
unsigned int idx = 0; unsigned int idx = 0;
char *p, *var; char *p, *n;
defstart = ebuf->floc; defstart = ebuf->floc;
p = parse_variable_definition (name, &flavor); p = parse_variable_definition (name, &var);
if (p == NULL) if (p == NULL)
/* No assignment token, so assume recursive. */ /* No assignment token, so assume recursive. */
flavor = f_recursive; var.flavor = f_recursive;
else else
{ {
if (*(next_token (p)) != '\0') if (var.value[0] != '\0')
error (&defstart, _("extraneous text after `define' directive")); error (&defstart, _("extraneous text after `define' directive"));
/* Chop the string before the assignment token to get the name. */ /* Chop the string before the assignment token to get the name. */
p[flavor == f_recursive ? -1 : -2] = '\0'; var.name[var.length] = '\0';
} }
/* Expand the variable name and find the beginning (NAME) and end. */ /* Expand the variable name and find the beginning (NAME) and end. */
var = allocated_variable_expand (name); n = allocated_variable_expand (name);
name = next_token (var); name = next_token (n);
if (*name == '\0') if (name[0] == '\0')
fatal (&defstart, _("empty variable name")); fatal (&defstart, _("empty variable name"));
p = name + strlen (name) - 1; p = name + strlen (name) - 1;
while (p > name && isblank ((unsigned char)*p)) while (p > name && isblank ((unsigned char)*p))
@ -1439,9 +1439,10 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
else else
definition[idx - 1] = '\0'; definition[idx - 1] = '\0';
v = do_variable_definition (&defstart, name, definition, origin, flavor, 0); v = do_variable_definition (&defstart, name,
definition, origin, var.flavor, 0);
free (definition); free (definition);
free (var); free (n);
return (v); return (v);
} }
@ -2467,7 +2468,7 @@ readline (struct ebuffer *ebuf)
w_colon A colon w_colon A colon
w_dcolon A double-colon w_dcolon A double-colon
w_semicolon A semicolon w_semicolon A semicolon
w_varassign A variable assignment operator (=, :=, +=, ?=, or !=) w_varassign A variable assignment operator (=, :=, ::=, +=, ?=, or !=)
Note that this function is only used when reading certain parts of the Note that this function is only used when reading certain parts of the
makefile. Don't use it where special rules hold sway (RHS of a variable, makefile. Don't use it where special rules hold sway (RHS of a variable,
@ -2506,7 +2507,13 @@ get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
{ {
case ':': case ':':
++p; ++p;
wtype = w_dcolon; if (p[1] != '=')
wtype = w_dcolon;
else
{
wtype = w_varassign;
++p;
}
break; break;
case '=': case '=':

View File

@ -1,5 +1,8 @@
2012-01-29 Paul Smith <psmith@gnu.org> 2012-01-29 Paul Smith <psmith@gnu.org>
* scripts/variables/flavors: Add tests for ::=
* scripts/variables/define: Ditto
* scripts/functions/file: Test the new $(file ...) function. * scripts/functions/file: Test the new $(file ...) function.
2012-01-12 Paul Smith <psmith@gnu.org> 2012-01-12 Paul Smith <psmith@gnu.org>

View File

@ -30,6 +30,10 @@ define simple :=
@echo $(FOO) @echo $(FOO)
endef endef
define posix ::=
@echo $(FOO)
endef
append = @echo a append = @echo a
define append += define append +=
@ -49,10 +53,54 @@ FOO = there
all: ; $(multi) all: ; $(multi)
$(simple) $(simple)
$(posix)
$(append) $(append)
$(cond) $(cond)
', ',
'', "echo hi\nhi\nthere\nfoo\na\nb\nfirst\n"); '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n");
# TEST 1a: Various new-style define/endef, with no spaces
run_make_test('
FOO = foo
define multi=
echo hi
@echo $(FOO)
endef # this is the end
define simple:=
@echo $(FOO)
endef
define posix::=
@echo $(FOO)
endef
append = @echo a
define append+=
@echo b
endef
define cond?= # this is a conditional
@echo first
endef
define cond?=
@echo second
endef
FOO = there
all: ; $(multi)
$(simple)
$(posix)
$(append)
$(cond)
',
'', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n");
# TEST 2: define in true section of conditional (containing conditional) # TEST 2: define in true section of conditional (containing conditional)

View File

@ -73,4 +73,24 @@ all: ; @echo $(foo)
', ',
'', "Hello\n"); '', "Hello\n");
# TEST 6: Simple using POSIX syntax
run_make_test('
bar = Goodbye
foo ::= $(bar)
bar = ${ugh}
ugh = Hello
all: ; @echo $(foo)
',
'', "Goodbye\n");
# TEST 7: POSIX syntax no spaces
run_make_test('
bar = Goodbye
foo::=$(bar)
bar = ${ugh}
ugh = Hello
all: ; @echo $(foo)
',
'', "Goodbye\n");
1; 1;

View File

@ -1396,17 +1396,27 @@ do_variable_definition (const struct floc *flocp, const char *varname,
/* Parse P (a null-terminated string) as a variable definition. /* Parse P (a null-terminated string) as a variable definition.
If it is not a variable definition, return NULL. If it is not a variable definition, return NULL and the contents of *VAR
are undefined, except NAME is set to the first non-space character or NIL.
If it is a variable definition, return a pointer to the char after the If it is a variable definition, return a pointer to the char after the
assignment token and set *FLAVOR to the type of variable assignment. */ assignment token and set the following fields (only) of *VAR:
name : name of the variable (ALWAYS SET) (NOT NUL-TERMINATED!)
length : length of the variable name
value : value of the variable (nul-terminated)
flavor : flavor of the variable
Other values in *VAR are unchanged.
*/
char * char *
parse_variable_definition (const char *p, enum variable_flavor *flavor) parse_variable_definition (const char *p, struct variable *var)
{ {
int wspace = 0; int wspace = 0;
const char *e = NULL;
p = next_token (p); p = next_token (p);
var->name = (char *)p;
var->length = 0;
while (1) while (1)
{ {
@ -1451,6 +1461,7 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
if (isblank ((unsigned char)c)) if (isblank ((unsigned char)c))
{ {
wspace = 1; wspace = 1;
e = p - 1;
p = next_token (p); p = next_token (p);
c = *p; c = *p;
if (c == '\0') if (c == '\0')
@ -1461,8 +1472,10 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
if (c == '=') if (c == '=')
{ {
*flavor = f_recursive; var->flavor = f_recursive;
return (char *)p; if (! e)
e = p - 1;
break;
} }
/* Match assignment variants (:=, +=, ?=, !=) */ /* Match assignment variants (:=, +=, ?=, !=) */
@ -1471,16 +1484,16 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
switch (c) switch (c)
{ {
case ':': case ':':
*flavor = f_simple; var->flavor = f_simple;
break; break;
case '+': case '+':
*flavor = f_append; var->flavor = f_append;
break; break;
case '?': case '?':
*flavor = f_conditional; var->flavor = f_conditional;
break; break;
case '!': case '!':
*flavor = f_shell; var->flavor = f_shell;
break; break;
default: default:
/* If we skipped whitespace, non-assignments means no var. */ /* If we skipped whitespace, non-assignments means no var. */
@ -1490,17 +1503,34 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
/* Might be assignment, or might be $= or #=. Check. */ /* Might be assignment, or might be $= or #=. Check. */
continue; continue;
} }
return (char *)++p; if (! e)
e = p - 1;
++p;
break;
}
/* Check for POSIX ::= syntax */
if (c == ':')
{
/* A colon other than :=/::= is not a variable defn. */
if (*p != ':' || p[1] != '=')
return NULL;
/* POSIX allows ::= to be the same as GNU make's := */
var->flavor = f_simple;
if (! e)
e = p - 1;
p += 2;
break;
} }
else if (c == ':')
/* A colon other than := is a rule line, not a variable defn. */
return NULL;
/* If we skipped whitespace, non-assignments means no var. */ /* If we skipped whitespace, non-assignments means no var. */
if (wspace) if (wspace)
return NULL; return NULL;
} }
var->length = e - var->name;
var->value = next_token (p);
return (char *)p; return (char *)p;
} }
@ -1513,27 +1543,15 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
struct variable * struct variable *
assign_variable_definition (struct variable *v, char *line) assign_variable_definition (struct variable *v, char *line)
{ {
char *beg;
char *end;
enum variable_flavor flavor;
char *name; char *name;
beg = next_token (line); if (!parse_variable_definition (line, v))
line = parse_variable_definition (beg, &flavor);
if (!line)
return NULL; return NULL;
end = line - (flavor == f_recursive ? 1 : 2);
while (end > beg && isblank ((unsigned char)end[-1]))
--end;
line = next_token (line);
v->value = line;
v->flavor = flavor;
/* Expand the name, so "$(foo)bar = baz" works. */ /* Expand the name, so "$(foo)bar = baz" works. */
name = alloca (end - beg + 1); name = alloca (v->length + 1);
memcpy (name, beg, end - beg); memcpy (name, v->name, v->length);
name[end - beg] = '\0'; name[v->length] = '\0';
v->name = allocated_variable_expand (name); v->name = allocated_variable_expand (name);
if (v->name[0] == '\0') if (v->name[0] == '\0')

View File

@ -35,7 +35,7 @@ enum variable_origin
enum variable_flavor enum variable_flavor
{ {
f_bogus, /* Bogus (error) */ f_bogus, /* Bogus (error) */
f_simple, /* Simple definition (:=) */ f_simple, /* Simple definition (:= or ::=) */
f_recursive, /* Recursive definition (=) */ f_recursive, /* Recursive definition (=) */
f_append, /* Appending definition (+=) */ f_append, /* Appending definition (+=) */
f_conditional, /* Conditional definition (?=) */ f_conditional, /* Conditional definition (?=) */
@ -52,9 +52,9 @@ enum variable_flavor
struct variable struct variable
{ {
char *name; /* Variable name. */ char *name; /* Variable name. */
int length; /* strlen (name) */
char *value; /* Variable value. */ char *value; /* Variable value. */
struct floc fileinfo; /* Where the variable was defined. */ struct floc fileinfo; /* Where the variable was defined. */
int length; /* strlen (name) */
unsigned int recursive:1; /* Gets recursively re-evaluated. */ unsigned int recursive:1; /* Gets recursively re-evaluated. */
unsigned int append:1; /* Nonzero if an appending target-specific unsigned int append:1; /* Nonzero if an appending target-specific
variable. */ variable. */
@ -160,7 +160,7 @@ struct variable *do_variable_definition (const struct floc *flocp,
enum variable_flavor flavor, enum variable_flavor flavor,
int target_var); int target_var);
char *parse_variable_definition (const char *line, char *parse_variable_definition (const char *line,
enum variable_flavor *flavor); struct variable *v);
struct variable *assign_variable_definition (struct variable *v, char *line); struct variable *assign_variable_definition (struct variable *v, char *line);
struct variable *try_variable_definition (const struct floc *flocp, char *line, struct variable *try_variable_definition (const struct floc *flocp, char *line,
enum variable_origin origin, enum variable_origin origin,