Makefiles - Make and the Double colon


make has a little used feature that is signified with the double colon (::). This tells make to associate more than one action for a target. Normally, with a the single colon (:), you can have multiple target and prerequisites to map the dependencies, but only one of them can have an associated action. For example:

aaa.o : aaa.c in.h
	cc -c aaa.c

aaa.o : another.h
aaa.o : yetanother.h
The double colon allows your to do something like this:
libxxx.a :: sub1.o
	ar rv libxxx.a sub1.o

libxxx.a :: sub2.o
	ar rv libxxx.a sub2.o
Where the library libxxx.a depends on sub[12].o and will add them into the library collection as needed.

The double colon mechanism is very useful for automatically generated Makefiles. The actions can be collected together in a verticle sense, as opposed to the usual approach that lists prerequisites in a horizontal sense. The latter is more efficient from a make operational view, but can be difficult to automate Makefile generation for a batch of source files.

The following is a very contrived example that demonstrate some of the shell looping tricks and how to automatically self-generate a Makefile.

The ``header'' part of the Makefile is not automatically generated and is carried from one version to the next. It has the executable mainx depend on the Makefile and below it depends on the .o files. The Makefile itself depends on the sources in the current directory. If you add a new source file, say sub4.c, make will then execute the action for the Makefile target. The action is a very sophisticated use of shell looping. It passes the Makefile through sed to cut out the header part, then echo and append the sentinel lines to Makefile. The for loop keys on every .c file in the current directory. It uses basename to create variables with the .o name. The action uses gcc -MM to parse a source file to generate a target with prerequisites. The following echos generate dependencies for

mainx <- .o file <- .c file, etc.

Next, it creates a collected action, clean, for removing the .o file. Finally, the action does a recursive make to build the executable with the new Makefile!

# LD = gcc SHELL = /bin/sh mainx : Makefile $(LD) -o mainx *.o clean :: -$(RM) mainx Makefile : *.c @sed -e '/^### Do Not edit this line$$/,$$d' Makefile \ > MMM.$$$$ && mv MMM.$$$$ Makefile @echo "### Do Not edit this line" >> Makefile @echo "### Everything below is auto-generated" >> Makefile @for f in *.c ; do echo ===$$f=== 1>&2 ; ff=`basename $$f .c`.o ; \ gcc -MM $$f ; echo ""; echo "mainx : $$ff" ; echo "$$ff : $$f" ; \ echo ' $$(CC) $$(CFLAGS) -c '"$$f" ; echo "" ; echo "clean ::" ; \ echo ' -$$(RM) '"$$ff" ; echo "" ; done >> Makefile @$(MAKE) ### Do Not edit this line ### Everything below is auto-generated main.o: main.c proj.h mainx : main.o main.o : main.c $(CC) $(CFLAGS) -c main.c clean :: -$(RM) main.o sub1.o: sub1.c proj.h mainx : sub1.o sub1.o : sub1.c $(CC) $(CFLAGS) -c sub1.c clean :: -$(RM) sub1.o sub2.o: sub2.c proj.h mainx : sub2.o sub2.o : sub2.c $(CC) $(CFLAGS) -c sub2.c clean :: -$(RM) sub2.o sub3.o: sub3.c proj.h mainx : sub3.o sub3.o : sub3.c $(CC) $(CFLAGS) -c sub3.c clean :: -$(RM) sub3.o
Suppose the source sub4.c is added to the existing project. It need not be explicitly added to the Makefile, just type ``make''. It regenerates the Makefile and builds the executable accordingly.
% make
make[1]: Entering directory `/u/owen/rk/make/src/ex5
cc  -c sub4.c
gcc -o mainx *.o
make[1]: Leaving directory `/u/owen/rk/make/src/ex5'
make: `mainx' is up to date.

This is a fiendishly clever Makefile. normally I stay away from such overly clever make tricks, opting for explicit simplicity and control. However, it does demonstrate the power make and what is possible.


Slide 10