http://www.bell-labs.com/project/nmake/newsletters/
The team is working towards release alu3.9 targeted for 3Q2007. A beta period prior to the release is also currently targeted for 6/7/2007 through 7/20/2007. If you are interested in trying the beta please let us know at nmake@alcatel-lucent.com.
Here are some of the significant changes in the release in addition to the usual fixes and enhancements.
Sometimes it is important to control when nmake variables are expanded.
Expansion of variables can be delayed by adding a "$" to the front of the
variable. For instance, $(VAR) has no delay, $$(VAR)
is delayed once, $$$(VAR) is delayed twice, etc. Each time a
delayed variable is evaluated one "$" is removed until $(VAR)
is evaluated and expanded.
Variables are expanded at different times depending on how they are used.
For instance, in a simple assignment like x=$(VAR),
x does not get the expanded value of $(VAR)
but rather just the variable. $(VAR) can be changed
after the assignment so expanding x gets the new value.
For example:
$ cat Makefile VAR = one x = $(VAR) VAR = two t : : x = $(x) $ nmake + : x = two
Using += behaves differently. In the assignment
x+=$(VAR), $(VAR) is expanded immediately
so x gets the current value of $(VAR).
This is also true for the := and &=
assignment operators.
$ cat Makefile VAR = one x += $(VAR) VAR = two t : : x = $(x) $ nmake + : x = one
However, if $(VAR) is delayed in the +=
assignment it will behave like = and x will
expand with the new value.
$ cat Makefile VAR = one x += $$(VAR) VAR = two t : : x = $(x) $ nmake + : x = two
Here is a more practical example. The variable CC.PIC is
defined in the probe file as the pic
flag for the current compiler. It can be used with CCFLAGS
to pass the appropriate pic flag to the compiler without hard coding
compiler/platform specific flags in the local makefile.
$ cat Makefile CC = gcc CCFLAGS = $(CC.PIC) libabc.so :: a.c $ nmake + gcc -D_PIC_ -I- -c a.c + /opt/exp/gnu/bin/gcc -G -o libabc.so a.o
Now lets to add CC.PIC to the existing CCFLAGS
using +=.
$ cat Makefile CC = gcc CCFLAGS += $(CC.PIC) libabc.so :: a.c $ nmake make: warning: building a shared library without -D_PIC_ may result in a nonfunc tional or inefficient library - try CCFLAGS+=$$(CC.PIC) + gcc -O -I- -c a.c + /opt/exp/gnu/bin/gcc -G -o libabc.so a.o
We lost the pic flag! What happened? CC.PIC is defined in
the probe file which isn't loaded until after the makefile is parsed and
compiled. Since += expands $(CC.PIC) immediately
the variable is still null because the probe file hasn't been loaded yet.
We need to delay the expansion.
$ cat Makefile CC = gcc CCFLAGS += $$(CC.PIC) libabc.so :: a.c $ nmake + gcc -O -D_PIC_ -I- -c a.c + /opt/exp/gnu/bin/gcc -G -o libabc.so a.o
Let's move on to another example. Variables in the target and prerequisite lists are expanded when the makefile is compiled. When building a shared library we can use another probe variable, CC.SUFFIX.SHARED, to set the proper shared library suffix for the current platform.
$ cat Makefile CC = gcc CCFLAGS += $$(CC.PIC) libabc$(CC.SUFFIX.SHARED) :: a.c $ nmake + gcc -O -D_PIC_ -I- -c a.c + gcc -O -o libabc a.o
Oops, notice it created libabc instead of libabc.so (or libabc.sl on
HP-UX). Again, since CC.SUFFIX.SHARED is expanded when the
makefile is compiled the probe file hasn't been loaded yet and the variable
is null. The variable needs to be delayed once.
$ cat Makefile CC = gcc CCFLAGS += $$(CC.PIC) libabc$$(CC.SUFFIX.SHARED) :: a.c $ nmake + gcc -O -D_PIC_ -I- -c a.c + /opt/exp/gnu/bin/gcc -G -o libabc.so a.o
Variables in action blocks are expanded at build time, after the makefile
is compiled when nmake is building targets. The following example shows the
difference between a variable in the prerequisite list and in the action
block. The variable FILES dynamically gets the list of .c
files in the current directory.
$ ls -l
total 4
-rw-r--r-- 1 richb richb 80 Apr 18 10:46 Makefile
-rw-r--r-- 1 richb richb 28 Apr 17 16:56 a.c
$ cat Makefile
FILES = $(".":L=*.c)
mytarget : $(FILES)
: FILES = $(FILES)
: prereqs = $(*)
$ nmake
+ : FILES = a.c
+ : prereqs = a.c
The inital build looks normal. Now let's add another .c file.
$ ls -l total 24 -rw-r--r-- 1 richb richb 80 Apr 18 10:46 Makefile -rw-r--r-- 1 richb richb 3311 Apr 18 10:46 Makefile.mo -rw-r--r-- 1 richb richb 15123 Apr 18 10:46 Makefile.ms -rw-r--r-- 1 richb richb 28 Apr 17 16:56 a.c -rw-r--r-- 1 richb richb 28 Apr 18 10:47 b.c $ nmake + : FILES = a.c b.c + : prereqs = a.c
FILES expanded as expected in the action block but b.c
wasn't added to the prerequisite list. This is because when the makefile
was initially compiled the variable in the prerequisite list was expanded
to a.c and saved in the Makefile.mo. The second build loads
the compiled makefile and doesn't see the changes. (Note, this is not
a problem when the makefile defines an explicit list of files.) Since the
variable in the action block is expanded at build time we see the updated
value there. To fix the prerequisite list lets delay that variable.
$ cat Makefile
FILES = $(".":L=*.c)
mytarget : $$(FILES)
: FILES = $(FILES)
: prereqs = $(*)
$ nmake
+ : FILES = a.c b.c
+ : prereqs = a.c b.c
And add another .c file.
ls -l total 24 -rw-r--r-- 1 richb richb 81 Apr 18 10:48 Makefile -rw-r--r-- 1 richb richb 3316 Apr 18 10:48 Makefile.mo -rw-r--r-- 1 richb richb 15191 Apr 18 10:48 Makefile.ms -rw-r--r-- 1 richb richb 28 Apr 17 16:56 a.c -rw-r--r-- 1 richb richb 28 Apr 18 10:47 b.c -rw-r--r-- 1 richb richb 28 Apr 18 10:50 c.c $ nmake + : FILES = a.c b.c c.c + : prereqs = a.c b.c c.c
Now it looks like what we want.
For more information on variable expansion and delayed variables see Chapter 3 of the User's Guide.
To simplify the examples we used $(".":L=*.c) to get the list
of .c files in the current directory. Note that this method will not
viewpath, it only looks in the physical current directory, so this method
is not recommended for general use. Instead use something like the
following which will look through the viewpath.
.SOURCE.tmp : . FILES = $(*.SOURCE.tmp:L=*.c)
Here is a quick tip showing how to report targets that failed to build.
If you have a variable containing all the targets in the makefile then
use it with the .FAILED special atom and the -k
(keepgoing) command line option. (If you don't want to set such a
variable keep reading for another option.) In the following example
b.c fails to compile so target t2 is reported.
$ cat Makefile CC = gcc TARGETS = t1 t2 t3 :ALL: t1 :: a.c t2 :: b.c t3 :: c.c .DONE: .MAKE print -- ++ targets failed: $(TARGETS:A=.FAILED) $ nmake -k + gcc -O -I- -c a.c + gcc -O -o t1 a.o + gcc -O -I- -c b.c b.c: In function `main': b.c:4: error: syntax error before "return" make: *** exit code 1 making b.o + gcc -O -I- -c c.c + gcc -O -o t3 c.o ++ targets failed: t2 make: *** 1 action failed
If we fix b.c and rerun we get:
$ nmake -k + gcc -O -I- -c b.c + gcc -O -o t2 b.o ++ targets failed:
If you don't want to leave it blank when all targets build successfully
use the :Y (yeild) edit operator to print a message when
the list is null.
$ cat Makefile CC = gcc TARGETS = t1 t2 t3 :ALL: t1 :: a.c t2 :: b.c t3 :: c.c .DONE: .MAKE print -- ++ targets failed: $(TARGETS:A=.FAILED:@Y??none?o) $ nmake -k + gcc -O -I- -c a.c + gcc -O -o t1 a.o + gcc -O -I- -c b.c + gcc -O -o t2 b.o + gcc -O -I- -c c.c + gcc -O -o t3 c.o ++ targets failed: none
If you don't have a TARGETS variable or would like more
detailed information you can try using the $(...) automatic
variable, however it may give too much info. For this example we'll
break b.c again.
$ cat Makefile CC = gcc :ALL: t1 :: a.c t2 :: b.c t3 :: c.c .DONE: .MAKE print -- ++ targets failed: $(...:A=.FAILED:@Y??none?o) $ nmake -k + gcc -O -I- -c a.c + gcc -O -o t1 a.o + gcc -O -I- -c b.c b.c: In function `main': b.c:4: error: syntax error before "return" make: *** exit code 1 making b.o + gcc -O -I- -c c.c + gcc -O -o t3 c.o ++ targets failed: t2 .ALL b.o make: *** 1 action failed
You might want to filter the output to get your desired report.
The following adds a :N (shell file match) edit operator
to strip .* targets.
cat Makefile CC = gcc :ALL: t1 :: a.c t2 :: b.c t3 :: c.c .DONE: .MAKE print -- ++ targets failed: $(...:A=.FAILED:N!=.*:@Y??none?o) $ nmake -k + gcc -O -I- -c b.c b.c: In function `main': b.c:4: error: syntax error before "return" make: *** exit code 1 making b.o ++ targets failed: t2 b.o make: *** 1 action failed
<<home / newsletters