ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Buck 仓库中的 bazel-skylib:Skylark 构建规则标准库与 skylark_library 规则解析

Buck 仓库中的 bazel-skylib:Skylark 构建规则标准库与 skylark_library 规则解析 开发工具构建工具【免费下载链接】buckA fast build system that encourages the creation of small, reusable modules over a variety of platforms and languages.项目地址https://gitcode.com/gh_mirrors/bu/buck点击查看免费下载Skylib 是一套面向 Bazel/Skylark 构建规则开发的“标准库”提供集合、字典、文件路径、select 选择、shell 转义和单元测试等工具函数。本文基于 Buck 仓库中 vendored 的 bazel-skylib 快照 及其模块源码讲解其模块组织方式、各模块的实际 API、聚合规则skylark_library的实现原理以及它在 Buck 自身 Bazel 迁移规则中的真实用途读完后可独立为.bzl规则文件选择和使用这些标准函数。Skylib 的定位为什么 Buck 仓库里有一份 Bazel 库README.md 的开篇定义Skylib 是“a standard library that provides functions useful for manipulating collections, file paths, and other features that are useful when writing custom build rules in Bazel”——即一套专门服务于自定义构建规则编写的函数库而非常规构建产物库。README 同时明确提示该库处于早期开发阶段API 在此期间可能发生变化。结合仓库内的其他文件可以确认它的来源与用途README.facebook 说明该目录是从bazelbuild/bazel-skylib上游仓库整体拷贝并“as-is”展开的属于第三方 vendored 代码WORKSPACE 中声明workspace(name bazel_skylib)即它作为独立 Bazel 工作区时暴露的仓库名从 Buck 自身的构建文件来看它在被加载时使用了buck_bazel_skylib这一仓库名例如 tools/build_rules/module_rules.bzl 与 tools/build_rules/java_rules.bzl 均有load(buck_bazel_skylib//lib:collections.bzl, collections)programs/BUCK 中则有load(buck_bazel_skylib//lib:dicts.bzl, dicts)。也就是说Buck 仓库把 Skylib 作为外部依赖external repository引入供其 Bazel 迁移用的tools/build_rules/*.bzl规则集调用这正是 Skylib 设计初衷“支撑自定义规则开发”的落地场景。模块体系.bzl文件即“模块”lib.bzl作为索引README 定义了 Skylib 的核心组织约定lib/目录下的每个.bzl文件定义一个“模块”module——一个struct内含一组相关函数和其他符号可作为单个单元被加载顶层文件lib.bzl充当索引index供其他模块从中统一导入使用方式是load(bazel_skylib//:lib.bzl, ...)后通过点号访问 struct 字段。README 给出的示例load(bazel_skylib//:lib.bzl, paths, shell) p paths.basename(foo.bar) s shell.quote(p)对照实际源码每个模块都严格遵循“私有实现 公开 struct 导出”的模式。以 lib/collections.bzl 为例collections struct( after_each _after_each, before_each _before_each, uniq _uniq, )私有函数以下划线命名_after_each等仅在文件内部可见调用方通过collections.uniq(...)这样的命名空间访问公开 API。README 中“编写新模块”一节正是这一约定的规范化描述详见后文。需要说明的一个事实细节当前快照中的 lib.bzl 仅包含版权头和一行 docstringIndex from which multiple modules can be loaded.并未内联任何load语句。实际的聚合工作由根目录 BUILD 中的skylark_library(name lib, ...)目标承担其deps列出了全部模块//lib:collections、//lib:dicts、//lib:paths、//lib:selects、//lib:sets、//lib:shell、//lib:structs、//lib:unittest。README 的“List of modules”列出的是 collections、dicts、paths、selects、sets、shell、unittest 七个模块而lib/目录中实际还存在 structs.bzl并被根 BUILD 的聚合目标依赖但未被 README 列入清单——这印证了 README 中“API 可能变动”的版本提示引用时以实际文件为准。核心模块 API 与实现解析以下逐个模块给出本快照中实际可用的函数及实现要点路径均相对于仓库根目录。collections序列操作lib/collections.bzl 导出三个函数collections.after_each(separator, iterable)在每个元素之后插入分隔符返回新列表collections.before_each(separator, iterable)在每个元素之前插入分隔符collections.uniq(iterable)利用字典推导式{element: None for element in iterable}去重要求元素可哈希。uniq的实现第 49-60 行展示了典型的 Bazel/Skylark 技巧——Skylark 语言没有内置集合类型用 dict 的键唯一性模拟 set 去重且保持首次出现顺序。dicts字典合并lib/dicts.bzl 只导出一个函数dicts.add(*dictionaries)返回包含所有入参字典条目的新 dict同键时参数列表中靠后的字典覆盖靠前的。其 docstring 特别说明设计动机是“支持零个或多个参数”——零参数返回空字典单参数等价于拷贝从而满足算术恒等律让调用方无需为输入数量写特判。这正是 Buck 的 programs/BUCK 加载它来合并字典配置的用途所在。paths文件路径操作功能最重的模块lib/paths.bzl 是本快照中最大的模块约 236 行提供 8 个函数全部针对Unix 风格分隔符——模块头部注释明确声明不支持 Windows 反斜杠路径或盘符函数行为要点basename(p)p.rpartition(/)[-1]取文件部分p以斜杠结尾时返回空串与 Pythonos.path.basename一致而与 Unixbasename命令不同dirname(p)取不含 basename 的前缀并像os.path.dirname一样去掉末尾连续斜杠无前缀时返回分隔符本身is_absolute(path)path.startswith(/)join(path, *others)模拟 POSIX 下os.path.join任一后续组件为绝对路径时丢弃之前所有组件normalize(path)模拟os.path.normpath空路径返回.、删除.段、折叠多余斜杠、处理..段相对路径保留前导..relativize(path, start)返回path相对于祖先路径start的部分因无法访问文件系统若path不在start之下会直接fail报错而非向上回溯replace_extension(p, new_extension)替换扩展名无扩展名时追加新扩展名split_extension(p)拆分为(root, ext)元组恒有root ext pbasename 前导点不计为扩展点故split_extension(.bashrc)返回(.bashrc, )其中normalize第 96-149 行用栈式遍历逐段处理..并保留了“单双前导斜杠原样、三个及以上折叠为一个”的 POSIX 语义relativize第 151-186 行则通过逐段 zip 比较前缀校验“path 必须位于 start 之下”。这些行为与tests/paths_tests.bzl280 行全库最大的测试文件中的用例一一对应。selects支持 OR 键的 select()lib/selects.bzl 提供selects.with_or(input_dict)与selects.with_or_dict(input_dict)with_or是原生select()的替代品键除了普通形式//foo:config1还允许元组形式(//foo:config1, //foo:config2)表示“任一命中即可”最终被展开为多个同值键with_or_dict返回展开后的 dict 本体便于 Skylark 宏检查select()内容原生select()的结果无法直接检查实现上会校验同一 config setting 标签不得在输入中出现多次重复时直接fail(key %s appears multiple times % ...)。sets 与 shell集合与 shell 转义lib/sets.bzl137 行提供以 dict 模拟集合的运算函数对应测试为 tests/sets_tests.bzllib/shell.bzl 导出两个函数shell.quote(s)单引号包裹并将内部单引号替换为\\即标准 POSIX shell 转义法实现仅一行 s.replace(, \\) shell.array_literal(iterable)生成可直接嵌入 shell 脚本的数组字面量例如shell.array_literal([a, b, c])得到(a b c)所有元素一律加引号以保证安全。这两个函数在编写genrule的cmd或sh_binary包装脚本时是拼接安全命令行的常用工具。structs 与 unittest结构转换与规则级单测lib/structs.bzl 的structs.to_dict(s)用dir(s)枚举字段并移除to_json/to_proto两个内置方法后构造 dict用于把 struct 转为可迭代的数据结构lib/unittest.bzl262 行导出两个模块unittest声明与定义单元测试与asserts断言函数。其核心unittest.make(impl)第 24-76 行把实现函数包装成_skylark_testable True且test True的 rule并从str(impl)中解析函数名用于测试输出用法模式为def _your_test(ctx): env unittest.begin(ctx) # 断言语句 unittest.end(env) your_test unittest.make(_your_test)docstring 同时提醒测试规则名必须以_test结尾。该模块还load(:sets.bzl, sets)是库内模块互相依赖的实例。skylark_library聚合.bzl的规则与 ProviderREADME 最后一节介绍skylark_library.bzl规则其完整实现见 skylark_library.bzl。该规则用于“聚合一组 Skylark 文件及其依赖供测试目标和文档生成使用”。规则定义第 46-59 行skylark_library rule( implementation _skylark_library_impl, attrs { srcs: attr.label_list(allow_files [.bzl]), deps: attr.label_list( allow_files [.bzl], providers [[SkylarkLibraryInfo]], ), }, )srcs本目标直接包含的.bzl文件deps依赖的其他skylark_library目标要求提供SkylarkLibraryInfo。实现逻辑第 26-44 行以depset(ctx.files.srcs, order postorder)起步累加各依赖的文件集然后同时返回DefaultInfofiles与runfiles都填充全部文件和自定义 providerSkylarkLibraryInfosrcs与transitive_srcs两个字段。源码注释解释了双重填充的意图全部依赖文件同时出现在files与runfiles中保证skylark_library既可以被其他程序作为data引用也可以出现在genrule()的tools里。docstring 中的标准用法示例第 60-109 行展示了在一个含checkstyle/、lua/子包的工作区里如何为各子包声明skylark_library目标load(buck_bazel_skylib//:skylark_library.bzl, skylark_library) skylark_library( name lua-rules, srcs [lua.bzl, luarocks.bzl], )在 Skylib 自身的 BUILD 中该规则被用于两个目标lib聚合索引入口deps 为全部 8 个模块和skylark_library本身每个模块在 lib/BUILD 中也有各自的skylark_library目标如unittest依赖:sets与源码中的load关系一致。此外两个 BUILD 都声明了testonly True的test_depsfilegroup供规则测试收集文件。测试组织与“编写新模块”流程tests/目录为每个模块配备同名测试文件collections_tests.bzl、paths_tests.bzl、shell_tests.bzl 等全部基于unittest模块编写与模块一一对应——这是 Skylib 对“每个模块必须带单测”约定的直接体现。README 的“Writing a new module”一节给出完整的模块新增流程与库内现有代码完全吻合在lib/下新建.bzl文件在文件内以下划线前缀私有方式定义函数或常量等符号创建导出的模块 struct把公开名映射到私有实现。README 的示例def _manipulate(): ... things struct( manipulate_manipulate, )在lib.bzl中加一行load(bazel_skylib//lib:things.bzl, things)使其可从索引访问注意按前文所述本快照中该索引职责已由根 BUILD 的skylark_library(name lib)目标承担实际维护时应同步更新该deps列表调用方即可load(bazel_skylib//:lib.bzl, things)后things.manipulate()使用在tests/目录为模块补充单元测试。适用前提与使用边界版本边界本库是上游早期快照版权头 2017 年README 明确 API 可能变化例如 README 模块清单未包含structs而lib.bzl索引文件仅有 docstring 无内联 load。引用时以仓库内实际文件为准平台边界paths模块仅支持 Unix 风格路径分隔符见 lib/paths.bzl 模块头注释语言边界全部函数运行于 Bazel 的 Skylark 环境不能直接用于普通 Python 或 Buck 的 Python DSL 中在 Buck 中的角色它是 Buck 仓库 Bazel 迁移规则集tools/build_rules/的外部依赖通过buck_bazel_skylib仓库名加载lib/collections.bzl、lib/dicts.bzl等模块而非 Buck 构建流程本身的组成部分。综上bazel-skylib 以“模块 struct 索引聚合 skylark_library 依赖管理 逐模块单测”的完整范式为在 Buck 这类大型构建系统中编写和维护可复用的 Skylark 规则提供了标准函数基座与工程化模板。赞分享开发工具构建工具【免费下载链接】buckA fast build system that encourages the creation of small, reusable modules over a variety of platforms and languages.项目地址https://gitcode.com/gh_mirrors/bu/buck点击查看免费下载相关推荐bazel_rules_mongo 接入指南在 Bazel 仓库间复用 MongoDB 构建规则的 Bzlmod 模块实践bazel_rules_mongo 接入指南在 Bazel 仓库间复用 MongoDB 构建规则的 Bzlmod 模块实践 导读 bazel_rules_mo数据库文档数据库后端PyTorch 仓库协作规范深度解析AGENTS.md 中的构建、测试、Lint 与 ghstack 实战规则PyTorch 仓库协作规范深度解析AGENTS.md 中的构建、测试、Lint 与 ghstack 实战规则 PyTorch 仓库根目录下的 AGENTS.人工智能机器学习深度学习分布式训练模型编译COSMIC 桌面环境从零到上手10 分钟完整装好并跑通日常使用COSMIC 桌面环境从零到上手10 分钟完整装好并跑通日常使用 你是不是也遇到过这种情况正干着活窗口卡住不动alttab 没反应重启一下桌面还是有操作系统桌面应用上一篇UKB_RAP 项目模块化使用指南下一篇wx-livespy微信视频号直播数据实时捕获与分析引擎创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进