目录
[3.循环控制(CMake 3.20+ 支持)](#3.循环控制(CMake 3.20+ 支持))
1.简介
CMake 的 foreach 是遍历列表 / 集合的核心指令,支持传统遍历 和现代关键字遍历两种语法,常用于批量处理源文件、库依赖、编译选项等场景。
语法:
cpp
foreach(<循环变量> IN [LISTS <列表名>] [ITEMS <元素>] [RANGE <起始> <结束>])
# 循环体
endforeach()
LISTS:遍历已定义的 CMake 列表变量ITEMS:遍历直接给出的元素RANGE:遍历数值范围(如RANGE 1 5对应 1-5)
2.使用场景
2.1.批量添加源文件到目标
适用于项目源文件较多的情况,避免重复写 add_executable 的参数。
cpp
cmake_minimum_required(VERSION 3.15)
project(ForeachDemo LANGUAGES CXX)
# 定义源文件列表
set(SOURCE_FILES
main.cpp
utils.cpp
network.cpp
)
# 遍历源文件列表,打印并收集(这里直接用列表也能加,遍历主要用于额外操作)
foreach(src IN LISTS SOURCE_FILES)
message(STATUS "添加源文件: ${src}")
endforeach()
# 添加可执行文件
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
# Windows + VS2019 环境配置
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
endif()
2.2.遍历目录下的所有指定文件
结合 file(GLOB) 收集目录中的 .cpp 文件,适合动态管理源文件。
cpp
cmake_minimum_required(VERSION 3.15)
project(ForeachFileDemo LANGUAGES CXX)
# 收集 src 目录下的所有 .cpp 文件(GLOB 会自动生成列表)
file(GLOB_RECURSE SRC_FILES ${CMAKE_SOURCE_DIR}/src/*.cpp)
# 遍历文件列表,过滤掉不需要的文件(比如 test_xxx.cpp)
foreach(file IN LISTS SRC_FILES)
# 检查文件名是否包含 test_
if(file MATCHES "test_.*\\.cpp$")
message(STATUS "过滤测试文件: ${file}")
list(REMOVE_ITEM SRC_FILES ${file}) # 从列表中移除
endif()
endforeach()
add_executable(${PROJECT_NAME} ${SRC_FILES})
注意:
file(GLOB)不推荐用于频繁增减文件的场景,若文件变动需手动执行cmake --build . --clean-first重新生成。
2.3.批量链接第三方库
适用于项目依赖多个库的情况,简化 target_link_libraries 的写法。
cpp
cmake_minimum_required(VERSION 3.15)
project(ForeachLibDemo LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp)
# 定义依赖库列表(假设已通过 find_package 找到这些库)
set(DEPEND_LIBS
Qt5::Core
Qt5::Sql
Boost::asio
)
# 遍历库列表,批量链接
foreach(lib IN LISTS DEPEND_LIBS)
target_link_libraries(${PROJECT_NAME} PRIVATE ${lib})
message(STATUS "链接库: ${lib}")
endforeach()
3.循环控制(CMake 3.20+ 支持)
CMake 3.20 及以上版本支持 continue()(跳过当前循环)和 break()(终止循环),示例如下:
cpp
foreach(num RANGE 1 10)
if(num LESS 3)
continue() # 跳过 1、2
endif()
if(num GREATER 7)
break() # 终止于 8
endif()
message(STATUS "当前数值: ${num}")
endforeach()
4.同时循环多个列表
CMake 的 foreach 本身不支持直接并行遍历多个列表,需借助索引循环 + 列表取值 实现,核心思路是通过列表长度确定循环次数,再用 list(GET) 获取对应索引的元素。
示例:同时遍历源文件列表 和对应的编译选项列表,为不同源文件设置专属编译参数
cpp
cmake_minimum_required(VERSION 3.15)
project(MultiListForeach LANGUAGES CXX)
# 定义两个长度相同的列表
set(SOURCE_FILES main.cpp utils.cpp network.cpp)
set(COMPILE_FLAGS "-O2" "-g -Wall" "-O3 -march=native")
# 获取列表长度(需确保所有列表长度一致)
list(LENGTH SOURCE_FILES FILE_COUNT)
if(NOT FILE_COUNT EQUAL ${CMAKE_ARGV2})
message(FATAL_ERROR "列表长度不一致,无法并行遍历!")
endif()
# 索引循环,遍历多个列表
math(EXPR MAX_INDEX "${FILE_COUNT} - 1")
foreach(IDX RANGE 0 ${MAX_INDEX})
# 获取对应索引的元素
list(GET SOURCE_FILES ${IDX} CURRENT_FILE)
list(GET COMPILE_FLAGS ${IDX} CURRENT_FLAG)
# 打印信息 + 应用编译选项
message(STATUS "为 ${CURRENT_FILE} 设置编译选项: ${CURRENT_FLAG}")
set_source_files_properties(${CURRENT_FILE} PROPERTIES COMPILE_FLAGS ${CURRENT_FLAG})
endforeach()
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
# Windows VS2019 环境适配
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
endif()
5.注意事项
1.循环变量的作用域:foreach 内定义的变量默认是全局的,若需局部作用域,可在循环内用 set(<var> <value> PARENT_SCOPE) 控制。
2.跨平台路径处理:遍历文件路径时,建议用 CMAKE_SOURCE_DIR/CMAKE_CURRENT_SOURCE_DIR 等变量,避免硬编码 Windows 路径分隔符。