Subject: Re: make: asignment modifiers
To: None <current-users@netbsd.org>
From: Simon J. Gerraty <sjg@quick.com.au>
List: current-users
Date: 05/28/2000 00:49:51
> I'm thinking of adding a new make variable modifier '=' (and '+=',
> etc). This message is to solicit feedback on the idea.
Ok, I've done this, and it does just what I need :-)
The .for loop in:
> some/path/file.gz:
> .for t in ${.TARGET} ${.TARGET:R}-blah.gz
> @echo t:T=${t:T}
> @echo t:R:T=${t:R:T}
> .endfor
Can now be written as:
.for i in ${.TARGET} ${.TARGET:R}-blah.gz
@: ${t:=$i}
@echo t:T=${t:T}
@echo t:R:T=${t:R:T}
.endfor
and actually work as expected.
Diffs are below. If no one violenty objects (without offering to
implement :-) to :=[+?!] cf. :[+?!]= I'll commit this shortly (after
updating the man page too).
--sjg
Index: var.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/make/var.c,v
retrieving revision 1.43
diff -u -p -r1.43 var.c
--- var.c 2000/05/14 15:14:41 1.43
+++ var.c 2000/05/27 14:50:16
@@ -1856,6 +1856,23 @@ Var_Parse (str, ctxt, err, lengthPtr, fr
* the form '${x:P}'.
* :!<cmd>! Run cmd much the same as :sh run's the
* current value of the variable.
+ * The := modifiers, actually asign a value to the variable.
+ * Their main purpose is in supporting modifiers of .for loop
+ * iterators which derrive from local variables (eg. .TARGET).
+ * 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:
+ * @: ${t:=${.TARGET}}
+ * ...
+ * It would be neater if :=[+?!] were :[+?!]= but that would be
+ * much messier to implement given the existing ! and ? modifiers.
+ * :=<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 +1887,85 @@ Var_Parse (str, ctxt, err, lengthPtr, fr
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;