Search code examples
cmakefileunix-ar

Automatically remove files from static library


I am wondering if it is possible to setup a makefile rule so as to automatically remove object files which are no longer present in the library without having to do a clean build. I have my makefile setup like this.

SRC_FILES = a.c b.c c.c

libtest.a : $(SRC_FILES:.c=.o)
    ar -rcs $@ $?

%.o : %.c
    gcc -o $@ -c $<

Now suppose i remove c.c from SRC_FILES, I want the next make run to delete the corresponding object file from the archive. Is there any way to do this without having to run a clean build? Deleting the archive first and then rebuilding it doesn't work since the rule is never invoked when the library is more recent than all its dependencies. I also don't wanna rebuild the library if nothing actually changed, so making it a .PHONY wont work either.


Solution

  • There's nothing automatic or built-in that you can do. make just isn't very good at noticing this sort of thing. The simplest thing you can do that would "solve" this problem would be to keep a FORCEd target that contains a list of the source files represented in the archive, include that file as a prerequisite of libtest.a and compare the contents of the file against the contents of the archive and rebuild/add/delete/etc. to/from the library as appropriate.

    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
    

    Or if you don't care about avoiding the rebuilding forget the listing/diffing/etc. and just re-run the ar command to build the archive.

    As an additional improvement you could add the diffing logic to the libtest.lst recipe instead so that it only updates the libtest.lst file if it changes (to avoid make thinking it needs to run the libtest.a rule when the library contents haven't changed). Something like this.

    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 $<,$?)