自动从静态库

时间:2015-11-09 16:55:31

标签: c makefile ar

我想知道是否可以设置一个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也不会工作。

3 个答案:

答案 0 :(得分:1)

你可以做任何自动或内置的事情。 make只是不擅长注意这类事情。你可以做的最简单的事情就是解决问题。这个问题是保留一个FORCEd目标,其中包含存档中表示的源文件列表,包含该文件作为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.ob.oc.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)