ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

F´ CMake 构建目标(Targets)体系详解:从内置目标到自定义扩展

F´ CMake 构建目标(Targets)体系详解:从内置目标到自定义扩展 嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fp/fprime点击查看免费下载F´F Prime是一个面向飞行软件与嵌入式系统的开源框架其构建系统基于 CMake 构建。本文围绕官方用户指南中的 Targets 文档展开系统讲解 F´ 构建系统中构建目标Targets子系统的核心机制它如何以全局目标 模块级目标双层结构组织构建动作仓库内置了哪些标准目标以及如何通过register_fprime_target注册自定义目标。读完本文你将掌握make target与make MODULE_target两种调用方式的使用场景、内置目标的实现原理并能独立编写一个可在全局与每个模块上同时生效的自定义构建目标。一、什么是 F´ 的构建目标子系统在 F´ 的 CMake 构建体系中目标Targets指的是构建系统向用户暴露的可执行动作入口。除了 CMake 本身为每个库library、可执行文件executable生成的构建产物目标外F´ 额外提供了一套自定义动作目标的注册机制让构建系统能够承载与编译产物无关的功能性任务。官方文档Targets.md给出了一个典型动机用户可能希望创建一个统计系统内文件总数的构建目标。这种目标不属于某个可执行文件或库的编译产物而是独立的功能入口——这正是 Targets 子系统要解决的需求让用户把任意新功能注册进构建系统并通过make直接触发。Targets 子系统在 cmake/target/target.cmake 中被描述为允许针对模块与顶层目标进行构建的多部分构建multi-part builds典型的例子如sloc代码行数统计或dict字典生成——它们一部分动作作用于每个模块最终再汇入某个全局命令。官方文档将这种机制概括为Targets are applied both at the global scope and per-module scope. Thus each target can provide a set of build targets (one per registered module) and a global build target.即每个注册的 target 文件通常产生两类入口全局目标global target例如count作用于整个项目模块级目标module target例如MODULE_count作用于某个具体模块。对于生成 GNU make 文件的工程执行方式为make target与make MODULE_target。文档给出的具体示例为make Svc_CmdDispatcher_coverage——即对Svc_CmdDispatcher模块执行coverage目标。二、内置标准目标Built-In TargetsF´ 的 CMake 系统内置了若干对所有项目都有用的标准目标由 cmake/FPrime.cmake 中的fprime_setup_standard_targets宏统一注册。该宏的实现如下macro(fprime_setup_standard_targets) if (NOT DEFINED FPRIME_SUB_BUILD_TARGETS) register_fprime_target(target/build) register_fprime_build_autocoder(autocoder/fpp OFF) register_fprime_build_autocoder(autocoder/ai_xml OFF) register_fprime_build_autocoder(autocoder/packets OFF) register_fprime_target(target/version) register_fprime_target(target/install) register_fprime_ut_target(target/ut) if (FPRIME_ENABLE_UTIL_TARGETS) register_fprime_target(target/refresh_cache) register_fprime_ut_target(target/check) endif() endif() endmacro(fprime_setup_standard_targets)每个内置目标对应cmake/target/目录下的一个.cmake文件见 cmake/target 目录包括build.cmake、version.cmake、install.cmake、ut.cmake、check.cmake、refresh_cache.cmake、fpp_depend.cmake、fpp_locs.cmake、default.cmake等。下面结合源码逐一说明核心内置目标。1. build模块构建目标build目标是整个系统的基础目标由 cmake/target/build.cmake 实现。它的职责是为系统中的每个模块完成实际的构建配置注册目标库或可执行文件、设置编译源文件、标记自动代码生成autocoder产出的源文件、解析模块与链接依赖、配置头文件包含路径等。注意其文档中的特别说明This target sets up the build for every module in the system. WARNING: it registers a target set to the module name, not including_build. This is for historical reasons.也就是说build目标注册的模块级目标直接使用模块名不带_build后缀这是出于历史兼容性的设计。其全局目标build_add_global_target是一个空实现——因为常规 CMake 构建中的all目标已经覆盖了构建全部的语义function(build_add_global_target TARGET) endfunction(build_add_global_target)模块级实现build_add_module_target则真正完成核心构建配置包括调用run_ac_set运行自动编码器、通过resolve_dependencies解析依赖、将手写源码与生成源码分类后交给build_setup_build_module处理并把模块信息写入module-info.txtfunction(build_add_module_target MODULE TARGET SOURCES DEPENDENCIES) get_target_property(MODULE_TYPE ${MODULE} FP_TYPE) message(STATUS Adding ${MODULE_TYPE}: ${MODULE}) get_property(CUSTOM_AUTOCODERS GLOBAL PROPERTY FPRIME_AUTOCODER_TARGET_LIST) run_ac_set(${SOURCES} ${CUSTOM_AUTOCODERS}) resolve_dependencies(RESOLVED ${DEPENDENCIES} ${AC_DEPENDENCIES}) filter_lists(${AC_SOURCES} SOURCES AC_GENERATED) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/module-info.txt ${HEADER_FILES}\n${SOURCES_FILTERED}\n${AC_GENERATED}\n${AC_FILE_DEPENDENCIES}\n${DEPENDENCIES}\n ) build_setup_build_module(${MODULE} ${SOURCES_FILTERED} ${AC_GENERATED_FILTERED} ${RESOLVED}) ... endfunction(build_add_module_target)2. version版本信息生成目标version目标用于生成版本文件由 cmake/target/version.cmake 实现。其全局目标会生成三个文件到构建目录的versions子目录下version.hppversion.cppversion.json生成过程通过add_custom_command调用 Python 脚本 cmake/target/version/generate_version_info.py并注入PYTHONPATH、FPRIME_PROJECT_ROOT、FPRIME_FRAMEWORK_PATH、FPRIME_LIBRARY_LOCATIONS等环境变量。当设置FPRIME_CHECK_FRAMEWORK_VERSION为 ON 时脚本会额外传入--check参数用于版本校验function(version_add_global_target TARGET) set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/versions) ... if (FPRIME_CHECK_FRAMEWORK_VERSION) set(OPTIONAL_CHECK_ARG --check) endif() add_custom_command(OUTPUT ${OUTPUT_HPP} ${OUTPUT_CPP} ${OUTPUT_JSON} COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH${PYTHONPATH}:${FPRIME_FRAMEWORK_PATH}/Autocoders/Python/src FPRIME_PROJECT_ROOT${FPRIME_PROJECT_ROOT} FPRIME_FRAMEWORK_PATH${FPRIME_FRAMEWORK_PATH} FPRIME_LIBRARY_LOCATIONS${FPRIME_LIBRARY_LOCATIONS_CSV} ${FPRIME_VERSION_INFO_SCRIPT} ${OUTPUT_DIR} ${OPTIONAL_CHECK_ARG} ... ) add_library(${TARGET} ${OUTPUT_CPP}) endfunction()值得注意version目标最终通过add_library(${TARGET} ${OUTPUT_CPP})生成了一个真正的 CMake 库目标而不是单纯的add_custom_target——这展示了 target 文件内部既可以使用add_custom_target也可以使用标准 CMake 目标命令来组织构建。该目标的模块级实现version_add_module_target为空函数因为版本信息属于全局性产物。3. install安装目标install目标将 F´ 构建产物安装到 build-artifacts 目录由 cmake/target/install.cmake 实现依赖 CMake 3.13 的安装命令。文件开头强制设置set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE CACHE BOOL Install all dependency FORCE)它通过_install_real_helper过滤出真正的可安装目标可执行文件、库等编译产物即is_target_real判断为真的目标再执行安装。由于字典dictionaries是每个部署deployment级别的产物全局安装目标为空实现# Dictionaries are per-deployment, a global variant does not make sense function(install_add_global_target) endfunction()4. ut 与 check单元测试相关目标ut与check是仅当构建单元测试BUILD_TESTINGON时才生效的目标二者通过register_fprime_ut_target注册见 cmake/API.cmake 中的说明这些目标只在系统构建单元测试时创建。ut 目标cmake/target/ut.cmake全局目标在FPRIME_ENABLE_UTIL_TARGETS开启时创建名为ut_exe的目标UT_TARGET变量注释标明 For historical reasons。该文件还负责在运行测试前清理*.gcda覆盖率文件通过_ut_setup_clean_file生成clean.cmake脚本并注册到TEST_INCLUDE_FILES。模块级与部署级目标会遍历依赖的FPRIME_UTS属性把所有单元测试可执行文件挂到对应目标上。check 目标cmake/target/check.cmake提供一个运行单元测试的入口。全局实现直接调用 CTestfunction(check_add_global_target TARGET_NAME) add_custom_target(${TARGET_NAME} COMMAND ${CMAKE_CTEST_COMMAND}) endfunction(check_add_global_target)模块级实现为每个模块生成MODULE_check目标执行ctest --verbose并依赖对应 UT 可执行文件部署级实现则把部署内所有 UT 名称拼接为正则通过ctest -R 正则只运行该部署内的测试。若部署没有任何 UT则打印 No unit tests defined for ${MODULE} 提示。5. refresh_cache 与其他辅助目标refresh_cachecmake/target/refresh_cache.cmake是一个轻量目标仅在需要刷新 CMake 缓存时快速执行模块级与部署级实现均为空。此外仓库还包含fpp_depend.cmake与fpp_locs.cmake两个目标文件用于 FPP 模型的依赖信息与位置信息的子构建sub-build由fprime_initialize_build_system中的run_sub_build(info-cache target/fpp_locs target/fpp_depend)触发。全部内置目标的注册与说明可参见 docs/UsersGuide/cmake/cmake-api.md 中Target Documentation一节其提到dict 目标可能先构建本地字典片段再汇总为全局字典——这正是 Targets 子系统模块级产出 全局汇总设计思想的典型应用。三、Target 文件的结构约定三个必需函数无论内置目标还是自定义目标每个 target 文件都必须遵循统一的接口约定。根据 cmake/target/target.cmake 顶部的规范说明target 文件必须定义三个函数函数作用域必需性典型行为${TARGET_NAME}_add_global_target(TARGET_NAME)全局必须可为空实现通过add_custom_target(${TARGET_NAME} ...)创建顶层目标如make dict若传ALL参数则随常规make一并执行${TARGET_NAME}_add_module_target(MODULE_NAME TARGET_NAME SOURCE_FILES DEPENDENCIES)每个模块必须可为空实现为每个模块注册MODULE_target子目标可通过DEPENDS依赖自动编码器输出AC_OUTPUTS以保证在自动代码生成之后执行${TARGET_NAME}_add_deployment_target(MODULE TARGET SOURCES DEPENDENCIES FULL_DEPENDENCIES)每个部署必须可为空实现针对register_fprime_deployment注册的部署模块执行汇总逻辑FULL_DEPENDENCIES为递归解析后的完整依赖列表这里需要说明命名细节target 文件内部定义的函数实际名称由目标名文件名加函数名后缀构成。例如 cmake/target/version.cmake 定义的是version_add_global_target文件名version.cmake的 stem 是versioncmake/target/check.cmake 定义的是check_add_global_target、check_add_module_target、check_add_deployment_target。系统通过get_target_name见 cmake/target/target.cmake从目标文件路径提取 stem 得到TARGET_NAME并据此派生出模块级目标名MODULE_target。执行顺序与调度机制setup_global_targets遍历全局属性FPRIME_TARGET_LIST与FPRIME_UT_TARGET_LIST后者仅在BUILD_TESTING时生效为每个已注册 target 调用setup_global_targetsetup_global_target通过plugin_include_helpercmake/utilities.cmake惰性加载 target 文件——仅当以target名_add_global_target命名的命令尚未定义时才include该文件随后用cmake_language(CALL ...)动态调用setup_module_targets/setup_single_target在注册模块时被调用根据模块的FP_TYPE属性分发普通模块调用add_module_targetDeployment类型模块调用add_deployment_target并传入模块源码、依赖及对部署递归依赖列表。recurse_targets负责从FPRIME_TARGET_DEPENDENCIES与FPRIME_MODULES全局属性中递归收集所有 F´ 模块依赖。这套惰性加载 命名约定 动态调用的机制见 cmake/utilities.cmake 的注释dispatch_function(PLUGIN_NAME ...)分发模式使得多个 target 文件之间互不干扰且可以在不加载全部文件的情况下按需初始化。四、注册目标register_fprime_target 与 register_fprime_ut_target自定义 target 文件的注册入口是register_fprime_target宏定义于 cmake/API.cmakemacro(register_fprime_target TARGET_FILE_PATH) if (CMAKE_DEBUG_OUTPUT) message(STATUS [target] Registering custom target: ${TARGET_FILE_PATH}) endif() register_fprime_list_helper(${TARGET_FILE_PATH} FPRIME_TARGET_LIST OFF) endmacro(register_fprime_target)其参数TARGET_FILE_PATH可以是文件路径直接指向定义目标的 CMake 文件短 include 路径要求该文件所在目录已加入 CMake 的 include path例如内置目标直接写target/build。被注册的文件应定义前述三个函数add_global_target、add_module_target、add_deployment_targetAPI.cmake 的注释中明确列出。注册后目标会进入全局属性FPRIME_TARGET_LIST由构建系统在各模块设置阶段统一展开。配套宏register_fprime_ut_targetcmake/API.cmake与前者完全一致但仅当BUILD_TESTINGON时才注册——这是ut、check等测试类目标使用的注册通道保证生产构建不会携带测试目标。此外cmake/FPrime.cmake 中的fprime_setup_override_targets展示了目标的覆盖机制当用户设置FPRIME_SUB_BUILD_TARGETS变量一个目标文件列表时系统跳过标准目标注册仅注册用户指定的目标从而实现仅子构建或自定义目标集合的构建模式。在cmake/test/data/TestDeployment/CMakeLists.txtcmake/test/data/TestDeployment/CMakeLists.txt中可以看到真实调用示例register_fprime_target(target/test) # Test target and autocoder supporting it五、编写自定义目标从钩子模式到完整示例Customization 指南 将自定义目标的实现方式总结为钩子模式hook pattern创建一个 CMake 文件在其中定义add_global_target与add_module_target两个函数按前述命名约定实际函数名前缀为目标名通过register_fprime_target注册该文件在两个 add 函数内部使用 CMake 原生的add_custom_target来组装目标本身。该指南特别指出如果目标不需要同时具备每模块与全局两个步骤用户也可以直接调用add_custom_target添加目标即跳过钩子模式写一个裸全局目标。裸全局目标示例Customization.md 给出的最小示例——生成 Python 字典目录的dict目标add_custom_target( dict COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/dict/serializable ${CMAKE_SOURCE_DIR}/py_dict/serializable COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR}/py_dict/serializable/__init__.py )运行方式cmake ../Ref make dict这个例子展示了add_custom_target的典型用法通过COMMAND指定一个或多个要执行的命令这里用cmake -E完成目录拷贝与空文件创建从而得到可导入的 Python 包结构。完整钩子模式示例全局 模块级 部署级参照内置目标的实现风格一个同时覆盖全局与模块级视图的 target 文件骨架如下# count.cmake —— 统计文件数的自定义目标 # 命名约定函数名前缀 目标文件名count function(count_add_global_target TARGET) add_custom_target(${TARGET} COMMAND ${CMAKE_COMMAND} -E echo Counting all files... # 在这里添加全局统计命令例如递归统计仓库文件总数 ) endfunction(count_add_global_target) function(count_add_module_target MODULE_NAME TARGET_NAME SOURCE_FILES DEPENDENCIES) add_custom_target(${MODULE_NAME}_${TARGET_NAME} COMMAND ${CMAKE_COMMAND} -E echo Counting files in ${MODULE_NAME}: ${SOURCE_FILES} # 在这里对 SOURCE_FILES 执行按模块统计 DEPENDS ${AC_OUTPUTS} # 需要时依赖自动编码输出 ) endfunction(count_add_module_target) function(count_add_deployment_target MODULE TARGET SOURCES DEPENDENCIES FULL_DEPENDENCIES) add_custom_target(${MODULE}_${TARGET} COMMAND ${CMAKE_COMMAND} -E echo Rolling up count for deployment ${MODULE} # 汇总该部署内所有依赖模块的统计结果 ) endfunction(count_add_deployment_target)注册方式在部署顶层 CMakeLists.txt 或自定义构建入口中list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # 确保 include 路径 register_fprime_target(count) # 使用短路径 # 或 register_fprime_target(${CMAKE_CURRENT_LIST_DIR}/cmake/count.cmake) # 使用文件路径注册后即可执行make count # 全局统计 make Ref_count # 按部署汇总 make Svc_TlmChan_count # 按模块统计针对 Svc/TlmChan 模块参数说明与实现要点add_module_target收到的SOURCE_FILES是模块 CMakeLists.txt 中通过set(SOURCE_FILES ...)指定的手写源码AC_INPUTS/AC_OUTPUTS分别是自动编码器的输入.xml/.fpp文件与输出Ac.cpp/Ac.hppMOD_DEPS是set(MOD_DEPS ...)指定的依赖。需要让目标在自动代码生成之后运行就应把AC_OUTPUTS放入add_custom_target的DEPENDS见 cmake/target/target.cmake 的规范。若希望目标随常规make一并构建在add_custom_target中传ALL参数见 cmake/target/target.cmake。模块级与部署级函数必须定义但允许为空实现default.cmakecmake/target/default.cmake的默认实现中add_module_target为空、add_deployment_target仅做依赖汇总、add_global_target仅创建空目标而setup_global_target的注释也强调所有 target 必须定义target名_add_global_target即使它是空函数。这保证系统在调度时不会因函数缺失而崩溃。default.cmake中add_deployment_target的默认行为值得借鉴它创建${MODULE}_${TARGET}空目标后遍历依赖列表把每个存在的${DEPENDENCY}_${TARGET}通过add_dependencies挂到其上——这就是模块级产物汇总到部署级/全局的标准模式。六、常见问题与调试技巧make MODULE_target找不到目标确认该目标文件已通过register_fprime_target注册且模块确实存在。目标名由文件名 stem 派生例如注册count.cmake得到count与MODULE_count。若使用短路径注册请确认目录已加入CMAKE_MODULE_PATH。区分register_fprime_target与register_fprime_ut_target测试相关目标如check必须用后者注册它仅在BUILD_TESTINGON时生效cmake/API.cmake。调试输出在 CMake 配置时设置CMAKE_DEBUG_OUTPUT为 ON可以看到[target] Registering custom target: ...、[target] Setting up ... on all module ...等日志见 cmake/API.cmake 与 cmake/target/target.cmake用于定位注册与分发过程中的问题。模块级与部署级二选一setup_single_target只会对某个模块调用两个函数之一普通模块走add_module_targetDeployment类型走add_deployment_target不要假设两者都会被执行cmake/target/target.cmake。自定义目标的官方范例仓库cmake/target/目录下的每个内置文件本身都是 target 文件的最佳范本编写自定义目标时可直接对照 cmake/target/version.cmake、cmake/target/check.cmake 等实现。总结F´ 的 Targets 子系统以一个 target 文件、三类钩子函数、双层执行入口的简洁模型把任意自定义动作平滑地接入 CMake 构建流程add_global_target提供全局入口add_module_target提供逐模块入口add_deployment_target提供逐部署的汇总入口三者由构建系统统一调度cmake/target/target.cmake。内置的build、version、install、ut、check、refresh_cache等目标注册逻辑见 cmake/FPrime.cmake覆盖了编译、版本生成、安装与测试等标准需求而register_fprime_target与register_fprime_ut_targetcmake/API.cmake则向开发者开放了同等的扩展能力。无论是要实现统计文件总数这类工具型目标还是要构建先按模块生成字典片段、再汇总全局字典的多阶段目标都可以在本文给出的骨架之上直接落地。赞分享嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fp/fprime点击查看免费下载相关推荐F´ 构建系统 Targets 完全指南内置 Target 解析与自定义 Target 注册F´ 构建系统 Targets 完全指南内置 Target 解析与自定义 Target 注册 F´F Prime的 CMake 构建系统提供了一套可扩展的嵌入式系统编程Cytoscape.js 布局Layout体系详解从内置算法到自定义扩展Cytoscape.js 布局Layout体系详解从内置算法到自定义扩展 布局Layout是 Cytoscape.js 中负责 为图中每个节点计算坐标数据可视化Burn 训练框架指标体系详解从内置 Metric、Adaptor 适配到自定义指标实现Burn 训练框架指标体系详解从内置 Metric、Adaptor 适配到自定义指标实现 Burn 的 Learner 训练框架内置了一套可插拔的指标MetUI组件前端上一篇CANN/ascend-transformer-boost ReshapeAndCache C示例下一篇FanControl 风扇曲线快速上手指南华硕主板风扇噪音优化的 4 套参数与完整清单创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表