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:
archive(member) is made to depend on member.
The transformation from the member's suffix to the archive's suffix is applied to the archive(member) target.
The archive(member)'s .TARGET variable is set to the name of the member if member is actually a target, or the path to the member file if member is only a source.
The .ARCHIVE variable for the archive(member) target is set to the name of the archive.
The .MEMBER variable is set to the actual string inside the parentheses. In most cases, this will be the same as the .TARGET variable.
The archive(member)'s place in the local variables of the targets that depend on it is taken by the value of its .TARGET variable.
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