CMake基础:foreach详解

目录

1.简介

2.使用场景

2.1.批量添加源文件到目标

2.2.遍历目录下的所有指定文件

2.3.批量链接第三方库

[3.循环控制(CMake 3.20+ 支持)](#3.循环控制(CMake 3.20+ 支持))

4.同时循环多个列表

5.注意事项


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 路径分隔符。

相关推荐
oem1105 小时前
C++中的适配器模式
开发语言·c++·算法
yiwenrong5 小时前
系统初始化
linux
逸Y 仙X5 小时前
文章八:ElasticSearch特殊数据字段类型解读
java·大数据·linux·运维·elasticsearch·搜索引擎
脱脱克克5 小时前
云端 OpenClaw 远程执行本地进程原理机制详解:Gateway、approvals 与 system.run 到底谁在判定、谁在执行?
linux·gateway·openclaw
行者..................5 小时前
第2课:恢复出厂、掌握 Linux 基础命令并完成首次 GCC 编译
linux·qt·driver
2401_894241925 小时前
C++与Rust交互编程
开发语言·c++·算法
格林威5 小时前
工业相机图像高速存储(C++版):RAID 0 NVMe SSD 阵列方法,附堡盟相机实战代码!
开发语言·c++·人工智能·数码相机·opencv·计算机视觉·视觉检测
承渊政道5 小时前
ToClaw是什么?一句话:装在云端的OpenClaw
windows·科技·学习·其他·macos·claw
啊我不会诶5 小时前
Codeforces Round 1083 (Div. 2)vp补题
c++·学习·算法
源远流长jerry5 小时前
RDMA Memory Region (MR) 机制详解:地址转换与内存保护
linux·服务器·网络·tcp/ip·架构·mr