Implemented a set of assignment modifiers. These solve obscure

problems such as using modifiers on .for loop iterators derived from
local variables (eg .TARGET).
Unless the variable already exists in a global context, these assignments are
local to the current context (this is usually what is wanted).
This commit is contained in:
sjg 2000-05-30 02:32:21 +00:00
parent b8cb7f6356
commit 339f702f71
2 changed files with 146 additions and 4 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.40 2000/04/29 12:18:52 sjg Exp $
.\" $NetBSD: make.1,v 1.41 2000/05/30 02:32:21 sjg Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@ -664,6 +664,46 @@ is the value.
.It Cm sh
If the variable is non-empty it is run as a command and the output
becomes the new value.
.It Cm = Ar str
The variable is assigned the value
.Ar str
after substitution. This modifier and its variations are useful in
obscure situations such as wanting to apply modifiers to
.Cm \&.for
loop iteration variables which won't work due to the way
.Cm \&.for
loops are implemented. These assignment modifiers always expand to
nothing, so if appearing in a rule line by themselves should be
preceded with something to keep
.Nm
happy. As in:
.Bd -literal
use_foo: \&.USE
\&.for i in ${\&.TARGET} ${\&.TARGET:R}\&.gz
@: ${t:=$i}
@echo t:R:T=${t:R:T}
\&.endfor
.Ed
While [?+!]= might look better than =[?+!], given that substitution is
always done before the assignment they are more like variations on
.Cm :=
rather than
.Cm =
in normal variable assignments. Coupled with the obscurity of their
use, the extra complexity in implementing [?+!]= is unwarranted.
.It Cm =? Ar str
As for
.Cm =
but only if the variable does not already have a value.
.It Cm =+ Ar str
Append
.Ar str
to the variable.
.It Cm =! Ar cmd
Assign the output of
.Ar cmd
to the variable.
.El
.El
.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS

View File

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.43 2000/05/14 15:14:41 sjg Exp $ */
/* $NetBSD: var.c,v 1.44 2000/05/30 02:32:21 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -39,14 +39,14 @@
*/
#ifdef MAKE_BOOTSTRAP
static char rcsid[] = "$NetBSD: var.c,v 1.43 2000/05/14 15:14:41 sjg Exp $";
static char rcsid[] = "$NetBSD: var.c,v 1.44 2000/05/30 02:32:21 sjg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
#else
__RCSID("$NetBSD: var.c,v 1.43 2000/05/14 15:14:41 sjg Exp $");
__RCSID("$NetBSD: var.c,v 1.44 2000/05/30 02:32:21 sjg Exp $");
#endif
#endif /* not lint */
#endif
@ -1856,6 +1856,29 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
* the form '${x:P}'.
* :!<cmd>! Run cmd much the same as :sh run's the
* current value of the variable.
* The := modifiers, actually assign a value to the variable.
* Their main purpose is in supporting modifiers of .for loop
* iterators and other obscure uses. They always expand to
* nothing. In a target rule that would otherwise expand to an
* empty line they can be preceded with @: to keep make happy.
* Eg.
*
* foo: .USE
* .for i in ${.TARGET} ${.TARGET:R}.gz
* @: ${t:=$i}
* @echo blah ${t:T}
* .endfor
*
* It would be neater if :=[+?!] were :[+?!]= but that would be
* much messier to implement given the existing ! and ? modifiers.
* Also the fact that substitution is always done on <str> makes
* these modifiers more like variations on the normal ':=' assignment.
* :=<str> Assigns <str> as the new value of variable.
* :=?<str> Assigns <str> as value of variable if
* it was not already set.
* :=+<str> Appends <str> to variable.
* :=!<cmd> Assigns output of <cmd> as the new value of
* variable.
*/
if ((str != (char *)NULL) && haveModifier) {
/*
@ -1870,6 +1893,85 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
printf("Applying :%c to \"%s\"\n", *tstr, str);
}
switch (*tstr) {
case '=':
{
GNode *v_ctxt; /* context where v belongs */
char *emsg;
VarPattern pattern;
int how;
if (v->flags & VAR_JUNK) {
/*
* JUNK vars get name = &str[1]
* we want the full name here.
*/
v->name--;
/*
* We need to strdup() it incase
* VarGetPattern() recurses.
*/
v->name = strdup(v->name);
v_ctxt = ctxt;
} else if (ctxt != VAR_GLOBAL) {
if (VarFind(v->name, ctxt, 0) == (Var *)NIL)
v_ctxt = VAR_GLOBAL;
else
v_ctxt = ctxt;
}
switch ((how = tstr[1])) {
case '+':
case '?':
case '!':
cp = &tstr[2];
break;
default:
cp = ++tstr;
break;
}
delim = '}';
pattern.flags = 0;
if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
NULL, &pattern.rightLen, NULL)) == NULL) {
if (v->flags & VAR_JUNK) {
free(v->name);
v->name = str;
}
goto cleanup;
}
termc = *--cp;
delim = '\0';
switch (how) {
case '+':
Var_Append(v->name, pattern.rhs, v_ctxt);
break;
case '!':
newStr = Cmd_Exec (pattern.rhs, &emsg);
if (emsg)
Error (emsg, str);
else
Var_Set(v->name, newStr, v_ctxt);
if (newStr)
free(newStr);
break;
case '?':
if ((v->flags & VAR_JUNK) == 0)
break;
/* FALLTHROUGH */
default:
Var_Set(v->name, pattern.rhs, v_ctxt);
break;
}
if (v->flags & VAR_JUNK) {
free(v->name);
v->name = str;
}
free(pattern.rhs);
newStr = var_Error;
break;
}
case '@':
{
VarLoop_t loop;