虽然这是一个老问题,但 Etan 已经给出了答案。我尝试像 Etan 一样解决它,但使用“更多制作”和“更少 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)
请注意,这使用了创建目标文件的隐式规则。
BSD ar 为全局符号表创建类似__.SYMDEF 的成员。 -grep -v 用于过滤掉这个成员。
更多细节
LibSources = $(wildcard *.c)
在您的情况下会扩展为 a.c b.c c.c 或任何扩展名为 .c 的文件。
LibObjects = $(patsubst %.c,%.o,$(LibSources))
给出应该在库中的目标文件列表。例如。 a.o, b.o, c.o.
LibContent = $(if $(wildcard $(Lib)),$(shell ar t $(Lib) | grep -v "^__"),)
如果库已经存在,这会给出一个归档对象文件的列表。需要过滤掉索引的条目__.SYMDEF SORTED。
LibRemoves = $(filter-out $(LibObjects),$(LibContent))
这给出了区别,即要删除的目标文件列表。和
SrcRemoves = $(patsubst %.o,%.c,$(LibRemoves))
因此扩展为已删除的源文件列表。
ArDelete = $(if $(LibRemoves),ar d $(Lib) $(LibRemoves),)
如果不需要删除任何内容,则扩展为空字符串,否则为命令ar d libtest.a ...objects-to-delete...
库的先决条件是:所有对象都是存档的成员,如果源文件被删除,对象也会被删除。如果库被修改,则索引会更新:
$(Lib) : $(Lib)($(LibObjects)) $(SrcRemoves)
ranlib $(Lib)
GNU make 支持archive members 的规则。如果目标文件不是存档的成员,则将其添加:
$(Lib)(%.o) : %.o
ar cr $@ $^
如果源文件被删除(或重命名),相应的目标文件会在存档中被删除
$(SrcRemoves) :
$(ArDelete)