为何Make在静态库比目标文件新时仍重复更新静态库归档?
静态库每次执行make都会重建的问题分析与解决
我通过foo.c、bar.c和baz.c构建静态库libfoo.a,哪怕静态库的时间戳比所有目标文件(foo.o、bar.o、baz.o)都新,每次执行make还是会重建它。以下是我的Makefile及操作步骤:
原Makefile
CC = gcc $(CCFLAGS) CCFLAGS = -std=gnu17 -mtune=corei7 -flto -fopenmp -pthread -Wall -pedantic -O3 -I../include #LDFLAGS = -L../lib -lm -ldds -ldealerV2 LDFLAGS = --verbose SRCS = foo.c bar.c baz.c OBJS = $(patsubst %.c,%.o,$(SRCS) ) LIBRARY = libfoo.a library : $(OBJS) ar -rcs $(LIBRARY) $? ar -t $(LIBRARY) touch -c $(LIBRARY) %.o : %.c $(CC) -c -o $@ $< clean : rm -f *.o a.out
操作步骤与现象
初始环境设置
$ make clean rm -f *.o a.out $ touch *.c $ ls -l total 48 -rw-rw-r-- 1 greg21 greg21 3742 Dec 9 14:02 bar.c -rw-rw-r-- 1 greg21 greg21 3916 Dec 9 14:02 baz.c -rw-rw-r-- 1 greg21 greg21 3220 Dec 9 14:02 foo.c -rw-rw-r-- 1 greg21 greg21 29668 Dec 9 14:02 libfoo.a -rw-rw-r-- 1 greg21 greg21 618 Dec 9 13:58 Makefile
首次执行make(正常更新库)
$ make gcc -std=gnu17 -mtune=corei7 -flto -fopenmp -pthread -Wall -pedantic -O3 -I../include -c -o foo.o foo.c gcc -std=gnu17 -mtune=corei7 -flto -fopenmp -pthread -Wall -pedantic -O3 -I../include -c -o bar.o bar.c gcc -std=gnu17 -mtune=corei7 -flto -fopenmp -pthread -Wall -pedantic -O3 -I../include -c -o baz.o baz.c ar -rcs libfoo.a foo.o bar.o baz.o ar -t libfoo.a foo.o bar.o baz.o touch -c libfoo.a
当前文件状态
$ ls -l total 84 -rw-rw-r-- 1 greg21 greg21 3742 Dec 9 14:02 bar.c -rw-rw-r-- 1 greg21 greg21 10256 Dec 9 14:03 bar.o -rw-rw-r-- 1 greg21 greg21 3916 Dec 9 14:02 baz.c -rw-rw-r-- 1 greg21 greg21 12616 Dec 9 14:03 baz.o -rw-rw-r-- 1 greg21 greg21 3220 Dec 9 14:02 foo.c -rw-rw-r-- 1 greg21 greg21 6456 Dec 9 14:03 foo.o -rw-rw-r-- 1 greg21 greg21 29684 Dec 9 14:03 libfoo.a -rw-rw-r-- 1 greg21 greg21 618 Dec 9 13:58 Makefile
更新libfoo.a时间戳确保它比所有文件新
$ touch libfoo.a $ ls -l total 84 -rw-rw-r-- 1 greg21 greg21 3742 Dec 9 14:02 bar.c -rw-rw-r-- 1 greg21 greg21 10256 Dec 9 14:03 bar.o -rw-rw-r-- 1 greg21 greg21 3916 Dec 9 14:02 baz.c -rw-rw-r-- 1 greg21 greg21 12616 Dec 9 14:03 baz.o -rw-rw-r-- 1 greg21 greg21 3220 Dec 9 14:02 foo.c -rw-rw-r-- 1 greg21 greg21 6456 Dec 9 14:03 foo.o -rw-rw-r-- 1 greg21 greg21 29684 Dec 9 14:04 libfoo.a -rw-rw-r-- 1 greg21 greg21 618 Dec 9 13:58 Makefile
再次执行make(仍重建库,未提示已最新)
$ make ar -rcs libfoo.a foo.o bar.o baz.o ar -t libfoo.a foo.o bar.o baz.o touch -c libfoo.a
问题原因
核心问题在于原Makefile中library是一个伪目标——它没有对应的实体文件,make会默认认为这类目标永远处于需要更新的状态,不会去对比libfoo.a和依赖文件的时间戳。每次执行make时,只要library是默认目标(第一个目标),就会无条件执行其下的所有命令,哪怕libfoo.a已经是最新的。
解决方案
将规则的目标直接改为静态库文件$(LIBRARY)(即libfoo.a),让make识别这是一个实体文件目标,从而自动对比它与依赖的.o文件的时间戳,仅当依赖更新时才重建库。
修改后的Makefile
CC = gcc $(CCFLAGS) CCFLAGS = -std=gnu17 -mtune=corei7 -flto -fopenmp -pthread -Wall -pedantic -O3 -I../include #LDFLAGS = -L../lib -lm -ldds -ldealerV2 LDFLAGS = --verbose SRCS = foo.c bar.c baz.c OBJS = $(patsubst %.c,%.o,$(SRCS) ) LIBRARY = libfoo.a # 直接以静态库文件作为目标,让make自动处理时间戳对比 $(LIBRARY) : $(OBJS) ar -rcs $@ $? ar -t $@ # 可选:ar命令会自动更新库的时间戳,除非有特殊需求,可去掉touch # touch -c $@ %.o : %.c $(CC) -c -o $@ $< clean : rm -f *.o a.out $(LIBRARY)
修改说明
- 目标替换:将伪目标
library改为实体文件目标$(LIBRARY),make会检查该文件是否存在,以及依赖的.o文件是否比它新,以此决定是否执行构建命令。 - 自动变量优化:用
$@(代表当前目标文件)代替硬编码的$(LIBRARY),让规则更通用简洁。 - 清理增强:在
clean目标中加入$(LIBRARY),方便彻底清理生成的静态库。 - 可选调整:
ar -rcs命令会自动更新静态库的时间戳,因此可以去掉touch -c $@,除非你需要强制保持某个特定时间戳。
验证效果
修改后,当libfoo.a的时间戳比所有.o文件都新时,执行make会输出:
make: 'libfoo.a' is up to date.
不会再执行ar等构建命令,符合预期。
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

