为何Makefile中无法向OBJ变量追加内容,却可修改CFLAGS/SRC?
问题原因分析
Makefile的变量展开分为解析阶段和执行阶段:
- 规则的依赖列表(比如
$(NAME): $(OBJ)里的$(OBJ))是在Makefile加载时的解析阶段就展开的,此时OBJ的值是初始定义的test1.o,后续bonus: OBJ += $(BOBJ)这种目标特定变量的修改,不会影响已经展开的依赖列表。 - 而规则命令里的变量(比如你第二个例子里的
$(SRC)、$(CFLAGS))是在执行阶段才展开的,所以目标特定变量的修改能生效——这就是你看到SRC和CFLAGS变化但OBJ没变化的核心原因。
解决方案
要实现需求,需要让$(NAME)的依赖列表能在执行阶段根据当前目标(all/bonus)动态展开,这里有两种可行方法:
方法1:使用二次展开(.SECONDEXPANSION)
通过Makefile的.SECONDEXPANSION特性,让规则的依赖列表延迟到执行阶段再展开,这样目标特定变量的修改就能生效:
SRC = test1.c BSRC = test2.c OBJ = $(SRC:.c=.o) BOBJ = $(BSRC:.c=.o) NAME = libtest.a CC = gcc all: $(NAME) bonus: OBJ += $(BOBJ) bonus: $(NAME) .SECONDEXPANSION: $(NAME): $$(OBJ) ar rcs $@ $^
这里把依赖列表里的$(OBJ)改成$$(OBJ),结合.SECONDEXPANSION,会在执行阶段重新展开OBJ,此时bonus目标下的OBJ已经追加了BOBJ,ar命令就会包含所有目标文件。
方法2:拆分中间目标
为all和bonus分别定义不同的依赖集合,避免变量冲突:
SRC = test1.c BSRC = test2.c OBJ = $(SRC:.c=.o) BOBJ = $(BSRC:.c=.o) ALL_OBJ = $(OBJ) BONUS_OBJ = $(OBJ) $(BOBJ) NAME = libtest.a CC = gcc all: $(NAME) $(NAME): $(ALL_OBJ) ar rcs $@ $^ bonus: $(NAME)_bonus $(NAME)_bonus: $(BONUS_OBJ) ar rcs $(NAME) $^
这种方法通过两个不同的中间目标分别对应常规和bonus的依赖集合,确保各自的依赖正确。同时也能满足依赖未变更时不执行ar命令的要求。
验证效果
执行make bonus时,会先编译test1.o和test2.o,然后执行ar rcs libtest.a test1.o test2.o;当依赖文件未变更时,会输出Nothing to be done for 'bonus'。
内容的提问来源于stack exchange,提问作者ludmuterol
相关产品推荐
相关产品推荐

