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:
The double colon allows your to do something like this:aaa.o : aaa.c in.h cc -c aaa.c aaa.o : another.h aaa.o : yetanother.h
Where the library libxxx.a depends on sub.o and will add them into the library collection as needed.libxxx.a :: sub1.o ar rv libxxx.a sub1.o libxxx.a :: sub2.o ar rv libxxx.a sub2.o
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
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.
# 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
% make ===main.c=== ===sub1.c=== ===sub2.c=== ===sub3.c=== ===sub4.c=== make: Entering directory `/u/owen/rk/make/src/ex5 ' cc -c sub4.c gcc -o mainx *.o make: 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.