首页 / 资讯中心 / 文章详情

CMake 教程 Step 11 实战:目标别名(ALIAS)与生成器表达式($<CONFIG>)——被主教程遗漏的“杂项特性“精讲

CMake 教程 Step 11 实战:目标别名(ALIAS)与生成器表达式($<CONFIG>)——被主教程遗漏的“杂项特性“精讲 ★ FEATURED ARTICLE
构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载本指南以 CMake 官方教程的收官章节 Help/guide/tutorial/Miscellaneous Features.rstStep 11为骨架系统讲解两类在常规教程中不易展开、却在真实项目中举足轻重的特性目标别名Target Aliases与生成器表达式Generator Expressions。读完本文你将掌握如何用add_library(... ALIAS ...)让add_subdirectory与find_package两条消费路径共享同一套命名空间接口以及如何借助$CONFIG这类延迟求值表达式在编译定义、依赖注入等场景中让构建配置注入到目标内部并配合仓库中 Step11 的完整示例代码完成动手练习。一、为什么主教程之外还需要杂项特性官方教程在 Miscellaneous Features.rst 开头就点明了这一章的定位有些特性不适合在主教程中占据篇幅或重要程度不足以被单独讲解但它们值得被提及因此被归为bonuses加分项。文档同时强调了一个容易被忽略的事实CMake 有大量未在教程中覆盖的特性其中一些被使用它们的项目视为必不可少essential另一些则被**打包者packagers**广泛使用但在软件开发者日常讨论中鲜少出现。也就是说这一章收集的两类练习恰恰是本地构建者不关心、但面向包分发与依赖治理时绕不开的能力目标别名Target Aliases——统一add_subdirectory与find_package两种依赖消费方式的接口生成器表达式Generator Expressions——将配置阶段未知、生成阶段才可知的信息注入构建系统。文档也声明这份清单并非 CMake 全部能力的穷举会随时代与相关性增长或收缩——因此这两讲本质上是打开一扇窗帮助读者意识到教程之外的广阔空间。二、Step 11 工程全景SimpleTest 与 TutorialProject 的分工动手之前先看清 Step 11 目录里两套工程的关系。仓库中该章节的完整布局位于 Help/guide/tutorial/Step11SimpleTest/一个极简的测试框架工程project(SimpleTest VERSION 0.0.1)见 SimpleTest/CMakeLists.txt。它定义INTERFACE库SimpleTest通过install(EXPORT ... NAMESPACE SimpleTest::)导出为SimpleTest::SimpleTest见 SimpleTest/CMakeLists.txt并连同SimpleTestConfig.cmake等文件一起安装到lib/cmake/SimpleTest目录。TutorialProject/教程的主工程。其测试目标TestMathFunctions通过find_package(SimpleTest REQUIRED)找到已安装的 SimpleTest并链接SimpleTest::SimpleTest见 Tests/CMakeLists.txt测试用例则直接使用TEST/REQUIRE宏断言数学函数见 Tests/TestMathFunctions.cxx。两套工程都通过CMakePresets.json提供tutorial预设如 TutorialProject/CMakePresets.json 所示预设将CMAKE_PREFIX_PATH指向install目录并关闭了 IPO因此后续所有构建命令都基于cmake --preset tutorial执行。Step 11 的两个练习分别落在两个工程中练习一改TutorialProject/MathFunctions/CMakeLists.txt练习二改SimpleTest/CMakeLists.txt。三、练习一目标别名Target Aliases3.1 动机add_subdirectory与find_package的命名空间鸿沟教程主体Step 10 之前的章节聚焦安装依赖并在安装树中消费它并推荐借助包管理器完成这一过程。但正如 Miscellaneous Features.rst 所指出的由于历史与现实的多重原因CMake 项目并非总是这样被消费的当依赖的源码被**整体嵌入vendor**父项目通过add_subdirectory引入时暴露出来的目标名是依赖项目内部使用的名字这些名字没有install(EXPORT)会加上的命名空间前缀如Tutorial::、SimpleTest::。于是同一个库在源码内嵌与包管理器安装两种场景下出现了两套目标名。为了让两种工作流对使用者呈现一致的接口CMake 提供了别名机制:command:add_library(ALIAS)与:command:add_executable(ALIAS)。文档给出了别名的最小示例add_library(MyLib INTERFACE) add_library(MyProject::MyLib ALIAS MyLib)3.2add_library(... ALIAS ...)的语法与规则在 Help/command/add_library.rst 的 Alias Libraries 一节官方对别名目标的规则有精确定义add_library(name ALIAS target)别名name可以在后续命令中代表target使用但不会作为 make 目标出现在生成的构建系统中被引用的target本身不能是另一个 ALIAS 目标别名目标可以被链接、可以读取其属性也可以用常规的if(TARGET)子命令检测其存在版本 3.11 起ALIAS 可以指向GLOBAL导入目标Imported Target3.18 起可以指向非 GLOBAL 导入目标此时别名仅在其创建目录及其以下作用域有效并可通过目标属性ALIAS_GLOBAL判断别名是否为全局版本 4.5 起别名name还可作为set_property、set_target_properties、target_link_libraries等命令的操作数来修改target的属性若将 ALIAS 目标传给install或export命令实际安装/导出的是其引用的目标。3.3 实操为 MathFunctions 添加别名TODO 1练习目标一句话概括为MathFunctions库添加一个与导出目标一致的库别名。需要编辑的文件是 Step11/TutorialProject/MathFunctions/CMakeLists.txt其中第 2 行留有# TODO1:注释。对照仓库中已完成全部练习的答案工程 Complete/TutorialProject/MathFunctions/CMakeLists.txtTODO 1 的完整解只需一行add_library(MathFunctions) add_library(Tutorial::MathFunctions ALIAS MathFunctions)为什么别名要命名为Tutorial::MathFunctions因为 TutorialProject 在 Step11/TutorialProject/CMakeLists.txt 中通过install(EXPORT TutorialTargets ... NAMESPACE Tutorial::)导出目标find_package消费者看到的名字正是Tutorial::MathFunctions。别名让add_subdirectory的消费者也能以完全相同的方式引用它——这就是文档所说的与 find_package 消费者所见接口保持一致。3.4 构建与运行文档给出的构建流程分两步先配置并安装SimpleTest再构建TutorialProject。# 在 Help/guide/Step11/SimpleTest 目录下 cmake --preset tutorial cmake --install build # 在 Help/guide/Step11/TutorialProject 目录下 cmake --preset tutorial cmake --build build文档特别提醒添加别名后行为不应有任何可观察的变化——这正是别名设计的精髓它不改变构建产物与依赖图只统一命名空间接口。四、练习二生成器表达式Generator Expressions4.1 概念延迟求值的条件表达式cmake-generator-expressions(7) 是 CMake 支持的一种复杂领域特定语言DSL官方文档将其概括为Generator expressions are evaluated during build system generation to produce information for each specific build configuration.即生成器表达式在构建系统生成阶段才被求值以产出针对每个具体构建配置的信息。原教程文档对它的通俗解释是——最容易将其理解为延迟求值的条件表达式deferred-evaluation conditionals它表达的需求其输入在 CMake 配置configure阶段尚不可知。也正因如此这类表达式被称为生成器表达式它们是在底层构建系统被生成时才求值的。历史上生成器表达式常与target_include_directories配合用于表达构建树与安装树之间的包含目录需求文档指出这一用途已被文件集FILE SET取代Step 11 的 CMakeLists 中大量使用的FILE_SET HEADERS正是例证。如今生成器表达式最常见的应用场景是多配置生成器multi-config generators与复杂的依赖注入系统dependency injection systems。文档给出的入门示例target_compile_definitions(MyApp PRIVATE MYAPP_BUILD_CONFIG$CONFIG)4.2 核心表达式$CONFIG深入解读练习二使用的$CONFIG是生成器表达式中最常用也最直观的一个。在 cmake-generator-expressions.7.rst 中其定义为$CONFIG配置名Configuration name官方明确建议用它取代已弃用的CONFIGURATION表达式$CONFIG:cfgs若当前配置是逗号分隔列表cfgs中的任意一项则求值为1否则为0。比较不区分大小写当该表达式作用于IMPORTED目标的属性时还会考虑MAP_IMPORTED_CONFIG_CONFIG的映射。自 3.19 起cfgs支持同时指定多个配置。理解$CONFIG的关键在于两个配置阶段可能不同在多配置生成器如 Visual Studio、Xcode、Ninja Multi-Config下同一个构建目录可产出 Debug / Release / RelWithDebInfo 等多个配置的产物而配置configure阶段根本不存在单一当前配置只有到生成generate阶段、为每个具体配置生成构建规则时$CONFIG才被替换为对应的配置名。4.3 实操为 SimpleTest 添加$CONFIG编译定义TODO 2练习二的目标是在SimpleTest中添加一个生成器表达式把构建配置写进一条编译定义里。需要编辑 Step11/SimpleTest/CMakeLists.txt其中第 17-18 行留有# TODO2:注释。答案工程的 Complete/SimpleTest/CMakeLists.txt 给出的完整解同样是一行但与文档示例不同之处在于使用了INTERFACE关键字target_compile_definitions(SimpleTest INTERFACE SIMPLETEST_CONFIG$CONFIG)SimpleTest本身是INTERFACE库只传递接口、不产生编译产物因此编译定义必须通过INTERFACE传播给消费者$CONFIG会按构建该可执行文件所用的配置求值——正如文档所强调的这个配置未必等于配置 SimpleTest 时的配置。该宏的消费端在 Complete/SimpleTest/SimpleTest.h 与 同文件 L132-L135运行测试时若定义了SIMPLETEST_CONFIG会通过SIMPLETEST_XSTRINGIFY(SIMPLETEST_CONFIG)字符串化宏打印一行SimpleTest built with config: 配置名。于是运行时即可直观验证编译定义中的配置究竟来自哪个构建配置。4.4 构建、运行与CMAKE_BUILD_TYPE的影响构建命令与练习一相同# 在 Help/guide/Step11/SimpleTest 目录下 cmake --preset tutorial cmake --install build # 在 Help/guide/Step11/TutorialProject 目录下 cmake --preset tutorial cmake --build build然后直接运行TestMathFunctions二进制应当看到一条指名构建该可执行文件所用的配置的消息。文档特别指出验证技巧在单配置生成器single-configuration generators上可以通过设置CMAKE_BUILD_TYPE变量来改变构建配置。对应到仓库中的教程说明Before You Begin.rstCMAKE_BUILD_TYPE可通过环境变量或cmake -DCMAKE_BUILD_TYPEconfig直接指定。修改CMAKE_BUILD_TYPE后重新构建TestMathFunctions打印的配置名应随之变化而在多配置生成器下同一构建目录内不同配置的构建会产生不同的$CONFIG求值结果——这正是生成器表达式在多配置场景中的价值所在。五、组合视角两个杂项特性如何协同支撑依赖治理把两个练习放在一起看能更完整地理解它们服务于同一个目标——让一个库在源码内嵌与包分发两条消费路径下呈现一致的接口与行为目标别名解决接口命名问题add_subdirectory消费者通过Tutorial::MathFunctions引用目标与find_package消费者所见完全一致生成器表达式解决行为差异化问题无论库以何种方式被消费SIMPLETEST_CONFIG$CONFIG都能在生成阶段把真实构建配置注入编译定义让测试框架报告与最终二进制一致的配置信息而不是配置阶段的静态值。这两点恰是打包者packager日常最关心的两类细节也呼应了 Miscellaneous Features.rst 开篇的论断教程未覆盖的特性往往是使用它们的项目眼中essential的能力。后续深入学习时可继续查阅 Help/command/add_library.rst目标命令全量文档、Help/manual/cmake-generator-expressions.7.rst生成器表达式手册涵盖$CONFIG、$LINK_ONLY、$TARGET_FILE等数百个表达式以及教程目录 Help/guide/tutorial 中的其他章节从杂项走向系统化掌握。赞分享构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载相关推荐CMake高级特性生成器表达式与策略系统CMake高级特性生成器表达式与策略系统 本文深入探讨CMake的两个核心高级特性生成器表达式和策略系统。生成器表达式是CMake的元编程工具允许在生成构构建工具开发工具CLI社区贡献指南如何参与Qwen3.5-9B-GLM5.1-Distill-v1的改进与优化社区贡献指南如何参与Qwen3.5 9B GLM5.1 Distill v1的改进与优化 Qwen3.5 9B GLM5.1 Distill v1是基于QweMMTextFieldEffects之Nao特效终极解析贝塞尔曲线路径变形动画的完整实现原理MMTextFieldEffects之Nao特效终极解析贝塞尔曲线路径变形动画的完整实现原理 如何让 iOS 的 UITextField 输入框拥有波浪线上一篇探索高效代码导航Vim-Gutentags下一篇wc/wcf跨平台部署教程Windows、Linux与macOS环境配置详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站