Nokia nmake Product Builder
Customer Support Newsletter

http://www.bell-labs.com/project/nmake/newsletters/

Issue No. 25 - April 2007
Contacts
Articles
  1. alu3.9 Release Schedule
  2. Delaying Variable Expansion
  3. How to report targets that don't build

alu3.9 Release Schedule

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.

index

Delaying Variable Expansion

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.

Side Note

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)

index

How to report targets that don't build

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

index

<<home / newsletters