如何编写可同时生成主程序与doctest测试程序两个可执行文件的Makefile
Makefile适配doctest测试目录及编译问题解决方案
需求背景
原始项目结构如下:
- src 目录:内含 file.cpp、main.cpp
- include 目录:内含 file.h
- Makefile 文件
原始Makefile代码:
TARGET_EXEC := main CC := g++ BUILD_DIR := . SRC_DIR := src OBJ_DIR := obj SRC := $(shell find $(SRC_DIR) -name '*.cpp') OBJ := $(SRC:%=$(OBJ_DIR)/%.o) DEPS := $(OBJ:.o=.d) INC_DIR := $(shell find $(SRC_DIR) -type d) INC_FLAGS := $(addprefix -I,$(INC_DIR)) CPPFLAGS := $(INC_FLAGS) -MMD -MP $(BUILD_DIR)/$(TARGET_EXEC): $(OBJ) $(CC) $(OBJ) -o $@ $(LDFLAGS) $(OBJ_DIR)/%.cpp.o: %.cpp @ mkdir -p $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ .PHONY: clean clean: rm -r $(OBJ_DIR) main -include $(DEPS)
需新增 test 目录存放基于doctest框架的 tests.cpp 代码,最终生成main(主程序)和tests(测试程序)两个可执行文件。
历史修改问题分析
第一次修改后问题:两个可执行文件功能完全一致
你怀疑的编译规则段没有错误,问题出在链接逻辑:
- 测试程序的依赖没有包含test目录下的源文件对应目标文件,编译阶段根本没有处理
tests.cpp - 测试程序链接时错误引入了src下所有目标文件,包含
src/main.o,导致两个可执行文件的链接内容完全一致,运行效果自然相同
第二次修改后问题:测试程序链接报undefined reference to 'main'
编译逻辑已经正确,问题出在两处:
- 测试程序链接仅引入了测试代码的目标文件,没有引入业务代码的目标文件,测试用例无法调用业务逻辑
- doctest的入口宏配置错误,无法自动生成测试程序的main入口函数
最终正确配置
1. Makefile修改
核心是拆分业务代码和主程序入口的目标文件,测试程序不引入src/main.o,同时补充业务代码依赖:
TARGET_EXEC := main TEST_EXEC := tests CC := g++ BUILD_DIR := . SRC_DIR := src OBJ_DIR := obj TEST_DIR := test # 拆分src文件:排除主入口的业务代码、主程序入口代码、测试代码 SRC_MAIN := $(SRC_DIR)/main.cpp SRC_BUSINESS := $(filter-out $(SRC_MAIN), $(shell find $(SRC_DIR) -name '*.cpp')) SRC_TEST := $(shell find $(TEST_DIR) -name '*.cpp') # 各部分对应的目标文件 OBJ_BUSINESS := $(SRC_BUSINESS:%=$(OBJ_DIR)/%.o) OBJ_MAIN := $(SRC_MAIN:%=$(OBJ_DIR)/%.o) OBJ_TEST := $(SRC_TEST:%=$(OBJ_DIR)/%.o) DEPS := $(OBJ_BUSINESS:.o=.d) $(OBJ_MAIN:.o=.d) $(OBJ_TEST:.o=.d) # 头文件路径新增test目录,适配doctest引入 INC_DIR := $(shell find $(SRC_DIR) -type d) $(TEST_DIR) INC_FLAGS := $(addprefix -I,$(INC_DIR)) CPPFLAGS := $(INC_FLAGS) -MMD -MP .PHONY: clean all all: $(BUILD_DIR)/$(TARGET_EXEC) $(BUILD_DIR)/$(TEST_EXEC) # 主程序链接:业务代码+主程序入口 $(BUILD_DIR)/$(TARGET_EXEC): $(OBJ_BUSINESS) $(OBJ_MAIN) $(CC) $^ -o $@ $(LDFLAGS) # 测试程序链接:业务代码+测试代码,不引入src/main.o $(BUILD_DIR)/$(TEST_EXEC): $(OBJ_BUSINESS) $(OBJ_TEST) $(CC) $^ -o $@ $(LDFLAGS) $(OBJ_DIR)/%.cpp.o: %.cpp @mkdir -p $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ clean: rm -rf $(OBJ_DIR) $(TARGET_EXEC) $(TEST_EXEC) -include $(DEPS)
2. doctest测试文件修改
要让doctest自动生成测试程序的main入口,需使用DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN宏,而非DOCTEST_CONFIG_IMPLEMENT,后者需要手动编写main函数调用测试逻辑。
正确的tests.cpp示例:
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" // 引入业务头文件编写测试用例 #include "file.h" TEST_CASE("示例测试用例") { CHECK(your_function(1) == 2); // 补充其他测试逻辑 }
内容的提问来源于stack exchange,提问作者Gianluca Bianco
相关产品推荐
相关产品推荐

