当前位置: 首页 > news >正文

CMake cmake_parse_arguments

这段CMake代码定义了一个自定义函数 nuttx_add_subdirectory,用于自动添加当前目录下的所有子目录,但可以排除指定的目录。

代码功能解析

1. 函数定义和参数解析

function(nuttx_add_subdirectory)set(options)set(oneValueArgs)set(multiValueArgs EXCLUDE)cmake_parse_arguments(NUTTX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  • 参数解析设置

    • options: 空,表示没有开关选项
    • oneValueArgs: 空,表示没有单值参数
    • multiValueArgs EXCLUDE: 定义了一个多值参数 EXCLUDE,用于指定要排除的目录列表
  • cmake_parse_arguments:

    • 解析传递给函数的参数
    • 解析后的变量会加上 NUTTX_ 前缀
    • 例如:如果调用时指定 EXCLUDE dir1 dir2,则会产生 NUTTX_EXCLUDE 变量包含 dir1;dir2

2. 查找子目录

file(GLOB subdirLIST_DIRECTORIES falseRELATIVE ${CMAKE_CURRENT_LIST_DIR}${CMAKE_CURRENT_LIST_DIR}/*/CMakeLists.txt)
  • 使用 file(GLOB) 查找当前目录下所有包含 CMakeLists.txt 的子目录
  • LIST_DIRECTORIES false: 只返回文件,不返回目录
  • RELATIVE: 返回相对路径
  • 结果示例:["subdir1/CMakeLists.txt", "subdir2/CMakeLists.txt"]

3. 处理每个子目录

foreach(dir ${subdir})get_filename_component(dir ${dir} DIRECTORY)list(FIND NUTTX_EXCLUDE ${dir} _skip_index)if(_skip_index GREATER -1)message(STATUS "nuttx_add_subdirectory: Skipping ${dir}")continue()endif()add_subdirectory(${dir})
endforeach()
  • 遍历找到的每个 CMakeLists.txt 文件
  • get_filename_component(dir ${dir} DIRECTORY): 提取目录名(去掉文件名)
  • 检查目录是否在排除列表中,如果是则跳过
  • 否则使用 add_subdirectory() 添加该目录

使用示例

# 添加所有子目录
nuttx_add_subdirectory()# 排除特定目录
nuttx_add_subdirectory(EXCLUDE tests examples)# 排除多个目录
nuttx_add_subdirectory(EXCLUDE dir1 dir2 dir3)

参数解析用法总结

cmake_parse_arguments 的基本语法:

cmake_parse_arguments(<prefix> <options> <oneValueArgs> <multiValueArgs> <args>)
  • <prefix>: 解析后变量的前缀
  • <options>: 布尔选项列表(无值参数)
  • <oneValueArgs>: 单值参数列表
  • <multiValueArgs>: 多值参数列表
  • <args>: 实际传入的参数(通常用 ${ARGN}

这个函数提供了一种结构化的方式来处理CMake函数和宏的参数,比手动解析 ${ARGN} 更加清晰和健壮。


我来详细讲解 cmake_parse_arguments 的用法,并举几个具体例子。

基本语法

cmake_parse_arguments(<prefix> <options> <oneValueArgs> <multiValueArgs> <args>)

参数类型详解

1. 选项参数 (options)

  • 布尔值,出现即为 TRUE,不出现为 FALSE
  • 不需要额外值

2. 单值参数 (oneValueArgs)

  • 每个参数后面必须跟一个值
  • 如果重复出现,后面的会覆盖前面的

3. 多值参数 (multiValueArgs)

  • 每个参数后面可以跟多个值
  • 会收集所有值到一个列表中

详细示例

示例1:基本用法

function(my_function)set(options VERBOSE FORCE)set(oneValueArgs NAME OUTPUT)set(multiValueArgs SOURCES INCLUDES)cmake_parse_arguments(MY "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})message("VERBOSE: ${MY_VERBOSE}")message("FORCE: ${MY_FORCE}")message("NAME: ${MY_NAME}")message("OUTPUT: ${MY_OUTPUT}")message("SOURCES: ${MY_SOURCES}")message("INCLUDES: ${MY_INCLUDES}")message("未解析的参数: ${MY_UNPARSED_ARGUMENTS}")
endfunction()# 调用示例
my_function(VERBOSENAME "my_project"SOURCES main.cpp util.cppINCLUDES /usr/include /opt/local/includeFORCEOUTPUT "build"EXTRA_ARG  # 这个会被识别为未解析参数
)

输出结果:

VERBOSE: TRUE
FORCE: TRUE
NAME: my_project
OUTPUT: build
SOURCES: main.cpp;util.cpp
INCLUDES: /usr/include;/opt/local/include
未解析的参数: EXTRA_ARG

示例2:复杂的使用场景

function(configure_target)set(options SHARED STATIC EXCLUDE_FROM_ALL)set(oneValueArgs TARGET VERSION)set(multiValueArgs SOURCES LIBRARIES DEFINITIONS)cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})# 检查必需参数if(NOT ARG_TARGET)message(FATAL_ERROR "TARGET 参数是必需的")endif()message("配置目标: ${ARG_TARGET}")message("类型: ${ARG_SHARED} (SHARED), ${ARG_STATIC} (STATIC)")message("版本: ${ARG_VERSION}")message("源文件: ${ARG_SOURCES}")message("库: ${ARG_LIBRARIES}")message("定义: ${ARG_DEFINITIONS}")message("排除从全部: ${ARG_EXCLUDE_FROM_ALL}")
endfunction()# 各种调用方式
configure_target(TARGET "my_lib"STATICVERSION "1.0.0"SOURCES src1.cpp src2.cpp src3.cppLIBRARIES pthread dlDEFINITIONS USE_FEATURE_A USE_FEATURE_B
)configure_target(TARGET "my_shared_lib"SHAREDEXCLUDE_FROM_ALLSOURCES shared.cpp
)

示例3:处理默认值和验证

function(create_executable)set(options DEBUG RELEASE)set(oneValueArgs NAME COMPILER)set(multiValueArgs FILES LINK_LIBS)cmake_parse_arguments(EXE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})# 设置默认值if(NOT EXE_COMPILER)set(EXE_COMPILER "gcc")endif()# 互斥选项检查if(EXE_DEBUG AND EXE_RELEASE)message(FATAL_ERROR "DEBUG 和 RELEASE 选项不能同时使用")endif()# 必需参数检查if(NOT EXE_NAME OR NOT EXE_FILES)message(FATAL_ERROR "NAME 和 FILES 参数是必需的")endif()# 构建类型if(EXE_DEBUG)set(BUILD_TYPE "Debug")elseif(EXE_RELEASE)set(BUILD_TYPE "Release")else()set(BUILD_TYPE "Default")endif()message("创建可执行文件: ${EXE_NAME}")message("编译器: ${EXE_COMPILER}")message("构建类型: ${BUILD_TYPE}")message("文件: ${EXE_FILES}")message("链接库: ${EXE_LINK_LIBS}")
endfunction()# 调用
create_executable(NAME "my_app"FILES main.c helper.cLINK_LIBS m pthreadDEBUG
)create_executable(NAME "another_app"FILES app.cppCOMPILER "clang++"RELEASE
)

示例4:与自定义宏结合使用

macro(my_advanced_function)set(options OPTION_A OPTION_B OPTION_C)set(oneValueArgs SINGLE_VALUE)set(multiValueArgs MULTI_VALUES)cmake_parse_arguments(ADV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})# 在宏中,变量会影响到调用者的作用域set(PARSED_OPTION_A ${ADV_OPTION_A})set(PARSED_SINGLE_VALUE ${ADV_SINGLE_VALUE})set(PARSED_MULTI_VALUES ${ADV_MULTI_VALUES})
endmacro()# 使用
my_advanced_function(OPTION_AOPTION_CSINGLE_VALUE "hello"MULTI_VALUES item1 item2 item3
)message("OPTION_A: ${PARSED_OPTION_A}")  # 输出: OPTION_A: TRUE
message("SINGLE_VALUE: ${PARSED_SINGLE_VALUE}")  # 输出: SINGLE_VALUE: hello
message("MULTI_VALUES: ${PARSED_MULTI_VALUES}")  # 输出: MULTI_VALUES: item1;item2;item3

重要注意事项

  1. 变量前缀:所有解析后的变量都会加上前缀,如 MY_VERBOSEMY_SOURCES

  2. 未解析参数:不在定义范围内的参数会保存在 <prefix>_UNPARSED_ARGUMENTS

  3. 缺失值的参数:期望有值但没有提供的参数会保存在 <prefix>_KEYWORDS_MISSING_VALUES

  4. 作用域:在函数中使用时,变量只在函数内有效;在宏中使用时,会影响调用者的作用域

  5. 参数顺序:调用时参数的顺序可以是任意的

这种参数解析方式让CMake函数和宏的使用更加清晰和灵活,类似于其他编程语言中的关键字参数。


三个列表的固定规则

是的,这三个列表的标识是固定死的,你必须使用这三个特定的变量名:

1. options - 开关选项列表

用途:存放不需要值的布尔参数(标志)
规则:参数出现即为 TRUE,不出现为 FALSE

set(options VERBOSE FORCE DEBUG)  # 固定使用变量名 options

2. oneValueArgs - 单值参数列表

用途:存放每个后面必须跟一个值的参数
规则:参数后面必须跟且只能跟一个值

set(oneValueArgs NAME OUTPUT TYPE)  # 固定使用变量名 oneValueArgs

3. multiValueArgs - 多值参数列表

用途:存放每个后面可以跟多个值的参数
规则:参数后面可以跟任意数量的值,直到遇到下一个已知参数

set(multiValueArgs SOURCES LIBRARIES INCLUDES)  # 固定使用变量名 multiValueArgs

完整语法结构

function(my_function)# 1. 必须使用这三个固定变量名定义参数类型set(options <开关参数列表>)set(oneValueArgs <单值参数列表>) set(multiValueArgs <多值参数列表>)# 2. 解析参数(参数顺序也是固定的)cmake_parse_arguments(PREFIX      # 自定义前缀"${options}"       # 必须引用 options 变量"${oneValueArgs}"  # 必须引用 oneValueArgs 变量  "${multiValueArgs}" # 必须引用 multiValueArgs 变量${ARGN}     # 必须传入实际参数)
endfunction()

实际示例

function(build_target)# 固定规则:必须用这三个变量名set(options VERBOSE STATIC SHARED)set(oneValueArgs TARGET OUTPUT) set(multiValueArgs SOURCES INCLUDES LIBS)# 固定规则:参数顺序必须是这样cmake_parse_arguments(ARG"${options}""${oneValueArgs}" "${multiValueArgs}"${ARGN})# 使用解析结果if(ARG_VERBOSE)message("详细模式开启")endif()message("目标: ${ARG_TARGET}")message("输出: ${ARG_OUTPUT}")message("源文件: ${ARG_SOURCES}")message("包含路径: ${ARG_INCLUDES}")message("库文件: ${ARG_LIBS}")
endfunction()# 调用示例
build_target(VERBOSETARGET "my_app"SOURCES main.cpp util.cpp helper.cppINCLUDES /usr/include /opt/local/includeLIBS pthread m dlOUTPUT "build/bin"
)

为什么是固定的?

这是 CMake 语言的语法规定,就像其他编程语言的关键字一样:

  • if()...endif() 是固定的
  • foreach()...endforeach() 是固定的
  • set(options ...) 中的 options 也是固定的

你不能随意改成其他名字,比如:

# 错误!CMake 无法识别
set(flags VERBOSE)                    # 应该用 options
set(singleArgs NAME)                  # 应该用 oneValueArgs  
set(multiArgs SOURCES)                # 应该用 multiValueArgs
cmake_parse_arguments(ARG "${flags}" "${singleArgs}" "${multiArgs}" ${ARGN})

总结规则

  1. 变量名固定:必须使用 optionsoneValueArgsmultiValueArgs
  2. 参数顺序固定cmake_parse_arguments(PREFIX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  3. 内容自定义:列表中的具体参数名可以按需定义
  4. 前缀自定义PREFIX 部分可以自定义

这样设计是为了保持 CMake 语法的统一性和可预测性。


这个用法比较特殊,让我来详细解析:

cmake_parse_arguments(SOURCE "" "" "CFLAGS;DEPS" ${ARGN})

参数解析

根据 cmake_parse_arguments 的语法:

cmake_parse_arguments(<前缀> <options列表> <单值参数列表> <多值参数列表> ${ARGN})

在这个例子中:

  • SOURCE - 前缀
  • "" - 空的 options 列表(没有开关参数)
  • "" - 空的 oneValueArgs 列表(没有单值参数)
  • "CFLAGS;DEPS" - 多值参数列表(只有 CFLAGSDEPS 两个多值参数)

这意味着

  1. 没有开关参数:不能使用像 VERBOSEFORCE 这样的标志
  2. 没有单值参数:不能使用像 NAME "value" 这样后面跟一个值的参数
  3. 只有两个多值参数
    • CFLAGS - 可以接受多个编译标志
    • DEPS - 可以接受多个依赖项

使用示例

function(my_function)cmake_parse_arguments(SOURCE "" "" "CFLAGS;DEPS" ${ARGN})message("CFLAGS: ${SOURCE_CFLAGS}")message("DEPS: ${SOURCE_DEPS}")
endfunction()# 调用方式
my_function(CFLAGS -O2 -Wall -WextraDEPS lib1 lib2 lib3
)my_function(DEPS dependency1CFLAGS -g -DDEBUG
)

输出结果:

CFLAGS: -O2;-Wall;-Wextra
DEPS: lib1;lib2;lib3

为什么这样设计?

这种用法适用于只需要多值参数的场景,比如:

# 编译配置函数
function(setup_compilation)cmake_parse_arguments(COMP "" "" "FLAGS;DEFINES;INCLUDES" ${ARGN})if(COMP_FLAGS)add_compile_options(${COMP_FLAGS})endif()if(COMP_DEFINES)add_compile_definitions(${COMP_DEFINES})endif()if(COMP_INCLUDES)include_directories(${COMP_INCLUDES})endif()
endfunction()# 使用
setup_compilation(FLAGS -O2 -Wall -std=c++11DEFINES USE_FEATURE_A DEBUG_MODEINCLUDES /usr/local/include /opt/include
)

总结

这种用法是 cmake_parse_arguments 的一个简化形式,当你只需要多值参数而不需要开关选项和单值参数时,可以把前两个参数设为空字符串。

语法规则:

  • 第一个参数:前缀(必需)
  • 第二个参数:options 列表(可以为空)
  • 第三个参数:单值参数列表(可以为空)
  • 第四个参数:多值参数列表(至少有一个)
  • 第五个参数:${ARGN}(必需)
http://www.dtcms.com/a/422535.html

相关文章:

  • 4、存储系统架构 - 从机械到闪存的速度革命
  • 淘宝店铺全量商品接口深度开发:从分页优化到数据完整性保障
  • 视频MixformerV2 onnx导出
  • winfrom 的 BindingSource ,ist<T> + LINQ,DataTable + DataView 自动刷新机制 优势劣势
  • Spring Statemachine 架构详解
  • 做网站大概费用给漫画网站做推广
  • Hadoop RPC深度解析:分布式通信的核心机制
  • 提升开发效率的RPC系统!
  • 微信小程序入门学习教程,从入门到精通,微信小程序页面交互 —— 知识点详解与案例实现(3)
  • 高端品牌网站建设电商网站设计常州的平台公司
  • 物联网存储选型避坑指南:SQLite/MySQL/InfluxDB深度对比(C#场景+性能测试+选型工具)
  • Sublime Text 4 下载 + 安装 + 汉化全流程教程(图文保姆级指南)
  • Print Conductor打印软件安装教程!一款非常好用的批量打印软件!支持PDF、Word、Excel、图片等
  • 华为HCIP认证条件及考试技巧
  • 【数值分析】08-非线性方程的求根方法-简单迭代法求根(1)
  • Django 视图与路由基础:从URL映射到视图函数
  • 华为 HCIA-Datacom 备考:VRP 通用路由平台原理-实操
  • 网站开发需要哪些知识展台
  • 高端网站建设服务器网站首页做一点开有动画
  • 借助串口以太网模块与三菱以太网通信处理器,实现三菱 FX3U PLC 和触摸屏通讯的案例
  • 现代控制理论4——第3章线性控制系统的能控性和能观性(1)
  • 【论文精读】Group Collaborative Learning for Co-Salient Object Detection
  • Apache NuttX 入门指南
  • MySQL进阶知识点(六)---- 存储引擎
  • 2025.8.10-学习C++(一)
  • QAxios研发笔记(一):在Qt环境下,构建Promise风格的Get请求接口
  • 【OpenGL】复杂光照理论与实践
  • Binder和IBinder
  • 标准化考场建设方案解析:全频阻断作弊防控系统介绍
  • 网站开发 团队协作h5响应式 wordpress