mirror of
https://github.com/mirror/make.git
synced 2025-03-13 11:20:38 +08:00
Support conditional modifiers on all assignment operators
Rework the single "?=" operator to instead allow a "?" modifier to be prepended to ANY assignment operator. If "?" is given then the variable is assigned (using whatever operator comes next) if and only if the variable is not already defined. If it is defined then no action is taken (the right-hand side is not expanded, etc.) * NEWS: Announce this new feature. * doc/make.texi: Modify the documentation around assignment operators. * src/variable.h: Remove the f_conditional variable flavor. (do_variable_definition): Add an argument specifying conditional. * src/variable.c (parse_variable_definition): Use the existing flag "conditional" to remember if we saw "?" rather than the flavor. When we see "?" skip it and continue trying to parse an assignment. (try_variable_definition): Pass condition to do_variable_definition(). (initialize_file_variables): Ditto. (do_variable_definition): Check for conditional up-front: quit if set. Remove handling of obsolete f_conditional flavor. * src/read.c (eval_makefile): MAKEFILE_LIST is not conditional. (do_define): Unset conditional for define with no operator. Pass the conditional flag to do_variable_definition(). (construct_include_path): .INCLUDE_DIRS is not conditional. * src/load.c (load_file): .LOADED is not conditional. * tests/scripts/variables/conditional: Add new tests.
This commit is contained in:
parent
82708b3a3a
commit
1eff20f6f6
8
NEWS
8
NEWS
@ -41,6 +41,14 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
|
||||
The check in GNU Make 4.3 for suffix rules with prerequisites didn't check
|
||||
single-suffix rules, only double-suffix rules. Add the missing check.
|
||||
|
||||
* New feature: Any assignment operator can be made conditional
|
||||
GNU Make has long supported the conditional operator "?=" which creates a
|
||||
recursive variable set to a value if and only if the variable is not already
|
||||
defined. In this release, the "?" can precede any assignment operator to
|
||||
make it conditional. For example, "?:=" creates a simply-expanded variable
|
||||
and expands the right-hand side if and only if the variable is not already
|
||||
defined. The constructs "?::=", "?:::=", and "?!=" also behave as expected.
|
||||
|
||||
* New feature: Unload function for loaded objects
|
||||
When a loaded object needs to be unloaded by GNU Make, it will invoke an
|
||||
unload function (if one is defined) beforehand that allows the object to
|
||||
|
159
doc/make.texi
159
doc/make.texi
@ -1572,18 +1572,18 @@ reference this section as you become familiar with them, in later
|
||||
chapters.
|
||||
|
||||
@subheading Variable Assignment
|
||||
@cindex +=, expansion
|
||||
@cindex =, expansion
|
||||
@cindex ?=, expansion
|
||||
@cindex +=, expansion
|
||||
@cindex :=, expansion
|
||||
@cindex ::=, expansion
|
||||
@cindex :::=, expansion
|
||||
@cindex !=, expansion
|
||||
@cindex +=, expansion
|
||||
@cindex define, expansion
|
||||
|
||||
Variable definitions are parsed as follows:
|
||||
|
||||
@example
|
||||
@var{immediate} = @var{deferred}
|
||||
@var{immediate} ?= @var{deferred}
|
||||
@var{immediate} := @var{immediate}
|
||||
@var{immediate} ::= @var{immediate}
|
||||
@var{immediate} :::= @var{immediate-with-escape}
|
||||
@ -1598,10 +1598,6 @@ define @var{immediate} =
|
||||
@var{deferred}
|
||||
endef
|
||||
|
||||
define @var{immediate} ?=
|
||||
@var{deferred}
|
||||
endef
|
||||
|
||||
define @var{immediate} :=
|
||||
@var{immediate}
|
||||
endef
|
||||
@ -1638,6 +1634,21 @@ in the variable named on the left, and that variable is considered a
|
||||
recursively expanded variable (and will thus be re-evaluated on each
|
||||
reference).
|
||||
|
||||
@subsubheading Conditional Assignment Modifier
|
||||
@cindex ?=, expansion
|
||||
@cindex ?:=, expansion
|
||||
@cindex ?::=, expansion
|
||||
@cindex ?:::=, expansion
|
||||
@cindex ?!=, expansion
|
||||
|
||||
Adding a conditional modifier (@pxref{Conditional Assignment, ,Conditional
|
||||
Variable Assignment}) to an assignment operator does not change the expansion
|
||||
style for that operator: if the variable is not defined then the assignment
|
||||
will be made using expansion as defined above.
|
||||
|
||||
If the variable is already defined, then the right-hand side of the assignment
|
||||
is ignored and not expanded.
|
||||
|
||||
@subheading Conditional Directives
|
||||
@cindex ifdef, expansion
|
||||
@cindex ifeq, expansion
|
||||
@ -5822,9 +5833,19 @@ POSIX specification in Issue 8 to provide portability.
|
||||
@cindex conditional variable assignment
|
||||
@cindex variables, conditional assignment
|
||||
@cindex ?=
|
||||
There is another assignment operator for variables, @samp{?=}. This
|
||||
is called a conditional variable assignment operator, because it only
|
||||
has an effect if the variable is not yet defined. This statement:
|
||||
@cindex ?:=
|
||||
@cindex ?::=
|
||||
@cindex ?:::=
|
||||
@cindex ?!=
|
||||
|
||||
Any assignment operator can be prefixed with a conditional operator, @samp{?}.
|
||||
If this modifier is provided then the assignment will proceed normally, but
|
||||
@emph{only if} the variable is not already defined.
|
||||
|
||||
If the variable is already defined, the assignment is ignored and the
|
||||
right-hand side (value) of the assignment is not processed.
|
||||
|
||||
For example this statement:
|
||||
|
||||
@example
|
||||
FOO ?= bar
|
||||
@ -5840,8 +5861,18 @@ ifeq ($(origin FOO), undefined)
|
||||
endif
|
||||
@end example
|
||||
|
||||
Note that a variable set to an empty value is still defined, so
|
||||
@samp{?=} will not set that variable.
|
||||
More generally a statement of the form:
|
||||
|
||||
@example
|
||||
NAME ?<op> VALUE
|
||||
@end example
|
||||
|
||||
will do nothing if the variable @samp{NAME} is defined, and will perform the
|
||||
operation @code{NAME <op> VALUE} for any assignment operation @samp{<op>} if
|
||||
@samp{NAME} is not defined.
|
||||
|
||||
Note that a variable set to an empty value is still defined, so assignments
|
||||
modified with @samp{?} will not set that variable.
|
||||
|
||||
@node Advanced
|
||||
@section Advanced Features for Reference to Variables
|
||||
@ -6142,7 +6173,6 @@ Several variables have constant initial values.
|
||||
@cindex :=
|
||||
@cindex ::=
|
||||
@cindex :::=
|
||||
@cindex ?=
|
||||
@cindex !=
|
||||
|
||||
To set a variable from the makefile, write a line starting with the variable
|
||||
@ -6174,31 +6204,12 @@ amount of memory on the computer. You can split the value of a
|
||||
variable into multiple physical lines for readability
|
||||
(@pxref{Splitting Lines, ,Splitting Long Lines}).
|
||||
|
||||
Most variable names are considered to have the empty string as a value if
|
||||
you have never set them. Several variables have built-in initial values
|
||||
that are not empty, but you can set them in the usual ways
|
||||
(@pxref{Implicit Variables, ,Variables Used by Implicit Rules}).
|
||||
Several special variables are set
|
||||
automatically to a new value for each rule; these are called the
|
||||
@dfn{automatic} variables (@pxref{Automatic Variables}).
|
||||
|
||||
If you'd like a variable to be set to a value only if it's not already
|
||||
set, then you can use the shorthand operator @samp{?=} instead of
|
||||
@samp{=}. These two settings of the variable @samp{FOO} are identical
|
||||
(@pxref{Origin Function, ,The @code{origin} Function}):
|
||||
|
||||
@example
|
||||
FOO ?= bar
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
and
|
||||
|
||||
@example
|
||||
ifeq ($(origin FOO), undefined)
|
||||
FOO = bar
|
||||
endif
|
||||
@end example
|
||||
Most variable names are considered to have the empty string as a value if you
|
||||
have never set them. Several variables have built-in initial values that are
|
||||
not empty, but you can set them in the usual ways (@pxref{Implicit Variables,
|
||||
,Variables Used by Implicit Rules}). Several special variables are set
|
||||
automatically to a new value while running the recipe for a rule; these are
|
||||
called the @dfn{automatic} variables (@pxref{Automatic Variables}).
|
||||
|
||||
The shell assignment operator @samp{!=} can be used to execute a
|
||||
shell script and set a variable to its output. This operator first
|
||||
@ -6229,6 +6240,55 @@ var := $(shell find . -name "*.c")
|
||||
As with the @code{shell} function, the exit status of the just-invoked
|
||||
shell script is stored in the @code{.SHELLSTATUS} variable.
|
||||
|
||||
@node Conditionalizing
|
||||
@section Conditionally Assigning to Variables
|
||||
@cindex ?=
|
||||
@cindex ?:=
|
||||
@cindex ?::=
|
||||
@cindex ?:::=
|
||||
@cindex ?!=
|
||||
|
||||
Sometimes you want to set a variable but only if it's not already defined.
|
||||
One way to do this is to test using the @code{origin} function (@pxref{Origin
|
||||
Function}), like this:
|
||||
|
||||
@example
|
||||
ifeq ($(origin FOO), undefined)
|
||||
FOO = value
|
||||
endif
|
||||
@end example
|
||||
|
||||
However this is a lot to type, and read, and so GNU Make provides a way to
|
||||
conditionally assign variables, only if they are not already defined.
|
||||
|
||||
To do this, prepend the conditional modifier @samp{?} to the assignment
|
||||
operator. In this form @code{make} will first check to see if the variable is
|
||||
defined; only if it is not will it proceed to execute the assignment (using
|
||||
whichever operator you specified).
|
||||
|
||||
Instead of the above example, you get identical behavior by writing:
|
||||
|
||||
@example
|
||||
FOO ?= value
|
||||
@end example
|
||||
|
||||
And of course, you can also use:
|
||||
|
||||
@example
|
||||
FOO ?:= value
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
rather than writing:
|
||||
|
||||
@example
|
||||
ifeq ($(origin FOO), undefined)
|
||||
FOO := value
|
||||
endif
|
||||
@end example
|
||||
|
||||
The other assignment operators @samp{::=}, @samp{:::=}, and @samp{!=} can also
|
||||
be used.
|
||||
|
||||
@node Appending
|
||||
@section Appending More Text to Variables
|
||||
@ -6625,13 +6685,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{:=} 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).
|
||||
(@samp{=}), simple (@samp{:=} or @samp{::=}), immediate (@samp{::=}), shell
|
||||
(@samp{!=}), or appending (@samp{+=}), and may be preceded by a conditional
|
||||
modifier (@samp{?}). The variable that appears within the
|
||||
@var{variable-assignment} is 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
|
||||
@ -13069,8 +13129,13 @@ 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 define @var{variable} ?:=
|
||||
@itemx define @var{variable} ?::=
|
||||
@itemx define @var{variable} ?:::=
|
||||
@itemx define @var{variable} ?!=
|
||||
@itemx define @var{variable} +=
|
||||
@itemx endef
|
||||
Define multi-line variables.@*
|
||||
@xref{Multi-Line}.
|
||||
|
@ -230,7 +230,7 @@ load_file (const floc *flocp, struct file *file, int noerror)
|
||||
|
||||
/* If the load didn't fail, add the file to the .LOADED variable. */
|
||||
if (r)
|
||||
do_variable_definition(flocp, ".LOADED", ldname, o_file, f_append_value, 0);
|
||||
do_variable_definition(flocp, ".LOADED", ldname, o_file, f_append_value, 0, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
27
src/read.c
27
src/read.c
@ -413,7 +413,7 @@ eval_makefile (const char *filename, unsigned short flags)
|
||||
|
||||
/* Add this makefile to the list. */
|
||||
do_variable_definition (&ebuf.floc, "MAKEFILE_LIST", filename, o_file,
|
||||
f_append_value, 0);
|
||||
f_append_value, 0, 0);
|
||||
|
||||
/* Evaluate the makefile */
|
||||
|
||||
@ -733,11 +733,11 @@ eval (struct ebuffer *ebuf, int set_default)
|
||||
record_waiting_files ();
|
||||
|
||||
if (vmod.undefine_v)
|
||||
{
|
||||
do_undefine (p, origin, ebuf);
|
||||
continue;
|
||||
}
|
||||
else if (vmod.define_v)
|
||||
{
|
||||
do_undefine (p, origin, ebuf);
|
||||
continue;
|
||||
}
|
||||
if (vmod.define_v)
|
||||
v = do_define (p, origin, ebuf);
|
||||
else
|
||||
v = try_variable_definition (fstart, p, origin, 0);
|
||||
@ -1395,8 +1395,11 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
|
||||
|
||||
p = parse_variable_definition (name, &var);
|
||||
if (p == NULL)
|
||||
/* No assignment token, so assume recursive. */
|
||||
var.flavor = f_recursive;
|
||||
{
|
||||
/* No assignment token, so assume recursive. */
|
||||
var.flavor = f_recursive;
|
||||
var.conditional = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (var.value[0] != '\0')
|
||||
@ -1480,8 +1483,8 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
|
||||
else
|
||||
definition[idx - 1] = '\0';
|
||||
|
||||
v = do_variable_definition (&defstart, name,
|
||||
definition, origin, var.flavor, 0);
|
||||
v = do_variable_definition (&defstart, name, definition,
|
||||
origin, var.flavor, var.conditional, 0);
|
||||
free (definition);
|
||||
free (n);
|
||||
return (v);
|
||||
@ -2954,10 +2957,10 @@ construct_include_path (const char **arg_dirs)
|
||||
|
||||
/* Now add each dir to the .INCLUDE_DIRS variable. */
|
||||
|
||||
do_variable_definition (NILF, ".INCLUDE_DIRS", "", o_default, f_simple, 0);
|
||||
do_variable_definition (NILF, ".INCLUDE_DIRS", "", o_default, f_simple, 0, 0);
|
||||
for (cpp = dirs; *cpp != 0; ++cpp)
|
||||
do_variable_definition (NILF, ".INCLUDE_DIRS", *cpp,
|
||||
o_default, f_append, 0);
|
||||
o_default, f_append, 0, 0);
|
||||
|
||||
free ((void *) include_directories);
|
||||
include_directories = dirs;
|
||||
|
@ -708,7 +708,7 @@ initialize_file_variables (struct file *file, int reading)
|
||||
v = do_variable_definition (
|
||||
&p->variable.fileinfo, p->variable.name,
|
||||
p->variable.value, p->variable.origin,
|
||||
p->variable.flavor, 1);
|
||||
p->variable.flavor, p->variable.conditional, 1);
|
||||
}
|
||||
|
||||
/* Also mark it as a per-target and copy export status. */
|
||||
@ -1370,15 +1370,22 @@ shell_result (const char *p)
|
||||
See the try_variable_definition() function for details on the parameters. */
|
||||
|
||||
struct variable *
|
||||
do_variable_definition (const floc *flocp, const char *varname,
|
||||
const char *value, enum variable_origin origin,
|
||||
enum variable_flavor flavor, int target_var)
|
||||
do_variable_definition (const floc *flocp, const char *varname, const char *value,
|
||||
enum variable_origin origin, enum variable_flavor flavor,
|
||||
int conditional, int target_var)
|
||||
{
|
||||
const char *newval;
|
||||
char *alloc_value = NULL;
|
||||
struct variable *v;
|
||||
int append = 0;
|
||||
int conditional = 0;
|
||||
|
||||
/* Conditional variable definition: only set if the var is not defined. */
|
||||
if (conditional)
|
||||
{
|
||||
v = lookup_variable (varname, strlen (varname));
|
||||
if (v)
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Calculate the variable's new value in VALUE. */
|
||||
|
||||
@ -1421,16 +1428,6 @@ do_variable_definition (const floc *flocp, const char *varname,
|
||||
newval = alloc_value;
|
||||
break;
|
||||
}
|
||||
case f_conditional:
|
||||
/* A conditional variable definition "var ?= value".
|
||||
The value is set IFF the variable is not defined yet. */
|
||||
v = lookup_variable (varname, strlen (varname));
|
||||
if (v)
|
||||
goto done;
|
||||
|
||||
conditional = 1;
|
||||
flavor = f_recursive;
|
||||
/* FALLTHROUGH */
|
||||
case f_recursive:
|
||||
/* A recursive variable definition "var = value".
|
||||
The value is used verbatim. */
|
||||
@ -1680,10 +1677,11 @@ do_variable_definition (const floc *flocp, const char *varname,
|
||||
|
||||
If it is a variable definition, return a pointer to the char after the
|
||||
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
|
||||
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
|
||||
conditional : whether it's a conditional assignment
|
||||
Other values in *VAR are unchanged.
|
||||
*/
|
||||
|
||||
@ -1696,11 +1694,13 @@ parse_variable_definition (const char *str, struct variable *var)
|
||||
NEXT_TOKEN (p);
|
||||
var->name = (char *)p;
|
||||
var->length = 0;
|
||||
var->conditional = 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)
|
||||
{
|
||||
const char *start;
|
||||
int c = *p++;
|
||||
|
||||
/* If we find a comment or EOS, it's not a variable definition. */
|
||||
@ -1718,26 +1718,36 @@ parse_variable_definition (const char *str, struct variable *var)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This is the start of a token. */
|
||||
start = p - 1;
|
||||
|
||||
/* If we see a ? then it could be a conditional assignment. */
|
||||
if (c == '?')
|
||||
{
|
||||
var->conditional = 1;
|
||||
c = *p++;
|
||||
}
|
||||
|
||||
/* If we found = we're done! */
|
||||
if (c == '=')
|
||||
{
|
||||
if (!end)
|
||||
end = p - 1;
|
||||
var->flavor = f_recursive;
|
||||
end = start;
|
||||
var->flavor = f_recursive; /* = */
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ':')
|
||||
{
|
||||
if (!end)
|
||||
end = p - 1;
|
||||
end = start;
|
||||
|
||||
/* We need to distinguish :=, ::=, and :::=, versus : outside of an
|
||||
assignment (which means this is not a variable definition). */
|
||||
c = *p++;
|
||||
if (c == '=')
|
||||
{
|
||||
var->flavor = f_simple;
|
||||
var->flavor = f_simple; /* := */
|
||||
break;
|
||||
}
|
||||
if (c == ':')
|
||||
@ -1745,12 +1755,12 @@ parse_variable_definition (const char *str, struct variable *var)
|
||||
c = *p++;
|
||||
if (c == '=')
|
||||
{
|
||||
var->flavor = f_simple;
|
||||
var->flavor = f_simple; /* ::= */
|
||||
break;
|
||||
}
|
||||
if (c == ':' && *p++ == '=')
|
||||
{
|
||||
var->flavor = f_expand;
|
||||
var->flavor = f_expand; /* :::= */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1763,20 +1773,17 @@ parse_variable_definition (const char *str, struct variable *var)
|
||||
switch (c)
|
||||
{
|
||||
case '+':
|
||||
var->flavor = f_append;
|
||||
break;
|
||||
case '?':
|
||||
var->flavor = f_conditional;
|
||||
var->flavor = f_append; /* += */
|
||||
break;
|
||||
case '!':
|
||||
var->flavor = f_shell;
|
||||
var->flavor = f_shell; /* != */
|
||||
break;
|
||||
default:
|
||||
goto other;
|
||||
}
|
||||
|
||||
if (!end)
|
||||
end = p - 1;
|
||||
end = start;
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
@ -1790,12 +1797,15 @@ parse_variable_definition (const char *str, struct variable *var)
|
||||
|
||||
if (c == '$')
|
||||
p = skip_reference (p);
|
||||
|
||||
var->conditional = 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -1854,7 +1864,7 @@ try_variable_definition (const floc *flocp, const char *line,
|
||||
return 0;
|
||||
|
||||
vp = do_variable_definition (flocp, v.name, v.value,
|
||||
origin, v.flavor, target_var);
|
||||
origin, v.flavor, v.conditional, target_var);
|
||||
|
||||
free (v.name);
|
||||
|
||||
|
@ -39,7 +39,6 @@ enum variable_flavor
|
||||
f_recursive, /* Recursive definition (=) */
|
||||
f_expand, /* POSIX :::= assignment */
|
||||
f_append, /* Appending definition (+=) */
|
||||
f_conditional, /* Conditional definition (?=) */
|
||||
f_shell, /* Shell assignment (!=) */
|
||||
f_append_value /* Append unexpanded value */
|
||||
};
|
||||
@ -172,7 +171,7 @@ struct variable *do_variable_definition (const floc *flocp,
|
||||
const char *name, const char *value,
|
||||
enum variable_origin origin,
|
||||
enum variable_flavor flavor,
|
||||
int target_var);
|
||||
int conditional, int target_var);
|
||||
char *parse_variable_definition (const char *line,
|
||||
struct variable *v);
|
||||
struct variable *assign_variable_definition (struct variable *v, const char *line);
|
||||
|
137
tests/scripts/variables/conditional
Normal file
137
tests/scripts/variables/conditional
Normal file
@ -0,0 +1,137 @@
|
||||
# -*-perl-*-
|
||||
|
||||
$description = "Test various flavors of conditional variable setting.";
|
||||
|
||||
$details = "";
|
||||
|
||||
# Test ?=
|
||||
|
||||
run_make_test(q!
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?= $(x)
|
||||
biz?=$(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=10 biz=20");
|
||||
|
||||
run_make_test(q!
|
||||
foo=1
|
||||
biz=2
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?= $(x)
|
||||
biz?=$(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=1 biz=2");
|
||||
|
||||
# Test ?:=
|
||||
|
||||
run_make_test(q!
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?:= $(x)
|
||||
biz?:=$(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=bar biz=baz");
|
||||
|
||||
run_make_test(q!
|
||||
foo=1
|
||||
biz=2
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?:= $(x)$(info expanded)
|
||||
biz?:=$(y)$(info expanded)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=1 biz=2");
|
||||
|
||||
# Test ?::=
|
||||
|
||||
run_make_test(q!
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?::= $(x)
|
||||
biz?::=$(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=bar biz=baz");
|
||||
|
||||
run_make_test(q!
|
||||
foo=1
|
||||
biz=2
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?::= $(x)$(info expanded)
|
||||
biz?::=$(y)$(info expanded)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=1 biz=2");
|
||||
|
||||
# Test ?:::=
|
||||
|
||||
run_make_test(q!
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?:::= $(x)
|
||||
biz?:::=$(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=bar biz=baz");
|
||||
|
||||
run_make_test(q!
|
||||
foo=1
|
||||
biz=2
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?:::= $(x)$(info expanded)
|
||||
biz?:::=$(y)$(info expanded)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
!,
|
||||
'', "foo=1 biz=2");
|
||||
|
||||
# Test ?!=
|
||||
|
||||
run_make_test(q/
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?!= echo $(x)
|
||||
biz?!=echo $(y)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
/,
|
||||
'', "foo=bar biz=baz");
|
||||
|
||||
run_make_test(q/
|
||||
foo=1
|
||||
biz=2
|
||||
x = bar
|
||||
y = baz
|
||||
foo ?!= echo $(x)$(info expanded)
|
||||
biz?!=echo $(y)$(info expanded)
|
||||
x = 10
|
||||
y = 20
|
||||
all:;@: $(info foo=$(foo) biz=$(biz))
|
||||
/,
|
||||
'', "foo=1 biz=2");
|
||||
|
||||
1;
|
Loading…
Reference in New Issue
Block a user