cmake项目使用递归CmakeLists.txt的方式添加源文件

发布时间:2026/7/10 5:43:31

cmake项目使用递归CmakeLists.txt的方式添加源文件 一般情况下要添加多个源文件用于编译需要手动添加例如add_executable(${PROJECT_NAME}src/main.cpp src/utils/message/message.cpp src/utils/sort/sort_algorithms.cpp)每次添加源文件都得修改根目录的CMakeLists.txt这样很麻烦且不利于多人协作为了解决这个问题可以在每一个源文件目录下添加一个CMakeLists.txt文件来解决1.先准备好源文件1.src/main.cpp#includeiostream#includevector#includealgorithm#includeutils/message/message.h#includeutils/sort/sort_algorithms.hstaticvoidtest_message(){std::string messagebuild_message();std::coutmessagestd::endl;}// 统一打印整型数组供排序和链表测试复用。staticvoidprint_values(conststd::vectorintvalues){std::cout[;for(std::size_t i0;ivalues.size();i){if(i0){std::cout, ;}std::coutvalues[i];}std::cout];}// 执行一个排序函数并用 std::is_sorted 判断排序结果是否正确。staticvoidrun_sort_test(conststd::stringname,std::vectorint(*sort_func)(std::vectorint),conststd::vectorintinput){conststd::vectorintresultsort_func(input);std::coutname: ;print_values(result);std::cout(std::is_sorted(result.begin(),result.end())? PASS: FAIL)std::endl;}staticvoidtest_sort(){// 所有排序算法使用同一份输入便于对比输出结果。conststd::vectorintvalues{9,4,6,2,8,1,5,3,7,0};run_sort_test(bubble_sort,sort_algorithms::bubble_sort,values);run_sort_test(selection_sort,sort_algorithms::selection_sort,values);run_sort_test(insertion_sort,sort_algorithms::insertion_sort,values);run_sort_test(merge_sort,sort_algorithms::merge_sort,values);run_sort_test(quick_sort,sort_algorithms::quick_sort,values);}intmain(){std::coutHello, World!std::endl;test_message();test_sort();return0;}2.src/utils/sort_algorithms.h#ifndefSORT_ALGORITHMS_H#defineSORT_ALGORITHMS_H#includevectornamespace sort_algorithms{// 冒泡排序重复交换相邻逆序元素返回排序后的副本。std::vectorintbubble_sort(std::vectorintvalues);// 选择排序每轮选择未排序区间的最小值返回排序后的副本。std::vectorintselection_sort(std::vectorintvalues);// 插入排序把当前元素插入到前方已排序区间返回排序后的副本。std::vectorintinsertion_sort(std::vectorintvalues);// 归并排序递归拆分并合并有序区间返回排序后的副本。std::vectorintmerge_sort(std::vectorintvalues);// 快速排序基于枢轴分区后递归排序返回排序后的副本。std::vectorintquick_sort(std::vectorintvalues);}// namespace sort_algorithms#endif3.src/utils/sort_algorithms.cpp#includesort_algorithms.h#includealgorithm#includecstddefnamespacesort_algorithms{namespace{// 对 [left, right) 区间做归并排序buffer 用作合并时的临时空间。voidmerge_sort_range(std::vectorintvalues,std::vectorintbuffer,std::size_t left,std::size_t right){if(right-left1){return;}conststd::size_t midleft(right-left)/2;merge_sort_range(values,buffer,left,mid);merge_sort_range(values,buffer,mid,right);// 两个子区间都已经有序把较小元素依次写入 buffer。std::size_t ileft;std::size_t jmid;std::size_t kleft;while(imidjright){buffer[k]values[i]values[j]?values[i]:values[j];}while(imid){buffer[k]values[i];}while(jright){buffer[k]values[j];}// 把合并后的有序结果复制回原数组对应区间。for(std::size_t indexleft;indexright;index){values[index]buffer[index];}}// 对闭区间 [left, right] 做快速排序分区和递归处理。voidquick_sort_range(std::vectorintvalues,intleft,intright){if(leftright){return;}constintpivotvalues[left(right-left)/2];intileft;intjright;// 从两端向中间扫描把小于 pivot 的元素放左侧大于 pivot 的元素放右侧。while(ij){while(values[i]pivot){i;}while(values[j]pivot){--j;}if(ij){std::swap(values[i],values[j]);i;--j;}}if(leftj){quick_sort_range(values,left,j);}if(iright){quick_sort_range(values,i,right);}}}// namespacestd::vectorintbubble_sort(std::vectorintvalues){for(std::size_t endvalues.size();end1;--end){boolswappedfalse;// 每轮把当前未排序区间的最大值“冒泡”到末尾。for(std::size_t i1;iend;i){if(values[i-1]values[i]){std::swap(values[i-1],values[i]);swappedtrue;}}// 如果本轮没有交换说明数组已经有序可以提前结束。if(!swapped){break;}}returnvalues;}std::vectorintselection_sort(std::vectorintvalues){for(std::size_t i0;ivalues.size();i){std::size_t min_indexi;// 在未排序区间中寻找最小值的位置。for(std::size_t ji1;jvalues.size();j){if(values[j]values[min_index]){min_indexj;}}std::swap(values[i],values[min_index]);}returnvalues;}std::vectorintinsertion_sort(std::vectorintvalues){for(std::size_t i1;ivalues.size();i){constintcurrentvalues[i];std::size_t ji;// 将比 current 大的元素右移给 current 腾出插入位置。while(j0values[j-1]current){values[j]values[j-1];--j;}values[j]current;}returnvalues;}std::vectorintmerge_sort(std::vectorintvalues){// buffer 只分配一次递归过程中复用避免每层递归重复分配。std::vectorintbuffer(values.size());merge_sort_range(values,buffer,0,values.size());returnvalues;}std::vectorintquick_sort(std::vectorintvalues){// 空数组不进入递归避免 size_t 到 int 转换时出现下溢。if(!values.empty()){quick_sort_range(values,0,static_castint(values.size()-1));}returnvalues;}}// namespace sort_algorithms4.src\utils\message\message.h#ifndefMESSAGE_H#defineMESSAGE_H#includestring// 构建示例程序启动时显示的提示信息。std::stringbuild_message();#endif5.src\utils\message\message.cpp#includemessage.hstd::stringbuild_message(){// 保持消息独立成函数方便演示静态库被主程序链接和调用。returnHello from a CMake subdirectory project built with MinGW.;}2.修改CMakeLists.txt1.根目录的CMakeLists.txtcmake_minimum_required(VERSION3.16)project(cmake-demo VERSION1.0.0LANGUAGES CXX)#C标准设置set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_CXX_EXTENSIONS OFF)# 可执行文件add_executable(${PROJECT_NAME})add_subdirectory(src)2.src/CMakeLists.txtfile(GLOB CURRENT_SOURCES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*.cpp${CMAKE_CURRENT_SOURCE_DIR}/*.cc${CMAKE_CURRENT_SOURCE_DIR}/*.cxx${CMAKE_CURRENT_SOURCE_DIR}/*.h${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)target_sources(${PROJECT_NAME}PRIVATE ${CURRENT_SOURCES})target_include_directories(${PROJECT_NAME}PRIVATE${CMAKE_CURRENT_SOURCE_DIR})add_subdirectory(utils)3.src\utils\CMakeLists.txtfile(GLOB CURRENT_SOURCES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*.cpp${CMAKE_CURRENT_SOURCE_DIR}/*.cc${CMAKE_CURRENT_SOURCE_DIR}/*.cxx${CMAKE_CURRENT_SOURCE_DIR}/*.h${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)target_sources(${PROJECT_NAME}PRIVATE ${CURRENT_SOURCES})target_include_directories(${PROJECT_NAME}PRIVATE${CMAKE_CURRENT_SOURCE_DIR})add_subdirectory(sort)add_subdirectory(message)4.src\utils\message\CMakeLists.txtfile(GLOB CURRENT_SOURCES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*.cpp${CMAKE_CURRENT_SOURCE_DIR}/*.cc${CMAKE_CURRENT_SOURCE_DIR}/*.cxx${CMAKE_CURRENT_SOURCE_DIR}/*.h${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)target_sources(${PROJECT_NAME}PRIVATE ${CURRENT_SOURCES})target_include_directories(${PROJECT_NAME}PRIVATE${CMAKE_CURRENT_SOURCE_DIR})5.src\utils\sort\CMakeLists.txtfile(GLOB CURRENT_SOURCES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*.cpp${CMAKE_CURRENT_SOURCE_DIR}/*.cc${CMAKE_CURRENT_SOURCE_DIR}/*.cxx${CMAKE_CURRENT_SOURCE_DIR}/*.h${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)target_sources(${PROJECT_NAME}PRIVATE ${CURRENT_SOURCES})target_include_directories(${PROJECT_NAME}PRIVATE${CMAKE_CURRENT_SOURCE_DIR})3.CMakeLists.txt逐行解释1.file(GLOB ...)— 收集文件列表file(GLOB CURRENT_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/*.cxx ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp )file(GLOB ...)用通配符匹配当前目录下所有指定后缀的文件CURRENT_SOURCES将匹配到的文件列表存入这个变量CONFIGURE_DEPENDS告诉 CMake 当添加/删除文件时自动重新扫描不写的话新增文件后需要手动重新运行 cmake 才会生效匹配的扩展名.cpp、.cc、.cxx、.h、.hpp2.target_sources(...)— 把文件添加到目标target_sources(${PROJECT_NAME} PRIVATE ${CURRENT_SOURCES})把上面收集到的所有源文件添加到名为${PROJECT_NAME}的目标可执行文件或库PRIVATE表示这些源文件只属于这个目标自身不会透传给依赖它的其他目标3.target_include_directories(...)— 添加头文件搜索路径target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})把当前目录CMAKE_CURRENT_SOURCE_DIR添加为头文件搜索路径这样在#include message.h时编译器能在这个子目录里找到头文件整体作用这种模式的目的是让 CMake 自动发现子目录下的源文件不需要每次新增文件都手动修改 CMakeLists.txt。例如在 utils 目录下它会自动匹配src/utils/sort_algorithms.cppsrc/utils/sort_algorithms.h以及sort/和message/子目录中通过add_subdirectory添加的文件⚠️注意虽然GLOB很方便但 CMake 官方更推荐显式列出源文件使用set()或手动列出因为GLOB在文件变更时可能不会自动触发重新配置除非加了CONFIGURE_DEPENDS。不过对于学习项目这样做问题不大。4.优化源文件夹的CMakeLists.txt观察到src/CMakeLists.txt,src\utils\CMakeLists.txt,src\utils\message\CMakeLists.txt,src\utils\sort\CMakeLists.txt四个源文件夹下面的CMakeLists.txt非常相似差别只在于添加子文件夹add_subdirectory这个操作因为需要添加不同名字的子文件夹。可以将手动添加指定子文件夹的操作改为自动扫描并添加所有子文件夹# 自动扫描并添加所有子文件夹file(GLOB ALL_ENTRIES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*)foreach(ENTRY ${ALL_ENTRIES})if(IS_DIRECTORY ${ENTRY})get_filename_component(DIR_NAME ${ENTRY}NAME)add_subdirectory(${DIR_NAME})endif()endforeach()

相关新闻