如何合并Makefile目标?合并失败原因及解决方法
Makefile目标规则合并错误分析与解决
问题背景
原有一段可正常运行的Makefile代码,尝试合并其中的目标规则后,执行时出现报错:gcc: error: toto.c no such file or directory。
原可运行代码
c_file_path := toto.c asm_file_path := toto.S c_files = $(notdir $(c_file_path)) asm_files = $(notdir $(asm_file_path)) vpath %.c $(dir $(c_file_path)) vpath %.S $(dir $(asm_file_path)) OBJDIR=objs/ c_objs:=$(addprefix $(OBJDIR),$(patsubst %.c,%.o,$(c_files))) asm_objs:=$(addprefix $(OBJDIR),$(patsubst %.S,%.S.o,$(asm_files))) $(info c_files $(c_files)) $(info s_files $(asm_files)) $(info c_objs $(c_objs)) $(info asm_objs $(asm_objs)) %.c: @touch $@ %.S: @touch $@ objs/%.o:%.c @mkdir -p $(OBJDIR) @gcc -c $^ -o $@ objs/%.S.o:%.S @mkdir -p $(OBJDIR) @gcc -c $^ -o $@ clean: @rm -rf objs/* all: $(c_objs) $(asm_objs) @ls $(c_objs) $(asm_objs) @echo "hello"
修改后的错误代码
c_file_path := toto.c asm_file_path := toto.S c_files = $(notdir $(c_file_path)) asm_files = $(notdir $(asm_file_path)) vpath %.c $(dir $(c_file_path)) vpath %.S $(dir $(asm_file_path)) OBJDIR=objs/ c_objs:=$(addprefix $(OBJDIR),$(patsubst %.c,%.o,$(c_files))) asm_objs:=$(addprefix $(OBJDIR),$(patsubst %.S,%.S.o,$(asm_files))) $(info c_files $(c_files)) $(info s_files $(asm_files)) $(info c_objs $(c_objs)) $(info asm_objs $(asm_objs)) %.c %.S: @touch $@ objs/%.o objs/%.S.o:%.c %.S @mkdir -p $(OBJDIR) @gcc -c $^ -o $@ clean: @rm -rf objs/* all: $(c_objs) $(asm_objs) @ls $(c_objs) $(asm_objs) @echo "hello"
报错信息
gcc: error: toto.c no such file or directory
错误原因
问题出在编译规则的合并方式错误:
- 依赖关联错误:
objs/%.o objs/%.S.o:%.c %.S这种多目标多依赖的写法,会让每个目标同时绑定%.c和%.S两个依赖。比如构建objs/toto.o时,Make会认为它需要toto.c和toto.S;构建objs/toto.S.o时,会错误地认为它需要toto.c和toto.S(实际只需要toto.S)。 - 命令参数错误:
$^会展开所有依赖文件,导致gcc尝试同时编译两个源文件,不符合单文件编译的需求。 - 模式匹配歧义:对于
objs/%.S.o目标,%会匹配toto,进而依赖toto.c,但该文件要么未触发生成规则,要么根本不需要,最终导致gcc找不到文件。
正确的目标合并方法
1. 共享命令的模式规则合并
将两个编译规则的目标-依赖对分别列出,共享同一组命令,Make会自动为每个目标匹配对应的依赖:
objs/%.o: %.c objs/%.S.o: %.S @mkdir -p $(OBJDIR) @gcc -c $^ -o $@
这种写法既实现了代码复用,又保证每个目标只依赖对应的源文件,$^会正确展开为单个依赖文件。
2. 静态模式规则合并
如果明确知道目标列表,用静态模式规则更清晰,直接绑定目标与对应的源文件模式:
$(c_objs): objs/%.o: %.c $(asm_objs): objs/%.S.o: %.S @mkdir -p $(OBJDIR) @gcc -c $^ -o $@
这种方式避免了模式匹配的歧义,适合目标列表固定的场景。
修正后的完整Makefile
c_file_path := toto.c asm_file_path := toto.S c_files = $(notdir $(c_file_path)) asm_files = $(notdir $(asm_file_path)) vpath %.c $(dir $(c_file_path)) vpath %.S $(dir $(asm_file_path)) OBJDIR=objs/ c_objs:=$(addprefix $(OBJDIR),$(patsubst %.c,%.o,$(c_files))) asm_objs:=$(addprefix $(OBJDIR),$(patsubst %.S,%.S.o,$(asm_files))) $(info c_files $(c_files)) $(info s_files $(asm_files)) $(info c_objs $(c_objs)) $(info asm_objs $(asm_objs)) %.c %.S: @touch $@ # 正确合并的编译规则 objs/%.o: %.c objs/%.S.o: %.S @mkdir -p $(OBJDIR) @gcc -c $^ -o $@ clean: @rm -rf objs/* all: $(c_objs) $(asm_objs) @ls $(c_objs) $(asm_objs) @echo "hello"
内容的提问来源于stack exchange,提问作者Guillaume D
相关产品推荐
相关产品推荐

