Subject: New make modifiers: :C///W, :tW, and :[]
To: None <tech-userlevel@NetBSD.org>
From: Alan Barrett <apb@cequrux.com>
List: tech-userlevel
Date: 07/31/2003 16:45:01
Simon Gerraty, Luke Mewburn and I have been discussing make modifiers.
Here are some suggestions from that process.

make(1) currently has a deeply ingrained idea that variables contain
space-separated words, and modifiers are applied separately to each
word.  For example, in the following:

	VAR= one two three
	test:
		echo ${VAR:C/ .*//}

the :C/// modifier will not do anything, because it's not applied to the
string "one two three", it's applied separately to the strings "one",
"two" and "three".

Having modifiers be applied separately to each word is not always
desirable.  For example, given

	CC= /usr/bin/gcc -Wall

we might want to check whether /usr/bin/gcc exists.  In revision 1.5
of share/mk/bsd.endian.mk, Luke Mewburn addressed this problem using
something like

	.if exists(${CC:ts::C/:.*$//})

Here, the ":ts:" converts the spaces to colons, and then :C/// sees only
one word.  It's debatable whether this should even have worked (perhaps
:C/// should still have seen multiple separate words, even though the
word separator had been changed).  It seems desirable to have an easier
mechanism to do this kind of thing.

We could add a W flag to the :C/// and :S/// modifiers, so that
the above example could be written

	CC= /usr/bin/gcc -Wall
	.if exists(${CC:C/ .*$//W})

(Mnemonic: "W" means "treat the entire value as one big Word, instead of
as several small words".)

Some other modifiers might also benefit from the ability to treat
the entire string as a single entity, instead of as a series of
space-separated words.  For example, :Mpattern and :Npattern might want
to match the pattern against the entire value instead of against one
word at a time.

To address this desire, we could add :tW and :tw modifiers. :tW would
set a flag that makes all subsequent modifiers treat the entire value as
a single string, and :tw would reverse that, making the value be treated
as a sequence of space-separated words.  Then the earlier example could
be written

	CC= /usr/bin/gcc -Wall
	.if exists(${CC:tW:C/ .*$//})

It would sometimes be useful to address individual words in a variable
expansion.  We could add a :[] modifier to do this, and it could even
subsume the functionality of :tW and :tw, as follows:

	:[1]   first word
	:[2]   second word
	:[-1]  last word
	:[0]   entire string as a single word
		    (equivalent to :tW)
	:[*]   entire string as separate words
		    (reverses the effect of :[0]; equivalent to :tw)

Using this modifier, the earlier example could we written

	CC= /usr/bin/gcc -Wall
	.if exists(${CC:[1]})

Simon Gerraty would prefer to use :t[] instead of :[], because of
concerns about potential incompatibility between makefile syntax between
NetBSD/FreeBSD/OpenBSD.  I think that the risk of another BSD using :[
for an incompatible purpose is low enough not to matter, and we could
even discuss it with the other BSDs before implementing.

So, my questions are:

 * should we implement :[] or :t[], and if so, which one?
 * should we implement :tW and :tw?
 * should we implement :C///W and :S///W?

By the way, I have written code for all the above options.  All that
remains is to delete the parts that people don't want in NetBSD, clean
up the remainder a little, update the man page, and apply the patches.

--apb (Alan Barrett)