From 6c06c547dcc990a9db0648d66b6a6519c3d1e5d4 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 18 Sep 2021 18:53:38 -0400 Subject: [PATCH] Add support for the POSIX :::= assignment operator. POSIX Issue 8 will require a new assignment operator, :::=. This operator behaves similarly to the BSD make := operator: the right-hand side is expanded immediately, but then the value is re-escaped (all '$' are converted to '$$') and the resulting variable is considered a recursive variable: the value is re-expanded on use. * src/variable.h (enum variable_flavor): Add f_expand flavor. * src/variable.c (do_variable_definition): When defining f_expand, post-process the result to re-escape '$' characters. Remove default: to the compiler warns about un-handled enum values. Set recursive values for both f_recursive and f_expand. (parse_variable_definition): Rewrite this method. The previous version was annoying to extend to ':::='. (print_variable): Remove default: so the compiler warns us about un-handled enum values. * src/function.c (func_origin): Remove default: so the compiler warns us about un-handled enum values. * doc/make.texi: Add documentation for :::=. * tests/scripts/variables/define: Add a test for define :::=. * tests/scripts/variables/flavors: Add tests for :::=. * tests/scripts/variables/negative: Add tests for :::=. --- doc/make.texi | 227 ++++++++++++++++++++++--------- src/function.c | 1 - src/makeint.h | 2 +- src/variable.c | 223 +++++++++++++++++------------- src/variable.h | 1 + tests/scripts/variables/define | 11 +- tests/scripts/variables/flavors | 44 ++++++ tests/scripts/variables/negative | 14 ++ 8 files changed, 363 insertions(+), 160 deletions(-) diff --git a/doc/make.texi b/doc/make.texi index 4470386d..53648b0a 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -258,6 +258,13 @@ How to Use Variables * Suppressing Inheritance:: Suppress inheritance of variables. * Special Variables:: Variables with special meaning or behavior. +The Two Flavors of Variables + +* Recursive Assignment:: Setting recursively expanded variables. +* Simple Assignment:: Setting simply expanded variables. +* Immediate Assignment:: Setting immediately expanded variables. +* Conditional Assignment:: Assigning variable values conditionally. + Advanced Features for Reference to Variables * Substitution Refs:: Referencing a variable with @@ -1528,6 +1535,7 @@ Variable definitions are parsed as follows: @var{immediate} ?= @var{deferred} @var{immediate} := @var{immediate} @var{immediate} ::= @var{immediate} +@var{immediate} :::= @var{immediate-with-escape} @var{immediate} += @var{deferred} or @var{immediate} @var{immediate} != @var{immediate} @@ -1551,6 +1559,10 @@ define @var{immediate} ::= @var{immediate} endef +define @var{immediate} :::= + @var{immediate-with-escape} +endef + define @var{immediate} += @var{deferred} or @var{immediate} endef @@ -1564,6 +1576,11 @@ For the append operator @samp{+=}, the right-hand side is considered immediate if the variable was previously set as a simple variable (@samp{:=} or @samp{::=}), and deferred otherwise. +For the @var{immediate-with-escape} operator @samp{:::=}, the value on +the right-hand side is immediately expanded but then escaped (that is, +all instances of @code{$} in the result of the expansion are replaced +with @code{$$}). + For the shell assignment operator @samp{!=}, the right-hand side is evaluated immediately and handed to the shell. The result is stored in the variable named on the left, and that variable is considered a @@ -5357,10 +5374,20 @@ often improved is automatic variables (@pxref{Automatic Variables}). @cindex recursively expanded variables @cindex variables, recursively expanded -There are two ways that a variable in GNU @code{make} can have a value; -we call them the two @dfn{flavors} of variables. The two flavors are -distinguished in how they are defined and in what they do when expanded. +There are different ways that a variable in GNU @code{make} can get a value; +we call them the @dfn{flavors} of variables. The flavors are distinguished in +how they handle the values they are assigned in the makefile, and in how those +values are managed when the variable is later used and expanded. +@menu +* Recursive Assignment:: Setting recursively expanded variables. +* Simple Assignment:: Setting simply expanded variables. +* Immediate Assignment:: Setting immediately expanded variables. +* Conditional Assignment:: Assigning variable values conditionally. +@end menu + +@node Recursive Assignment, Simple Assignment, Flavors, Flavors +@subsection Recursively Expanded Variable Assignment @cindex = The first flavor of variable is a @dfn{recursively expanded} variable. Variables of this sort are defined by lines using @samp{=} @@ -5417,7 +5444,9 @@ expanded. This makes @code{make} run slower; worse, it causes the because you cannot easily control when they are called, or even how many times. -To avoid all the problems and inconveniences of recursively expanded +@node Simple Assignment, Immediate Assignment, Recursive Assignment, Flavors +@subsection Simply Expanded Variable Assignment +To avoid the problems and inconveniences of recursively expanded variables, there is another flavor: simply expanded variables. @cindex simply expanded variables @@ -5427,34 +5456,35 @@ variables, there is another flavor: simply expanded variables. @dfn{Simply expanded variables} are defined by lines using @samp{:=} 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). +described by the POSIX standard (support for @samp{::=} is added to +the POSIX standard for POSIX Issue 8). -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 -expanded variable is the result of expanding the text that you write. -It does not contain any references to other variables; it contains their -values @emph{as of the time this variable was defined}. Therefore, +The value of a simply expanded variable is scanned once, expanding any +references to other variables and functions, when the variable is +defined. Once that expansion is complete the value of the variable is +never expanded again: when the variable is used the value is copied +verbatim as the expansion. If the value contained variable references +the result of the expansion will contain their values @emph{as of the +time this variable was defined}. Therefore, @example +@group x := foo y := $(x) bar x := later +@end group @end example @noindent is equivalent to @example +@group y := foo bar x := later +@end group @end example -When a simply expanded variable is referenced, its value is substituted -verbatim. - Here is a somewhat more complicated example, illustrating the use of @samp{:=} in conjunction with the @code{shell} function. (@xref{Shell Function, , The @code{shell} Function}.) This example @@ -5503,8 +5533,10 @@ this means you can include leading spaces in a variable value by protecting them with variable references, like this: @example +@group nullstring := space := $(nullstring) # end of the line +@end group @end example @noindent @@ -5528,6 +5560,84 @@ Here the value of the variable @code{dir} is @w{@samp{/foo/bar }} (with four trailing spaces), which was probably not the intention. (Imagine something like @w{@samp{$(dir)/file}} with this definition!) +@node Immediate Assignment, Conditional Assignment, Simple Assignment, Flavors +@subsection Immediately Expanded Variable Assignment +@cindex immediate variable assignment +@cindex variables, immediate assignment +@cindex :::= + +Another form of assignment allows for immediate expansion, but unlike simple +assignment the resulting variable is recursive: it will be re-expanded again +on every use. In order to avoid unexpected results, after the value is +immediately expanded it will automatically be quoted: all instances of +@code{$} in the value after expansion will be converted into @code{$$}. This +type of assignment uses the @samp{:::=} operator. For example, + +@example +@group +var = first +OUT :::= $(var) +var = second +@end group +@end example + +@noindent +results in the @code{OUT} variable containing the text @samp{first}, while here: + +@example +@group +var = one$$two +OUT :::= $(var) +var = three$$four +@end group +@end example + +@noindent +results in the @code{OUT} variable containing the text @samp{one$$two}. The +value is expanded when the variable is assigned, so the result is the +expansion of the first value of @code{var}, @samp{one$two}; then the value is +re-escaped before the assignment is complete giving the final result of +@samp{one$$two}. + +The variable @code{OUT} is thereafter considered a recursive variable, so it +will be re-expanded when it is used. + +This seems functionally equivalent to the @samp{:=} / @samp{::=} operators, +but there are a few differences: + +First, after assignment the variable is a normal recursive variable; when you +append to it with @samp{+=} the value on the right-hand side is not expanded +immediately. If you prefer the @samp{+=} operator to expand the right-hand +side immediately you should use the @samp{:=} / @samp{::=} assignment instead. + +Second, these variables are slightly less efficient than simply expanded +variables since they do need to be re-expanded when they are used, rather than +merely copied. However since all variable references are escaped this +expansion simply un-escapes the value, it won't expand any variables or run +any functions. + +Here is another example: + +@example +@group +var = one$$two +OUT :::= $(var) +OUT += $(var) +var = three$$four +@end group +@end example + +After this, the value of @code{OUT} is the text @samp{one$$two $(var)}. When +this variable is used it will be expanded and the result will be +@samp{one$two three$four}. + +This style of assignment is equivalent to the traditional BSD @code{make} +@samp{:=} operator; as you can see it works slightly differently than the GNU +@code{make} @samp{:=} operator. The @code{:::=} operator is added to the +POSIX specification in Issue 8 to provide portability. + +@node Conditional Assignment, , Immediate Assignment, Flavors +@subsection Conditional Variable Assignment @cindex conditional variable assignment @cindex variables, conditional assignment @cindex ?= @@ -5632,12 +5742,9 @@ sets @samp{bar} to @samp{a.c b.c l.a c.c}. @cindex @code{$}, in variable name @cindex dollar sign (@code{$}), in variable name -Computed variable names are a complicated concept needed only for -sophisticated makefile programming. For most purposes you need not -consider them, except to know that making a variable with a dollar sign -in its name might have strange results. However, if you are the type -that wants to understand everything, or you are actually interested in -what they do, read on. +Computed variable names are an advanced concept, very useful in more +sophisticated makefile programming. In simple situations you need not +consider them, but they can be extremely useful. Variables may be referenced inside the name of a variable. This is called a @dfn{computed variable name} or a @dfn{nested variable @@ -5685,10 +5792,9 @@ a := $($(x)) defines @code{a} as @samp{Hello}: @samp{$($(x))} becomes @samp{$($(y))} which becomes @samp{$(z)} which becomes @samp{Hello}. -Nested variable references can also contain modified references and -function invocations (@pxref{Functions, ,Functions for Transforming Text}), -just like any other reference. -For example, using the @code{subst} function +Nested variable references can also contain modified references and function +invocations (@pxref{Functions, ,Functions for Transforming Text}), just like +any other reference. For example, using the @code{subst} function (@pxref{Text Functions, ,Functions for String Substitution and Analysis}): @example @@ -5857,27 +5963,30 @@ Several variables have constant initial values. @cindex = @cindex := @cindex ::= +@cindex :::= @cindex ?= @cindex != -To set a variable from the makefile, write a line starting with the -variable name followed by @samp{=}, @samp{:=}, or @samp{::=}. Whatever -follows the @samp{=}, @samp{:=}, or @samp{::=} on the line becomes the -value. For example, +To set a variable from the makefile, write a line starting with the variable +name followed by one of the assignment operators @samp{=}, @samp{:=}, +@samp{::=}, or @samp{:::=}. Whatever follows the operator and any initial +whitespace on the line becomes the value. For example, @example objects = main.o foo.o bar.o utils.o @end example @noindent -defines a variable named @code{objects}. Whitespace around the variable -name and immediately after the @samp{=} is ignored. +defines a variable named @code{objects} to contain the value @samp{main.o +foo.o bar.o utils.o}. 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{:=} 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}. +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. Variables defined with @samp{:::=} +are @dfn{immediately expanded} variables. The different assignment operators +are described in @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. @@ -5987,13 +6096,12 @@ originally. @xref{Flavors, ,The Two Flavors of Variables}, for an 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{:=} 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, +essentially as if you had included the extra text in the initial 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 @@ -6010,15 +6118,9 @@ variable := $(variable) more @end example On the other hand, when you use @samp{+=} with a variable that you defined -first to be recursively-expanded using plain @samp{=}, @code{make} does -something a bit different. Recall that when you define a -recursively-expanded variable, @code{make} does not expand the value you set -for variable and function references immediately. Instead it stores the text -verbatim, and saves these variable and function references to be expanded -later, when you refer to the new variable (@pxref{Flavors, ,The Two Flavors -of Variables}). When you use @samp{+=} on a recursively-expanded variable, -it is this unexpanded text to which @code{make} appends the new text you -specify. +first to be recursively-expanded using plain @samp{=} or @samp{:::=}, +@code{make} appends the un-expanded text to the existing value, whatever it +is. This means that @example @group @@ -6247,6 +6349,7 @@ In such situations you may want to use the @code{undefine} directive to make a variable appear as if it was never set. For example: @example +@group foo := foo bar = bar @@ -6255,6 +6358,7 @@ undefine bar $(info $(origin foo)) $(info $(flavor bar)) +@end group @end example This example will print ``undefined'' for both variables. @@ -6342,14 +6446,14 @@ variable only. 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{:=} 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). +The @var{variable-assignment} can be any valid form of assignment; recursive +(@samp{=}), simple (@samp{:=} or @samp{::=}), immediate (@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 @@ -12362,6 +12466,7 @@ Here is a summary of the directives GNU @code{make} recognizes: @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 diff --git a/src/function.c b/src/function.c index 396f1297..5a7ad3a5 100644 --- a/src/function.c +++ b/src/function.c @@ -465,7 +465,6 @@ func_origin (char *o, char **argv, const char *funcname UNUSED) else switch (v->origin) { - default: case o_invalid: abort (); break; diff --git a/src/makeint.h b/src/makeint.h index 41c90e5b..eb78b402 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -407,7 +407,7 @@ extern int unixy_shell; #define NONE_SET(_v,_m) (! ANY_SET ((_v),(_m))) #define MAP_NUL 0x0001 -#define MAP_BLANK 0x0002 +#define MAP_BLANK 0x0002 /* space, TAB */ #define MAP_NEWLINE 0x0004 #define MAP_COMMENT 0x0008 #define MAP_SEMI 0x0010 diff --git a/src/variable.c b/src/variable.c index 0d1f1af6..9f66c649 100644 --- a/src/variable.c +++ b/src/variable.c @@ -1194,7 +1194,6 @@ do_variable_definition (const floc *flocp, const char *varname, switch (flavor) { - default: case f_bogus: /* Should not be possible. */ abort (); @@ -1205,6 +1204,25 @@ do_variable_definition (const floc *flocp, const char *varname, target-specific variable. */ p = alloc_value = allocated_variable_expand (value); break; + case f_expand: + { + /* A POSIX "var :::= value" assignment. Expand the value, then it + becomes a recursive variable. After expansion convert all '$' + tokens to '$$' to resolve to '$' when recursively expanded. */ + char *t = allocated_variable_expand (value); + char *np = alloc_value = xmalloc (strlen (t) * 2 + 1); + p = t; + while (p[0] != '\0') + { + if (p[0] == '$') + *(np++) = '$'; + *(np++) = *(p++); + } + *np = '\0'; + p = alloc_value; + free (t); + break; + } case f_shell: { /* A shell definition "var != value". Expand value, pass it to @@ -1440,8 +1458,8 @@ do_variable_definition (const floc *flocp, const char *varname, invoked in places where we want to define globally visible variables, make sure we define this variable in the global set. */ - v = define_variable_in_set (varname, strlen (varname), p, - origin, flavor == f_recursive, + v = define_variable_in_set (varname, strlen (varname), p, origin, + flavor == f_recursive || flavor == f_expand, (target_var ? current_variable_set_list->set : NULL), flocp); @@ -1456,7 +1474,7 @@ do_variable_definition (const floc *flocp, const char *varname, /* Parse P (a null-terminated string) as a variable definition. 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. + are undefined, except NAME points to the first non-space character or EOS. If it is a variable definition, return a pointer to the char after the assignment token and set the following fields (only) of *VAR: @@ -1468,15 +1486,17 @@ do_variable_definition (const floc *flocp, const char *varname, */ char * -parse_variable_definition (const char *p, struct variable *var) +parse_variable_definition (const char *str, struct variable *var) { - int wspace = 0; - const char *e = NULL; + const char *p = str; + const char *end = NULL; NEXT_TOKEN (p); var->name = (char *)p; var->length = 0; + /* Walk through STR until we find a valid assignment operator. Each time + through this loop P points to the next character to consider. */ while (1) { int c = *p++; @@ -1485,26 +1505,112 @@ parse_variable_definition (const char *p, struct variable *var) if (STOP_SET (c, MAP_COMMENT|MAP_NUL)) return NULL; + if (ISBLANK (c)) + { + /* Variable names can't contain spaces so if this is the second set + of spaces we know it's not a variable assignment. */ + if (end) + return NULL; + end = p - 1; + NEXT_TOKEN (p); + continue; + } + + /* If we found = we're done! */ + if (c == '=') + { + if (!end) + end = p - 1; + var->flavor = f_recursive; + break; + } + + if (c == ':') + { + if (!end) + end = p - 1; + + /* We need to distinguish :=, ::=, and :::=, and : outside of an + assignment (which means this is not a variable definition). */ + c = *p++; + if (c == '=') + { + var->flavor = f_simple; + break; + } + if (c == ':') + { + c = *p++; + if (c == '=') + { + var->flavor = f_simple; + break; + } + if (c == ':' && *p++ == '=') + { + var->flavor = f_expand; + break; + } + } + return NULL; + } + + /* See if it's one of the other two-byte operators. */ + if (*p == '=') + { + switch (c) + { + case '+': + var->flavor = f_append; + break; + case '?': + var->flavor = f_conditional; + break; + case '!': + var->flavor = f_shell; + break; + default: + goto other; + } + + if (!end) + end = p - 1; + ++p; + break; + } + + other: + /* We found a char which is not part of an assignment operator. + If we've seen whitespace, then we know this is not a variable + assignment since variable names cannot contain whitespace. */ + if (end) + return NULL; + if (c == '$') { - /* This begins a variable expansion reference. Make sure we don't - treat chars inside the reference as assignment tokens. */ + /* Skip any variable reference, to ensure we don't treat chars + inside the reference as assignment operators. */ char closeparen; unsigned int count; c = *p++; - if (c == '(') - closeparen = ')'; - else if (c == '{') - closeparen = '}'; - else if (c == '\0') - return NULL; - else - /* '$$' or '$X'. Either way, nothing special to do here. */ - continue; + switch (c) + { + case '(': + closeparen = ')'; + break; + case '{': + closeparen = '}'; + break; + case '\0': + return NULL; + default: + /* '$$' or '$X': skip it. */ + continue; + } - /* P now points past the opening paren or brace. - Count parens or braces until it is matched. */ + /* P now points past the opening paren or brace. Count parens or + braces until we find the closing paren/brace. */ for (count = 1; *p != '\0'; ++p) { if (*p == closeparen && --count == 0) @@ -1515,82 +1621,12 @@ parse_variable_definition (const char *p, struct variable *var) if (*p == c) ++count; } - continue; } - - /* If we find whitespace skip it, and remember we found it. */ - if (ISBLANK (c)) - { - wspace = 1; - e = p - 1; - NEXT_TOKEN (p); - c = *p; - if (c == '\0') - return NULL; - ++p; - } - - - if (c == '=') - { - var->flavor = f_recursive; - if (! e) - e = p - 1; - break; - } - - /* Match assignment variants (:=, +=, ?=, !=) */ - if (*p == '=') - { - switch (c) - { - case ':': - var->flavor = f_simple; - break; - case '+': - var->flavor = f_append; - break; - case '?': - var->flavor = f_conditional; - break; - case '!': - var->flavor = f_shell; - break; - default: - /* If we skipped whitespace, non-assignments means no var. */ - if (wspace) - return NULL; - - /* Might be assignment, or might be $= or #=. Check. */ - continue; - } - 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; - } - - /* If we skipped whitespace, non-assignments means no var. */ - if (wspace) - return NULL; } - var->length = (unsigned int) (e - var->name); + /* We found a valid variable assignment: END points to the char after the + end of the variable name and P points to the char after the =. */ + var->length = (unsigned int) (end - var->name); var->value = next_token (p); return (char *)p; } @@ -1690,7 +1726,6 @@ print_variable (const void *item, void *arg) origin = _("'override' directive"); break; case o_invalid: - default: abort (); } fputs ("# ", stdout); diff --git a/src/variable.h b/src/variable.h index 509777c4..194b82d5 100644 --- a/src/variable.h +++ b/src/variable.h @@ -35,6 +35,7 @@ enum variable_flavor f_bogus, /* Bogus (error) */ f_simple, /* Simple definition (:= or ::=) */ f_recursive, /* Recursive definition (=) */ + f_expand, /* POSIX :::= assignment */ f_append, /* Appending definition (+=) */ f_conditional, /* Conditional definition (?=) */ f_shell, /* Shell assignment (!=) */ diff --git a/tests/scripts/variables/define b/tests/scripts/variables/define index 984bdd8e..eecbd8f3 100644 --- a/tests/scripts/variables/define +++ b/tests/scripts/variables/define @@ -61,7 +61,7 @@ all: ; $(multi) # TEST 1a: Various new-style define/endef, with no spaces -run_make_test(' +run_make_test(q! FOO = foo define multi= @@ -77,6 +77,10 @@ define posix::= @echo $(FOO) endef +define posixbsd:::= +@echo '$(FOO)$$bar' +endef + append = @echo a define append+= @@ -97,10 +101,11 @@ FOO = there all: ; $(multi) $(simple) $(posix) + $(posixbsd) $(append) $(cond) -', - '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n"); +!, + '', "echo hi\nhi\nthere\nfoo\nfoo\nfoo\$bar\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 831e5d81..627672f8 100644 --- a/tests/scripts/variables/flavors +++ b/tests/scripts/variables/flavors @@ -153,4 +153,48 @@ dep: ; @: $(info recur=/$(recur)/ simple=/$(simple)/) !, '', "recur=/onetwothree/ simple=/fourfivesix/\n"); +# Test POSIX :::= +# This creates a recursive variable, but it expands the RHS first. Any +# variable escapes ('$$') are preserved so that this recursive variable can be +# expanded again without changing its contents. +run_make_test(' +bar = Goodbye +foo :::= $(bar) +bar = ${ugh} +ugh = Hello +all: ; @echo $(foo) +', + '', "Goodbye"); + +# POSIX :::= no spaces +run_make_test(q! +bar = Goodbye +foo:::=$(bar) +bar = ${ugh} +ugh = Hello +all: ; @echo $(foo) +!, + '', "Goodbye"); + +# Variable escapes ('$$') are preserved. +run_make_test(q! +bar = Good$$bye +foo :::= $(bar) $$what +bar = ${ugh} +ugh = Hello +all: ; @echo '$(foo)' +!, + '', 'Good$bye $what'); + +# Append works as expected +run_make_test(q! +bar = Good$$bye +foo :::= $(bar) +foo += $$what $(bar) +bar = ${ugh} +ugh = Hello +all: ; @echo '$(foo)' +!, + '', 'Good$bye $what Hello'); + 1; diff --git a/tests/scripts/variables/negative b/tests/scripts/variables/negative index 0f9abc82..5cb600a8 100644 --- a/tests/scripts/variables/negative +++ b/tests/scripts/variables/negative @@ -43,4 +43,18 @@ run_make_test(undef, '#MAKEFILE#:4: *** unterminated variable reference. Stop.', 512); +# Whitespace not allowed in variable names +run_make_test('x y =', '', + '#MAKEFILE#:1: *** missing separator. Stop.', 512); + +run_make_test('x y=', '', + '#MAKEFILE#:1: *** missing separator. Stop.', 512); + +# In theory an empty variable should be ignored, but during parsing it's a +# real token and so this fails. I'm not 100% sure if this is right or not. + +run_make_test('x $X=', '', + '#MAKEFILE#:1: *** missing separator. Stop.', 512); + + 1;