4.2. Archives and Libraries

UNIX® and Sprite allow you to merge files into an archive using the ar command. Further, if the files are relocatable object files, you can run ranlib on the archive and get yourself a library that you can link into any program you want. The main problem with archives is they double the space you need to store the archived files, since there is one copy in the archive and one copy out by itself. The problem with libraries is you usually think of them as -lm rather than /usr/lib/libm.a and the linker thinks they are out-of-date if you so much as look at them.

PMake solves the problem with archives by allowing you to tell it to examine the files in the archives (so you can remove the individual files without having to regenerate them later). To handle the problem with libraries, PMake adds an additional way of deciding if a library is out-of-date: if the table of contents is older than the library, or is missing, the library is out-of-date.

A library is any target that looks like -lname or that ends in a suffix that was marked as a library using the .LIBS target. .a is so marked in the system makefile. Members of an archive are specified as archive(member[member...]). Thus libdix.a(window.o) specifies the file window.o in the archive libdix.a. You may also use wildcards to specify the members of the archive. Just remember that most the wildcard characters will only find existing files. A file that is a member of an archive is treated specially. If the file does not exist, but it is in the archive, the modification time recorded in the archive is used for the file when determining if the file is out-of-date. When figuring out how to make an archived member target (not the file itself, but the file in the archive - the archive(member) target), special care is taken with the transformation rules, as follows:

Thus, a program library could be created with the following makefile:

.o.a            :
    ...
    rm -f $(.TARGET:T)
OBJS            = obj1.o obj2.o obj3.o
libprog.a       : libprog.a($(OBJS))
    ar cru $(.TARGET) $(.OODATE)
    ranlib $(.TARGET)

This will cause the three object files to be compiled (if the corresponding source files were modified after the object file or, if that does not exist, the archived object file), the out-of-date ones archived in libprog.a, a table of contents placed in the archive and the newly-archived object files to be removed.

All this is used in the makelib.mk system makefile to create a single library with ease. This makefile looks like this:

#
# Rules for making libraries. The object files that make up the library
# are removed once they are archived.
#
# To make several libraries in parallel, you should define the variable
# "many_libraries". This will serialize the invocations of ranlib.
#
# To use, do something like this:
#
# OBJECTS = <files in the library>
#
# fish.a: fish.a($(OBJECTS)) MAKELIB
#
#

#ifndef _MAKELIB_MK
_MAKELIB_MK    =

#include  <po.mk>

.po.a .o.a     :
    ...
    rm -f $(.MEMBER)

ARFLAGS        ?= crl

#
# Re-archive the out-of-date members and recreate the library's table of
# contents using ranlib. If many_libraries is defined, put the ranlib
# off til the end so many libraries can be made at once.
#
MAKELIB        : .USE .PRECIOUS
    ar $(ARFLAGS) $(.TARGET) $(.OODATE)
#ifndef no_ranlib
# ifdef many_libraries
    ...
# endif many_libraries
    ranlib $(.TARGET)
#endif no_ranlib

#endif _MAKELIB_MK