我想知道是否可以设置一个makefile规则,以便自动删除库中不再存在的目标文件,而无需进行干净的构建。我有这样的makefile设置。
SRC_FILES = a.c b.c c.c
libtest.a : $(SRC_FILES:.c=.o)
ar -rcs $@ $?
%.o : %.c
gcc -o $@ -c $<
现在假设我从c.c
删除SRC_FILES
,我希望下一个make run从存档中删除相应的目标文件。有没有办法做到这一点,而无需运行干净的构建?首先删除存档然后重建它并不起作用,因为当库比其所有依赖项更新时,永远不会调用规则。如果没有真正改变的话,我也不想重建图书馆,所以要成为一个.PHONY也不会工作。
答案 0 :(得分:1)
你可以做任何自动或内置的事情。 make
只是不擅长注意这类事情。你可以做的最简单的事情就是解决问题。这个问题是保留一个FORCE
d目标,其中包含存档中表示的源文件列表,包含该文件作为libtest.a
的先决条件,并将文件内容与文件内容进行比较。存档和重建/添加/删除/等。适当地往/来自图书馆。
libtest.lst: FORCE $(SRC_FILES:.c=.o)
printf '%s\\n' $(filter-out $<,$^) > $@
libtest.a: libtest.lst $(SRC_FILES:.c=.o)
ar t $@ > $@.contents
if diff -q $@.contents libtest.lst; then \
ar ....; \
fi
rm $@.contents
或者,如果你不关心避免重建,请忘记列表/差异等。然后重新运行ar
命令来构建存档。
作为一项额外的改进,您可以将差异逻辑添加到libtest.lst
配方中,以便它只在libtest.lst
文件发生更改时才更新(以避免make
认为需要运行当图书馆内容没有改变时libtest.a
规则。这样的事情。
libtest.lst: FORCE $(SRC_FILES:.c=.o)
printf '%s\\n' $(filter-out $<,$^) | sort > $@.tmp
cmp -s $@ $@.tmp || mv $@.tmp $@
rm -f $@.tmp
libtest.a: libtest.lst $(SRC_FILES:.c=.o)
ar -rcs $@ $(filter-out $<,$?)
答案 1 :(得分:1)
另一种健壮的方法是使所有目标文件,档案,共享库和可执行文件都依赖于构建它们的Makefile
。在配方中,您$(filter-out Makefile,$^)
从编译器,ar和链接器命令行中过滤出Makefile
。最适合non-recursive makefiles。
这样,每当您更改Makefile
时,它都会自动重建并重新链接。理想的方法是仅在更改编译器选项的情况下重建。可能但可能需要付出一些额外的努力,而这可能不值得(ninja
自动完成,但是由于ninja
构建文件通常由更高级别的构建系统(例如{{1} },该功能几乎毫无意义。
此外,如果您不分发CMake
文件,则可能希望将 thin 存档与.a
ar
选项一起使用,以避免不必要地复制目标文件进入存档。
GNU
T
可以选择创建一个 thin 档案,该档案包含符号索引和对档案成员文件原始副本的引用。这对于构建要在本地构建树中使用的库很有用,在该树中,可重定位的对象应该保持可用,并且复制每个对象的内容只会浪费时间和空间。
答案 2 :(得分:0)
尽管这是一个古老的问题,但Etan已经给出了答案。我试图像伊斯坦(Etan)一样解决它,但使用了“更多制作”和“更少重击”:
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)