Programming Utilities Guide

Maintaining a Large Library as a Hierarchy of Subsidiaries

When maintaining a very large library, it is sometimes easier to break it up into smaller, subsidiary libraries, and use make to combine them into a complete package. Although you cannot combine libraries directly with ar, you can extract the member files from each subsidiary library, then archive those files in another step, as shown in the following example:

$ ar xv libx.a 
x - x1.o 
x - x2.o 
x - x3.o 
$ ar xv liby.a 
x - y1.o 
x - y2.o 
$ ar rv libz.a *.o 
a - x1.o 
a - x2.o 
a - x3.o 
a - y1.o 
a - y2.o 
ar: creating libz.a

A subsidiary library is maintained using a makefile in its own directory, along with the (object) files it is built from. The makefile for the complete library typically makes a symbolic link to each subsidiary archive, extracts their contents into a temporary subdirectory, and archives the resulting files to form the complete package.

The next example updates the subsidiary libraries, creates a temporary directory in which to put extracted the files, and extracts them. It uses the * (shell) wild card within that temporary directory to generate the collated list of files. While filename wild cards are generally frowned upon, this use of the wild card is acceptable because a new directory is created whenever the target is built. This guarantees that it contains only files extracted during the current make run.


Note -

In general, use of shell filename wild cards is considered to be bad form in a makefile. If you do use them, you need to take steps to insure that it excludes spurious files by isolating affected files in a temporary subdirectory


The example relies on a naming convention for directories. The name of the directory is taken from the basename of the library it contains. For instance, if libx.a is a subsidiary library, the directory that contains it is named libx.

It makes use of suffix replacements in dynamic-macro references to derive the directory name for each specific subdirectory. (You can verify that this is necessary.) It uses a shell for loop to successively extract each library and a shell command substitution to collate the object files into proper sequence for linking (using lorder and tsort) as it archives them into the package. Finally, it removes the temporary directory and its contents.

# Makefile for collating a library from subsidiaries.  

CFLAGS= -O 

.KEEP_STATE:
.PRECIOUS:  libz.a

all: lib.a 

libz.a: libx.a liby.a 
        	-rm -rf tmp 
        	-mkdir tmp 
        	set -x ; for i in libx.a liby.a ; \ 
              		do ( cd tmp ; ar x ../$$i ) ; done 
        	( cd tmp ; rm -f *_*_.SYMDEF ; ar cr ../$@ `lorder * | tsort` ) 
        	-rm -rf tmp libx.a liby.a 

libx.a liby.a: FORCE 
        	-cd $(@:.a=) ; $(MAKE) $@ 
        	-ln -s $(@:.a=)/$@ $@ 
FORCE: 

For the sake of clarity, this example omits support for alternate variants, as well as the targets for clean, install, and test (does not apply since the source files are in the subdirectories).

The rm -f *_*_.SYMDEF command embedded in the collating line prevents a symbol table in a subsidiary (produced by running ar on that library) from being archived in this library.

Because the nested make commands build the subsidiary libraries before the current library is processed, you can extend this makefile to account for libraries built from both subsidiaries and object files in the current directory. You need to add the list of object files to the dependency list for the library and a command to copy them into the temporary subdirectory for collation with object files extracted from subsidiary libraries.

# Makefile for collating a library from subsidiaries and local objects.  

CFLAGS= -O 

.KEEP_STATE:
.PRECIOUS:  libz.

OBJECTS= map.o calc.o draw.o

all: libz.a 

libz.a: libx.a liby.a $(OBJECTS) 
         	-rm -rf tmp 
         	-mkdir tmp 
         	-cp $(OBJECTS) tmp 
          set -x ; for i in libx.a liby.a ; \ 
              		do ( cd tmp ; ar x ../$$i ) ; done 
         	( cd tmp ; rm -f *_*_.SYMDEF ; ar cr ../$@ \
                `lorder * | tsort` ) 
          -rm -rf tmp lix.a liby.a 

libx.a liby.a: FORCE 
         	-cd $(@:.a=) ; $(MAKE) $@ 
         	-ln -s $(@:.a=)/$@ $@ 
FORCE: