Makefile仅编译列表首个文件:为何test.c未被编译?
问题:为何test.c未被编译?
问题场景
编写了如下Makefile代码:
SRCS = ../compilation-test2/src/test2.c ./test.c OBJS = $(foreach src,$(SRCS),$(patsubst %.c,build/%.o,$(notdir $(src)))) define build_obj $(info $(1)) obj_file := $(patsubst %.c, build/%.o, $(notdir $(1))) src_file := $(1) $(info $(obj_file)) $(info $(src_file)) $(obj_file): $(src_file) g++ -c $(src_file) -o $(obj_file) endef $(foreach src,$(SRCS),$(eval $(call build_obj,$(src)))) all: $(OBJS) g++ $^ -o build/exec_file clean: rm -rf build/*
对应的目录结构:
$ tree compilation-test compilation-test ├── Makefile ├── build └── test.c 1 directory, 2 files $ tree compilation-test2/ compilation-test2/ ├── include │ └── test2.h └── src └── test2.c
执行make命令后,仅test2.c被编译:
$ make ../compilation-test2/src/test2.c ./test.c build/test2.o ../compilation-test2/src/test2.c g++ -c ../compilation-test2/src/test2.c -o build/test2.o $ ls build/ test2.o
询问:为何test.c未被编译?
原因分析与解决办法
问题根源
你在build_obj宏中定义的obj_file和src_file是全局变量,每次调用宏时都会覆盖之前的变量值;再加上:=的立即展开特性,导致处理第二个源文件./test.c时,变量被前一次处理test2.c的内容污染,最终生成的编译规则并未正确关联test.c和build/test.o。
从执行输出的info信息也能看出异常:处理./test.c时,输出的obj_file仍是build/test2.o,src_file仍是../compilation-test2/src/test2.c,说明宏内部的变量赋值完全被前一次的内容覆盖了。
解决办法
办法1:使用局部变量(GNU Make 3.81+支持)
在宏内的变量定义前加上local关键字,确保每次调用宏时变量都是独立的局部变量,不会互相干扰:
define build_obj $(info $(1)) local obj_file := $(patsubst %.c, build/%.o, $(notdir $(1))) local src_file := $(1) $(info $(obj_file)) $(info $(src_file)) $(obj_file): $(src_file) g++ -c $(src_file) -o $(obj_file) endef
办法2:去掉中间变量,使用自动变量
直接利用Make的自动变量简化宏定义,避免变量污染问题。其中$$<代表第一个依赖文件(即源文件),$$@代表当前目标文件(即.o文件),注意要使用$$来转义,确保eval展开时能正确解析为自动变量:
define build_obj $(info $(1)) $(patsubst %.c, build/%.o, $(notdir $(1))): $(1) g++ -c $$< -o $$@ endef
修改后重新执行make,就能同时编译test.c和test2.c,生成对应的build/test.o和build/test2.o文件。
内容的提问来源于stack exchange,提问作者andre.hre
相关产品推荐
相关产品推荐

