如何使用Makefile将多目录源码的目标文件统一编译存放至单目录
多目录源码统一输出目标文件到单个目录的Makefile实现方案
原有配置的报错根因
之前遇到的“不存在构建obj/a.o目标的规则”错误,本质是两个问题:
- 仅通过
notdir提取不带路径的源文件名生成OBJ列表,但完全没建立每个obj目录下的.o文件和对应全路径源文件的映射关系 - Make的内置隐式推导规则在生成
obj/a.o时,只会在obj目录、当前工作目录下找同名的a.cpp,不会主动去core、utl这些子目录扫源文件,自然就判定没有对应构建规则
正确配置实现
按照你“所有.o文件统一存放至obj根目录”的需求,完整可运行的Makefile配置如下:
# 1. 基础配置:新增源码目录直接往DIRS列表添加即可,无需修改后续逻辑 DIRS := core utl CXX := g++ CXXFLAGS := -Wall -fPIC -O2 # 生成共享库需要*fPIC*编译参数 OBJ_DIR := obj # 2. 扫描所有指定目录下的cpp源文件,保留全路径 SRC := $(foreach dir,$(DIRS),$(wildcard $(dir)/*.cpp)) # 3. 建立源文件到目标文件的映射:core/a.cpp -> obj/a.o、utl/b.cpp -> obj/b.o OBJ := $(foreach src,$(SRC),$(OBJ_DIR)/$(notdir $(src:.cpp=.o))) # 4. 为每个目标文件显式指定对应的源文件和编译命令,绕开Make的隐式推导 define compile_rule $(OBJ_DIR)/$(notdir $(1:.cpp=.o)): $1 @mkdir -p $(OBJ_DIR) $$(CXX) $$(CXXFLAGS) -c $$< -o $$@ endef $(foreach src,$(SRC),$(eval $(call compile_rule,$(src)))) # 5. 后续基于目标文件构建共享库的示例 libdemo.so: $(OBJ) $(CXX) -shared $^ -o $@ # 清理目标 clean: rm -rf $(OBJ_DIR) libdemo.so .PHONY: clean
关键逻辑说明
- 这里通过
eval和call为每个源文件生成显式依赖规则,比如obj/a.o会被明确指定依赖core/a.cpp,编译命令也直接绑定,完全不靠Make的内置自动推导,从根上解决找不到构建规则的报错 - 编译命令里加了
mkdir -p $(OBJ_DIR),保证首次编译时自动创建obj目录,不会出现目录不存在的报错
重名源文件的兼容方案
如果不同源码目录下存在同名的cpp文件(比如core/test.cpp和utl/test.cpp),上面把所有.o拍平到obj根目录的方案会导致两个源文件生成的test.o互相覆盖。这时候可以调整为在obj目录下保留和源码一致的子目录结构,避免文件冲突:
# 替换原有的OBJ映射和编译规则即可 OBJ := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRC)) $(OBJ_DIR)/%.o: %.cpp @mkdir -p $(dir $@) $(CXX) $(CXXFLAGS) -c $< -o $@
这种方式生成的目标文件路径为obj/core/test.o、obj/utl/test.o,不会出现重名覆盖问题。
内容的提问来源于stack exchange,提问作者lucmobz
相关产品推荐
相关产品推荐

