From cde0c929cee66ae000b8cd9fa03640b46bac0bba Mon Sep 17 00:00:00 2001 From: psmith Date: Mon, 30 Jan 2012 00:21:57 +0000 Subject: [PATCH] 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. --- ChangeLog | 12 +++++++ NEWS | 6 ++++ doc/make.texi | 79 ++++++++++++++++++++++++----------------- read.c | 37 +++++++++++-------- tests/ChangeLog | 3 ++ tests/scripts/variables/define | 50 +++++++++++++++++++++++++- tests/scripts/variables/flavors | 20 +++++++++++ variable.c | 76 ++++++++++++++++++++++++--------------- variable.h | 6 ++-- 9 files changed, 209 insertions(+), 80 deletions(-) diff --git a/ChangeLog b/ChangeLog index facb4cc..9cbb1e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2012-01-29 Paul Smith + * 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 ...) * doc/make.texi (File Function): Document the $(file ..) function. * NEWS: Announce it. diff --git a/NEWS b/NEWS index 20b8704..b07ab98 100644 --- a/NEWS +++ b/NEWS @@ -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 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 This version of GNU make can be compiled with GNU Guile integration. GNU Guile serves as an embedded extension language for make. diff --git a/doc/make.texi b/doc/make.texi index dfc8465..e0de04d 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -4,7 +4,7 @@ @include version.texi @set EDITION 0.72 -@set RCSID $Id: make.texi,v 1.77 2012/01/29 18:12:22 psmith Exp $ +@set RCSID $Id: make.texi,v 1.78 2012/01/30 00:21:58 psmith Exp $ @settitle GNU @code{make} @setchapternewpage odd @@ -1399,6 +1399,7 @@ Variable definitions are parsed as follows: @var{immediate} = @var{deferred} @var{immediate} ?= @var{deferred} @var{immediate} := @var{immediate} +@var{immediate} ::= @var{immediate} @var{immediate} += @var{deferred} or @var{immediate} @var{immediate} != @var{immediate} @@ -1418,6 +1419,10 @@ define @var{immediate} := @var{immediate} endef +define @var{immediate} ::= + @var{immediate} +endef + define @var{immediate} += @var{deferred} or @var{immediate} endef @@ -1429,7 +1434,7 @@ endef For the append operator, @samp{+=}, the right-hand side is considered 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 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 expands to @samp{$(ugh)} which finally expands to @samp{Huh?}.@refill -This flavor of variable is the only sort supported by other versions of -@code{make}. It has its advantages and its disadvantages. An advantage -(most would say) is that: +This flavor of variable is the only sort supported by most other +versions of @code{make}. It has its advantages and its disadvantages. +An advantage (most would say) is that: @example CFLAGS = $(include_dirs) -O @@ -5005,8 +5010,14 @@ variables, there is another flavor: simply expanded variables. @cindex simply expanded variables @cindex variables, simply expanded @cindex := +@cindex ::= @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 once and for all, expanding any references to other variables and 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 = @cindex := +@cindex ::= @cindex ?= @cindex != To set a variable from the makefile, write a line starting with the -variable name followed by @samp{=} or @samp{:=}. Whatever follows the -@samp{=} or @samp{:=} on the line becomes the value. For example, +variable name followed by @samp{=} @samp{:=}, or @samp{::=}. Whatever +follows the @samp{=}, @samp{:=}, or @samp{::=} on the line becomes the +value. For example, @example 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 name and immediately after the @samp{=} is ignored. -Variables defined with @samp{=} are @dfn{recursively expanded} variables. -Variables defined with @samp{:=} are @dfn{simply expanded} variables; these -definitions can contain variable references which will be expanded before -the definition is made. @xref{Flavors, ,The Two Flavors of Variables}. +Variables defined with @samp{=} are @dfn{recursively expanded} +variables. Variables defined with @samp{:=} or @samp{::=} are +@dfn{simply expanded} variables; these definitions can contain +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 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 essentially as if you had included the extra text in the initial -definition of the variable. If you defined it first with @samp{:=}, -making it a simply-expanded variable, @samp{+=} adds to that -simply-expanded definition, and expands the new text before appending it -to the old value just as @samp{:=} does -(see @ref{Setting, ,Setting Variables}, for a full explanation of @samp{:=}). -In fact, +definition of the variable. If you defined it first with @samp{:=} or +@samp{::=}, making it a simply-expanded variable, @samp{+=} adds to +that simply-expanded definition, and expands the new text before +appending it to the old value just as @samp{:=} does (see +@ref{Setting, ,Setting Variables}, for a full explanation of +@samp{:=} or @samp{::=}). In fact, @example variable := value @@ -5898,13 +5912,13 @@ Multiple @var{target} values create a target-specific variable value for each member of the target list individually. The @var{variable-assignment} can be any valid form of assignment; -recursive (@samp{=}), simple (@samp{:=}), appending (@samp{+=}), or -conditional (@samp{?=}). All variables that appear within the -@var{variable-assignment} are evaluated within the context of the -target: thus, any previously-defined target-specific variable values -will be in effect. Note that this variable is actually distinct from -any ``global'' value: the two variables do not have to have the same -flavor (recursive vs.@: simple). +recursive (@samp{=}), simple (@samp{:=} or @samp{::=}), appending +(@samp{+=}), or conditional (@samp{?=}). All variables that appear +within the @var{variable-assignment} are evaluated within the context +of the target: thus, any previously-defined target-specific variable +values will be in effect. Note that this variable is actually +distinct from any ``global'' value: the two variables do not have to +have the same flavor (recursive vs.@: simple). Target-specific variables have the same priority as any other makefile 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 define either a recursively-expanded variable or a simply-expanded variable. The examples shown above make a recursively-expanded -variable; to make a simply-expanded variable, write @samp{:=} instead -of @samp{=}. But, unless you want to include a variable reference or -function call in the @emph{value} that you specify, it makes no -difference which kind of variable you create. +variable; to make a simply-expanded variable, write @samp{:=} or +@samp{::=} instead of @samp{=}. But, unless you want to include a +variable reference or function call in the @emph{value} that you +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 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} @itemx define @var{variable} = @itemx define @var{variable} := +@itemx define @var{variable} ::= @itemx define @var{variable} += @itemx define @var{variable} ?= @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. This means you've defined a normal (recursive) @code{make} variable @var{xxx} that, when it's expanded, will refer to itself (@var{xxx}). -This is not allowed; either use simply-expanded variables (@code{:=}) or -use the append operator (@code{+=}). @xref{Using Variables, ,How to Use -Variables}. +This is not allowed; either use simply-expanded variables (@samp{:=} +or @samp{::=}) or use the append operator (@samp{+=}). @xref{Using +Variables, ,How to Use Variables}. @item Unterminated variable reference. Stop. This means you forgot to provide the proper closing parenthesis diff --git a/read.c b/read.c index 634eb4c..4378d1b 100644 --- a/read.c +++ b/read.c @@ -495,9 +495,9 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod) { int wlen; 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 (p2) @@ -1342,33 +1342,33 @@ static struct variable * do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf) { struct variable *v; - enum variable_flavor flavor; + struct variable var; struct floc defstart; int nlevels = 1; unsigned int length = 100; char *definition = xmalloc (length); unsigned int idx = 0; - char *p, *var; + char *p, *n; defstart = ebuf->floc; - p = parse_variable_definition (name, &flavor); + p = parse_variable_definition (name, &var); if (p == NULL) /* No assignment token, so assume recursive. */ - flavor = f_recursive; + var.flavor = f_recursive; else { - if (*(next_token (p)) != '\0') + if (var.value[0] != '\0') error (&defstart, _("extraneous text after `define' directive")); /* 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. */ - var = allocated_variable_expand (name); - name = next_token (var); - if (*name == '\0') + n = allocated_variable_expand (name); + name = next_token (n); + if (name[0] == '\0') fatal (&defstart, _("empty variable name")); p = name + strlen (name) - 1; while (p > name && isblank ((unsigned char)*p)) @@ -1439,9 +1439,10 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf) else 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 (var); + free (n); return (v); } @@ -2467,7 +2468,7 @@ readline (struct ebuffer *ebuf) w_colon A colon w_dcolon A double-colon 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 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 ':': ++p; - wtype = w_dcolon; + if (p[1] != '=') + wtype = w_dcolon; + else + { + wtype = w_varassign; + ++p; + } break; case '=': diff --git a/tests/ChangeLog b/tests/ChangeLog index 3e0643c..7ad4085 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,8 @@ 2012-01-29 Paul Smith + * scripts/variables/flavors: Add tests for ::= + * scripts/variables/define: Ditto + * scripts/functions/file: Test the new $(file ...) function. 2012-01-12 Paul Smith diff --git a/tests/scripts/variables/define b/tests/scripts/variables/define index f91519e..68d493b 100644 --- a/tests/scripts/variables/define +++ b/tests/scripts/variables/define @@ -30,6 +30,10 @@ define simple := @echo $(FOO) endef +define posix ::= +@echo $(FOO) +endef + append = @echo a define append += @@ -49,10 +53,54 @@ FOO = there all: ; $(multi) $(simple) + $(posix) + $(append) + $(cond) +', + '', "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\na\nb\nfirst\n"); + '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n"); # TEST 2: define in true section of conditional (containing conditional) diff --git a/tests/scripts/variables/flavors b/tests/scripts/variables/flavors index 92feed6..ba133ea 100644 --- a/tests/scripts/variables/flavors +++ b/tests/scripts/variables/flavors @@ -73,4 +73,24 @@ all: ; @echo $(foo) ', '', "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; diff --git a/variable.c b/variable.c index a0eef9b..839aa5a 100644 --- a/variable.c +++ b/variable.c @@ -1396,17 +1396,27 @@ do_variable_definition (const struct floc *flocp, const char *varname, /* 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 - 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 * -parse_variable_definition (const char *p, enum variable_flavor *flavor) +parse_variable_definition (const char *p, struct variable *var) { int wspace = 0; + const char *e = NULL; p = next_token (p); + var->name = (char *)p; + var->length = 0; while (1) { @@ -1451,6 +1461,7 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor) if (isblank ((unsigned char)c)) { wspace = 1; + e = p - 1; p = next_token (p); c = *p; if (c == '\0') @@ -1461,8 +1472,10 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor) if (c == '=') { - *flavor = f_recursive; - return (char *)p; + var->flavor = f_recursive; + if (! e) + e = p - 1; + break; } /* Match assignment variants (:=, +=, ?=, !=) */ @@ -1471,16 +1484,16 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor) switch (c) { case ':': - *flavor = f_simple; + var->flavor = f_simple; break; case '+': - *flavor = f_append; + var->flavor = f_append; break; case '?': - *flavor = f_conditional; + var->flavor = f_conditional; break; case '!': - *flavor = f_shell; + var->flavor = f_shell; break; default: /* 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. */ 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 (wspace) return NULL; } + var->length = e - var->name; + var->value = next_token (p); return (char *)p; } @@ -1513,27 +1543,15 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor) struct variable * assign_variable_definition (struct variable *v, char *line) { - char *beg; - char *end; - enum variable_flavor flavor; char *name; - beg = next_token (line); - line = parse_variable_definition (beg, &flavor); - if (!line) + if (!parse_variable_definition (line, v)) 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. */ - name = alloca (end - beg + 1); - memcpy (name, beg, end - beg); - name[end - beg] = '\0'; + name = alloca (v->length + 1); + memcpy (name, v->name, v->length); + name[v->length] = '\0'; v->name = allocated_variable_expand (name); if (v->name[0] == '\0') diff --git a/variable.h b/variable.h index 1fd1b66..1fe3d1e 100644 --- a/variable.h +++ b/variable.h @@ -35,7 +35,7 @@ enum variable_origin enum variable_flavor { f_bogus, /* Bogus (error) */ - f_simple, /* Simple definition (:=) */ + f_simple, /* Simple definition (:= or ::=) */ f_recursive, /* Recursive definition (=) */ f_append, /* Appending definition (+=) */ f_conditional, /* Conditional definition (?=) */ @@ -52,9 +52,9 @@ enum variable_flavor struct variable { char *name; /* Variable name. */ - int length; /* strlen (name) */ char *value; /* Variable value. */ struct floc fileinfo; /* Where the variable was defined. */ + int length; /* strlen (name) */ unsigned int recursive:1; /* Gets recursively re-evaluated. */ unsigned int append:1; /* Nonzero if an appending target-specific variable. */ @@ -160,7 +160,7 @@ struct variable *do_variable_definition (const struct floc *flocp, enum variable_flavor flavor, int target_var); 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 *try_variable_definition (const struct floc *flocp, char *line, enum variable_origin origin, -- 2.11.4.GIT