You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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)

修改说明

  1. 目标替换:将伪目标library改为实体文件目标$(LIBRARY),make会检查该文件是否存在,以及依赖的.o文件是否比它新,以此决定是否执行构建命令。
  2. 自动变量优化:用$@(代表当前目标文件)代替硬编码的$(LIBRARY),让规则更通用简洁。
  3. 清理增强:在clean目标中加入$(LIBRARY),方便彻底清理生成的静态库。
  4. 可选调整:ar -rcs命令会自动更新静态库的时间戳,因此可以去掉touch -c $@,除非你需要强制保持某个特定时间戳。

验证效果

修改后,当libfoo.a的时间戳比所有.o文件都新时,执行make会输出:

make: 'libfoo.a' is up to date.

不会再执行ar等构建命令,符合预期。

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:04:52