
RIOT 特性清单自动化features_yaml2mx 如何从单一 YAML 生成构建系统与 Doxygen 文档【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOTdist/tools/features_yaml2mx是 RIOT 仓库中的一个命令行工具负责把以 YAML 描述的特性feature清单转换为两种下游产物一份供构建系统Makefile校验特性存在性的FEATURES_EXISTING清单以及一份供 Doxygen 消费的 Markdown 特性文档。本文以该工具目录下的 README.md 为主线结合 features_yaml2mx.py、schema.cddl 以及仓库根目录的 features.yaml 和 Makefile完整讲解该工具的输入语法、转换逻辑、命令行接口与在 RIOT 构建体系中的实际用途。一、工具定位一份 YAML两个消费端在 RIOT 的构建系统中特性feature是描述 CPU / 板卡能力的最小构建实体例如arch_32bit表示 CPU 是 32 位架构periph_uart表示存在 UART 外设。这些特性同时服务于两个方向构建系统方向Makefile 需要知道平台上存在哪些特性FEATURES_EXISTING以便在编译前校验某个应用FEATURES_REQUIRED请求的特性是否真的存在文档方向Doxygen 需要一份人类可读的特性清单把每个特性的含义help文本展示给开发者。为了避免同一份清单在两个地方手工维护而失控RIOT 选择了**单一事实源single source of truth**的方案仓库根目录的 features.yaml 是唯一需要维护的文件features_yaml2mx脚本将其转换为makefiles/features_existing.inc.mk—— Makefile 语法供构建系统直接 includedoc/doxygen/src/feature_list.md—— Markdown 格式供 Doxygen 作为文档页渲染。原 README 明确指出YAML 文件即仓库根目录的features.yaml其语法在该文件顶部注释中有说明而脚本应当通过仓库根目录执行make generate-features来调用。二、YAML 输入语法分组group与特性featurefeatures.yaml 顶部注释完整定义了语法。整个文件的核心结构如下%YAML 1.2 --- groups: - title: 分组标题 help: 分组说明文字 features: - name: 特性名 help: 特性描述 groups: - title: 嵌套子分组 ...关键语法规则group分组是一个映射mapping可包含字符串类型的title和help分组下可挂groups键值为一组子分组从而形成任意深度的树形层次分组下可挂features键值为特性列表feature特性是一个映射必须包含name字符串和可选的help字符串。该语法并非口语化描述而是有形式化定义dist/tools/features_yaml2mx/schema.cddl使用 CDDLConcise Data Definition Language精确约束了文件结构其要点如下root group-toplevel group-toplevel group .within { groups any } group-nested group .within { title tstr, any any } group { ? title tstr, ? help tstr, ? features [ feature ], ? groups [ group-nested ], } feature { name tstr, ? help tstr, }从这份 schema 可以看出顶层group-toplevel必须至少包含一个groups键即整个文件以分组为根嵌套分组group-nested必须包含titlefeatures是非空列表[ feature ]且每个feature的name是必填项help为可选项title、help、features、groups四个键均为可选键用?标注这给分组表达提供了很大灵活性。仓库根目录的features.yaml展示了真实的层次组织方式顶层分组包括Architecture Features、CPU Features、Arduino Features、RAM Related Features、Bluetooth Low Energy Features、Toolchain Features、Peripheral Features、Other Features、Board Features、以及Bugs in hardware, toolchain, or binary blobs等。其中Peripheral Features内部又嵌套了 GPIO、Serial Interfaces、Analog Features、Timer Features、Flash Features、Cryptographic Features 等子分组验证了 CDDL schema 对递归嵌套的支持。一个典型的叶子分组与特性条目如下取自 features.yaml- title: Word size help: Word size of the CPU features: - name: arch_8bit help: CPU has a 8-bits architecture - name: arch_16bit help: CPU has a 16-bits architecture - name: arch_32bit help: CPU has a 32-bits architecture - name: arch_64bit help: CPU has a 64-bits architecture另需注意两点help文本中允许包含 Doxygen 交叉引用如ref cpu_check_address、ref sys_puf_sram生成 Markdown 文档时会原样保留供 Doxygen 解析成内部链接同时文件注释明确提醒该语法乃至文件格式未来可能继续演进因此任何依赖此格式的脚本都应以上述 CDDL schema 为准。三、转换脚本源码剖析features_yaml2mx.py 是约 160 行的 Python 3 脚本依赖PyYAMLimport yaml整体流程为读取并yaml.safe_load输入文件 → 提取特性名集合 → 分别写出 Makefile 与 Markdown 文档。其函数结构如下。3.1collect_features()递归收集所有特性名def collect_features(parsed): result [] for group in parsed.get(groups, []): result collect_features(group) for feature in parsed.get(features, []): result.append(feature[name]) return result该函数对分组树做深度优先遍历先递归进入所有子分组再收集当前分组的特性名最终返回一个无序、顺序不稳定的特性名列表。注意它只取feature[name]说明 Makefile 产物只需要特性名集合描述文本全部留给 Markdown 产物。3.2write_makefile()生成FEATURES_EXISTING清单def write_makefile(outfile, yaml_path, parsed): outfile.write(f\ # WARNING: This has been auto-generated from {yaml_path}. # Do not edit this by hand, but update {yaml_path} instead. # Finally, run make generate-features in the root of the RIOT repo. ) outfile.write(FEATURES_EXISTING : \\\n) for feature in sorted(collect_features(parsed)): outfile.write(f {feature} \\\n) outfile.write( #\n) outfile.flush()输出格式为典型的 GNU Make 多行变量续行写法FEATURES_EXISTING : \ feat_a \ feat_b \ feat_c \ #实现细节值得注意特性名先经sorted()排序再输出保证产物确定性、便于 diff 审查文件头写入自动生成警告明确告知维护者不要手工编辑此文件应修改features.yaml并重新运行make generate-features行尾的#是 GNU Make 续行结束符的惯用技巧空注释行防止末项后残留空格。该函数实际生成的产物即 makefiles/features_existing.inc.mk内容与函数签名完全一致首部为 WARNING 注释随后是以\续行的全部特性名最后以#收尾。3.3write_md_section()递归生成 Markdown 小节def write_md_section(outfile, group, level): title group.get(title) outfile.write(# * level f {title} if title else \n) if help in group: outfile.write(\n) outfile.write(group[help]) outfile.write(\n) if features in group: outfile.write(\n) outfile.write(\ | Feature | Description | |:--------------------------------- |:----------------------------------------------------------------------------- | ) for feature in group[features]: name f{feature[name]} description feature[help].strip().replace(\n, ) outfile.write(f| {name:33} | {description:77} |\n) for group in group.get(groups, []): outfile.write(\n) write_md_section(outfile, group, level 1)每个分组映射为一个 Markdown 小节小节标题由title决定层级由level控制顶层从 0 开始随嵌套递增对应#、##、###等分组的help文本原样写入标题下方作为引言特性列表渲染为 Markdown 表格列为Feature / Description特性名用反引号包裹\name描述则把help 中的换行折叠为单个空格以保持表格单行整洁子分组通过递归调用自身、level 1降级渲染保持与 YAML 相同的树形层次。3.4write_mdfile()Markdown 文档外壳outfile.write(f\ # List of Features (Features as Build System Enties) {{#feature-list}} !-- WARNING: This has been auto-generated from {yaml_path}. Do not edit this by hand, but update {yaml_path} instead. Finally, run make generate-features in the root of the RIOT repo. -- [TOC] ) write_md_section(outfile, parsed, 0)生成的 Markdown 文档以# List of Features (Features as Build System Enties) {#feature-list}作为文档主标题锚点名#feature-list供 Doxygen 引用随后是同样的自动生成警告注释与[TOC]目录标记最后从顶层分组level 0开始渲染整棵树。3.5convert_features()与命令行接口def convert_features(yaml_file, mk_file, md_file): with open(yaml_file, rb) as file: parsed yaml.safe_load(file) if mk_file is not None: with open(mk_file, w, encodingutf-8) as file: write_makefile(file, yaml_file, parsed) if md_file is not None: with open(md_file, w, encodingutf-8) as file: write_mdfile(file, yaml_file, parsed)脚本通过argparse暴露如下命令行参数参数说明INPUT位置参数必填输入 YAML 文件路径--output-md PATH生成的 Markdown 文档输出路径默认不生成文档--output-makefile PATH生成的 Makefile 输出路径默认不生成 Makefilemk_file与md_file任一为None即跳过对应产物因此该脚本可单独用于只生成 Makefile 或只生成文档convert_features()在 YAML 解析yaml.safe_load失败时直接抛出异常终止。四、构建系统集成make generate-features与 doc 目标原 README 强调脚本应通过仓库根目录执行make generate-features调用。查看根目录 Makefile 可以还原完整的接线方式generate-features: ./dist/tools/features_yaml2mx/features_yaml2mx.py \ features.yaml \ --output-makefile makefiles/features_existing.inc.mk doc doc-man doc-latex doc-ci: ./dist/tools/features_yaml2mx/features_yaml2mx.py \ features.yaml \ --output-md doc/doxygen/src/feature_list.md $(MAKE) -C doc/doxygen $两条目标展示了工具在两种场景下的标准用法make generate-features以features.yaml为输入、makefiles/features_existing.inc.mk为输出仅生成 Makefile 清单——这是特性清单的日常再生成入口make doc及 doc-man / doc-latex / doc-ci先把features.yaml转换为doc/doxygen/src/feature_list.md再进入doc/doxygen子目录执行 Doxygen 构建把该 Markdown 文档编译进 API 文档。也就是说脚本同时承担着构建元数据与文档源文件两种角色的生成任务而这两种角色共享同一份 YAML从而保证文档与实际构建行为永远一致。作为交叉验证makefiles/features_existing.inc.mk 首部注释明确写着auto-generated from features.yamlfeatures.yaml顶部注释也声明其产出的两份文件分别是makefiles/features_existing.inc.mk和doc/doxygen/src/feature_list.md。五、下游消费FEATURES_EXISTING在构建系统中的作用生成的FEATURES_EXISTING变量并非摆设它是 RIOT 特性校验机制的数据基础。在make generate-features产物 makefiles/features_existing.inc.mk 中变量以续行形式列出当前仓库定义的全部特性例如FEATURES_EXISTING : \ arch_16bit \ arch_32bit \ ... periph_uart \ ... xiao_shield \ #该变量随后被 RIOT 构建系统makefiles 目录下的 features 校验逻辑用于判断某个平台是否提供某特性与 CPU/板卡的Makefile.features汇总结果比对校验应用通过FEATURES_REQUIRED请求的特性是否存在于FEATURES_EXISTING不存在则编译时报错从而把特性拼写错误/平台不支持的问题尽早暴露供FEATURES_OPTIONAL做存在性探测据此条件编译。同时features.yaml中每个help文本都尽量描述了特性的确切语义与使用前提例如periph_dma的 help 说明启用该特性会影响其他外设驱动的实现SPI/I²C/UART 传输在超过特定长度时可能改用 DMAbug_newlib_broken_stdio的 help 则说明其代表 newlib stdio 的线程安全问题并指明该特性一旦提供就总是被使用FEATURES_USED中任何bug_前缀特性不受构建配置影响。这些描述正是通过本工具流向 Doxygen 文档成为开发者选择特性的依据。六、工作流总结与使用建议综合 README、脚本源码与构建系统集成features_yaml2mx的完整工作流如下维护单一事实源在仓库根目录 features.yaml 中按 CDDL schema见 schema.cddl增删特性或分组并填写准确的name与help重新生成构建清单在仓库根目录执行make generate-features脚本依据 features_yaml2mx.py 重新生成 makefiles/features_existing.inc.mk重新生成文档执行make doc或 doc-man 等目标脚本先把 YAML 转为doc/doxygen/src/feature_list.md再由 Doxygen 编译进 API 文档提交产物由于两份产物均带auto-generated警告改动 YAML 时应同时提交重新生成的 Makefile 与文档避免两份产物与 YAML 失步。需要注意的是无论是 Makefile 还是 Markdown 文档都应视为生成物而非手写源文件手工修改 makefiles/features_existing.inc.mk 的编辑会在下一次make generate-features时被覆盖。对于需要在自定义构建流程中直接使用该脚本的场景可直接调用python3 dist/tools/features_yaml2mx/features_yaml2mx.py yaml --output-makefile mk --output-md md其位置参数与两个可选参数的行为与上文表格一致。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考