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;