cmake_minimum_required(VERSION 3.20) get_filename_component(MY_PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) project(${MY_PROJECT_NAME} C ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) # ============================================================================== # 🎯 宏:add_project_modules_with_tests # ============================================================================== macro(add_project_modules_with_tests OUT_APP_SOURCES OUT_TEST_FILES_LIST) foreach(MODULE_DIR ${ARGN}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}") message(STATUS "[Module Found]: ${MODULE_DIR}") # 自动将该目录加入全局头文件包含路径 include_directories("${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}") # 1. 搜集该目录下所有的 C/C++ 源文件 file(GLOB ALL_MODULE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.cc" "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.s" "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.S" ) # 2. 分离出测试文件 (*.t.cpp 或 *.t.c) foreach(FILE_PATH ${ALL_MODULE_FILES}) # 匹配以 .t.cpp, .t.cc, .t.c 结尾的文件 if(FILE_PATH MATCHES "\\.t\\.(cpp|cc|c)$") list(APPEND ${OUT_TEST_FILES_LIST} ${FILE_PATH}) message("\tTestUnit: ${FILE_PATH}") else() # 剩下的才是主程序的源代码 list(APPEND ${OUT_APP_SOURCES} ${FILE_PATH}) message("\tSource: ${FILE_PATH}") endif() endforeach() else() message(WARNING "[Module Warning]: Directory '${MODULE_DIR}' does not exist!") endif() endforeach() endmacro() # ============================================================================== # 🚀 使用宏收集目标目录 # ============================================================================== # 初始化一个空变量用于存放所有源码 set(SOURCES "") set(TEST_SOURCES "") configure_file(c_Config.h.in c_Config.h @ONLY) # 调用宏:第一个参数是接收源码的变量,后面跟上您所有的子目录 add_project_modules_with_tests(SOURCES TEST_SOURCES Base Foundation Graph Math Memory Search Sort String ) #foreach (item ${SOURCES}) # message(STATUS "[${MY_PROJECT_NAME}] SOURCE: ${item}") #endforeach () add_library(${MY_PROJECT_NAME} ${SOURCES}) target_include_directories(${MY_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) # ============================================================================== # 🧪 自动生成并注册单元测试 # ============================================================================== foreach(TEST_FILE ${TEST_SOURCES}) # 获取文件名(不含路径),例如 "C:/project/Math/matrix.t.cpp" -> "matrix.t.cpp" get_filename_component(TEST_NAME_WE ${TEST_FILE} NAME_WE) # 构造测试可执行文件的名称,例如 "test_matrix" set(TEST_EXE_NAME "test_${TEST_NAME_WE}") # 编译此测试程序:测试程序 = 测试源文件 + 主程序除main外的所有依赖 # 注意:如果您的主程序包含 main.cpp,建议将 main.cpp 从 ${APP_SOURCES} 中剥离, # 或者在这里只链接需要的源文件,防止出现多个 main 函数冲突。 # add_executable(${TEST_EXE_NAME} ${TEST_FILE} ${APP_SOURCES}) add_executable(${TEST_EXE_NAME} ${TEST_FILE}) target_link_libraries(${TEST_EXE_NAME} PRIVATE ${MY_PROJECT_NAME}) # 将其注册到 CMake 的 ctest 测试集中 add_test(NAME ${TEST_EXE_NAME} COMMAND ${TEST_EXE_NAME}) message(STATUS "[Test Registered]: ${TEST_EXE_NAME}") endforeach()