Although it's an old question and Etan already gave an answer. I tried to solve it like Etan but with "more make" and "less bash":
LibSources = $(wildcard *.c)
LibObjects = $(patsubst %.c,%.o,$(LibSources))
Lib := libtest.a
LibContent = $(if $(wildcard $(Lib)),$(shell ar t $(Lib) | grep -v "^__"),)
LibRemoves = $(filter-out $(LibObjects),$(LibContent))
SrcRemoves = $(patsubst %.o,%.c,$(LibRemoves))
ArDelete = $(if $(LibRemoves),ar d $(Lib) $(LibRemoves),)
.PHONY: all clean
all: $(Lib)
clean:
$(RM) $(Lib)
$(Lib)(%.o) : %.o
ar cr $@ $^
$(SrcRemoves) :
$(ArDelete)
$(Lib) : $(Lib)($(LibObjects)) $(SrcRemoves)
ranlib $(Lib)
Note that this uses the implicit rule for creating object files.
BSD ar creates members like __.SYMDEF
for the global symbol table. The -grep -v
is used to filter out this member.
A bit more details
LibSources = $(wildcard *.c)
would expand in your case to a.c b.c c.c or what ever files with extensions .c
you have.
LibObjects = $(patsubst %.c,%.o,$(LibSources))
gives the list of object files that should be in the library. For example. a.o
, b.o
, c.o
.
LibContent = $(if $(wildcard $(Lib)),$(shell ar t $(Lib) | grep -v "^__"),)
If the library already exists this gives a list of archived object files. The entry __.SYMDEF SORTED
for the index needs to be filtered out.
LibRemoves = $(filter-out $(LibObjects),$(LibContent))
That gives the difference, i.e. the list of object files to delete. And
SrcRemoves = $(patsubst %.o,%.c,$(LibRemoves))
therefore expands to the list of source files that were removed.
ArDelete = $(if $(LibRemoves),ar d $(Lib) $(LibRemoves),)
If nothing needs to be deleted it expands to an empty string, otherwise to the command ar d libtest.a ...objects-to-delete...
The prerequisites for the library are: all objects are members of the archive, and objects are removed if a source file was removed. In case the library was modified the index gets updated:
$(Lib) : $(Lib)($(LibObjects)) $(SrcRemoves)
ranlib $(Lib)
GNU make supports rules for archive members. If an object file is not a member of the archive it gets added:
$(Lib)(%.o) : %.o
ar cr $@ $^
If source files were deleted (or renamed) the corresponding object files get deleted in the archive
$(SrcRemoves) :
$(ArDelete)