向Makefile添加源目录src2后出现ld链接错误,如何解决?
多源码目录Makefile错误分析与修复
问题背景
原Makefile仅使用src1目录时可正常编译,将SOURCE_DIR修改为包含src1和src2(写法为SOURCE_DIR = src1\ src2)后,出现链接错误:
/usr/bin/ld: cannot find src1: file format not recognized collect2: error: ld returned 1 exit status make: *** [Makefile:22: build/mainDebug] Error 1
原Makefile代码
CPP_COMPILER = g++ CPP_COMPILER_FLAGS = -g -O0 -Wall -Wextra -Wpedantic -Wconversion -std=c++17 EXECUTABLE_NAME = mainDebug CPP_COMPILER_CALL = $(CPP_COMPILER) $(CPP_COMPILER_FLAGS) INCLUDE_DIR = include SOURCE_DIR = src1 BUILD_DIR = build CPP_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp) CPP_OBJECTS = $(patsubst $(SOURCE_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(CPP_SOURCES)) build: $(BUILD_DIR)/$(EXECUTABLE_NAME) TARGET_DEPS = $(CPP_OBJECTS) $(BUILD_DIR)/$(EXECUTABLE_NAME): $(TARGET_DEPS) $(CPP_COMPILER_CALL) $^ -o $@ execute: .$(BUILD_DIR)/$(EXECUTABLE_NAME) $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(CPP_COMPILER_CALL) -I $(INCLUDE_DIR) -c $< -o $@ clean: rm -rf $(BUILD_DIR)/*.o $(BUILD_DIR)/$(EXECUTABLE_NAME)
目录结构
. ├── build ├── include │ ├── mylib.h │ └── test.h ├── Makefile ├── src1 │ ├── main.cc │ └── mylib.cc └── src2 └── test.cc
错误原因
- 源文件匹配逻辑失效:当
SOURCE_DIR为多目录列表时,$(wildcard $(SOURCE_DIR)/*.cpp)会展开为src1/*.cpp src2/*.cpp,但后续的$(patsubst $(SOURCE_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(CPP_SOURCES))无法正确处理多目录路径——$(SOURCE_DIR)被解析为src1 src2,导致替换规则变成src1 src2/%.cpp,最终CPP_OBJECTS中混入了目录名src1,链接器将其误认为目标文件,从而报错。 - 编译规则不支持多目录:原规则
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp只能匹配单个目录下的源文件,无法处理来自src2的文件。
修复方案
步骤1:正确定义多源目录
将SOURCE_DIR定义为空格分隔的列表(换行加反斜杠也可,注意空格):
SOURCE_DIR = src1 src2
步骤2:遍历所有源目录收集源文件
替换原CPP_SOURCES的定义,遍历每个目录获取cpp文件:
CPP_SOURCES = $(foreach dir,$(SOURCE_DIR),$(wildcard $(dir)/*.cpp))
步骤3:生成正确的目标文件路径
替换原CPP_OBJECTS的定义,将每个源文件转换为build目录下的.o文件(若有同名文件,可改为保留目录前缀,比如build/src1_mylib.o避免冲突):
CPP_OBJECTS = $(addprefix $(BUILD_DIR)/,$(patsubst %.cpp,%.o,$(notdir $(CPP_SOURCES))))
步骤4:修改目标文件编译规则
添加vpath指定源文件搜索路径,让make能在多个目录中找到对应的.cpp文件:
vpath %.cpp $(SOURCE_DIR) $(BUILD_DIR)/%.o: %.cpp $(CPP_COMPILER_CALL) -I $(INCLUDE_DIR) -c $< -o $@
修复后的完整Makefile
CPP_COMPILER = g++ CPP_COMPILER_FLAGS = -g -O0 -Wall -Wextra -Wpedantic -Wconversion -std=c++17 EXECUTABLE_NAME = mainDebug CPP_COMPILER_CALL = $(CPP_COMPILER) $(CPP_COMPILER_FLAGS) INCLUDE_DIR = include SOURCE_DIR = src1 src2 BUILD_DIR = build # 遍历所有源目录收集cpp文件 CPP_SOURCES = $(foreach dir,$(SOURCE_DIR),$(wildcard $(dir)/*.cpp)) # 生成build目录下的目标文件路径 CPP_OBJECTS = $(addprefix $(BUILD_DIR)/,$(patsubst %.cpp,%.o,$(notdir $(CPP_SOURCES)))) build: $(BUILD_DIR)/$(EXECUTABLE_NAME) TARGET_DEPS = $(CPP_OBJECTS) $(BUILD_DIR)/$(EXECUTABLE_NAME): $(TARGET_DEPS) $(CPP_COMPILER_CALL) $^ -o $@ execute: ./$(BUILD_DIR)/$(EXECUTABLE_NAME) # 指定源文件搜索路径 vpath %.cpp $(SOURCE_DIR) $(BUILD_DIR)/%.o: %.cpp $(CPP_COMPILER_CALL) -I $(INCLUDE_DIR) -c $< -o $@ clean: rm -rf $(BUILD_DIR)/*.o $(BUILD_DIR)/$(EXECUTABLE_NAME)
(注:顺便修正了execute规则中的路径错误,原.$(BUILD_DIR)/$(EXECUTABLE_NAME)应为./$(BUILD_DIR)/$(EXECUTABLE_NAME))
内容的提问来源于stack exchange,提问作者Gaston
相关产品推荐
相关产品推荐

