ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MongoDB 的 Bazel 安装规则(install_rules)架构解析:从构建产物到可发布安装树

MongoDB 的 Bazel 安装规则(install_rules)架构解析:从构建产物到可发布安装树 MongoDB 的 Bazel 安装规则install_rules架构解析从构建产物到可发布安装树【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本文深入解析 MongoDB 仓库中bazel/install_rules目录实现的安装规则Install Rules体系。它负责把 Bazel 构建出来的二进制、动态库、测试与数据文件编排成一套完整的安装树并提供bazel-bin/install便捷树与 tgz/zip 归档。读完本文你将掌握该规则的分析Analysis、物化Materialization与共享便捷树Shared Convenience Tree三段式架构理解其如何规避并发竞态、处理符号链接与跨平台路径校验并能在自己的BUILD.bazel中正确使用mongo_install宏。一、定位与背景安装规则解决什么问题MongoDB 是一个拥有数千个 Bazel 目标的大型 C 工程。构建完成之后mongod、mongos、mongoshell、wiredtiger 工具等产物散落在bazel-out/配置/bin/...下还带有大量动态库、调试信息.dwp、.dSYM、.pdb、测试二进制及其 runfiles 数据。安装规则的目标是把一个目标的完整产物收敛到一棵结构化的安装树中bin/、lib/等为每个mongo_install_rule目标生成一个安装 action在bazel-bin/install发布一棵便捷树让开发者可以直接在构建根下访问安装结果同时为打包生成 zip/tgz 归档。整个流程可以用 README 中的架构图概括原文见 bazel/install_rules/README.mdinputs and transitive source maps │ ▼ Bazel analysis: normalize destinations, assign owners, reject overlaps │ ▼ declared outputs install depfile │ ▼ one Python install action ┌────┴────┐ ▼ ▼ action-private shared staging tree output tree (outside the lock) │ ▼ lock atomic rename │ ▼ bazel-bin/install这条流水线把每个目标一棵私有安装树与全局共享便捷树两种需求分离分析阶段在 Bazel 内完成冲突检查物化阶段由 Python 脚本 install_rules.py 执行共享树则通过无锁暂存 加锁原子改名的方式并发安全地发布。二、分析阶段所有权映射、冲突拒绝与 depfile分析阶段全部在 Bazel 分析期完成核心实现位于 install_rules.bzl 的mongo_install_rule_implL423-L707。2.1 所有权映射与规范化规则会收集每一个请求的安装目的地并存入install_owners映射。目的地先经过_normalize_install_destinationL254-L278规范化拒绝空路径、包含反斜杠、绝对路径或含:的路径拒绝包含、.、..等非法组件的路径路径不能逃逸安装树不允许../前缀最终返回(normalized, normalized.lower())二元组——比较是大小写不敏感的注释明确说明即使当前执行文件系统大小写敏感也要保守地拒绝仅大小写不同的别名因为安装树和归档会在不同文件系统之间迁移。2.2 三类冲突在 action 启动前被拒绝_declare_install_outputL295-L374在分析期就完成冲突判定避免安装 action 之间发生竞态精确冲突exact conflict同一目的地被不同源占用报install destination collision文件/目录冲突lib/tool与lib/tool/config这类前缀重叠prefix overlap报install destination prefix collision——无论新目标是祖先还是后代重复引用去重相同的源 目的地 类型组合通过多条依赖路径到达时只声明一个输出同一目的地由多个源写入则直接失败。这些行为都有对应的分析期测试固化在 install_rules_test.bzl 中_exact_collision_test期望报错install destination collision_prefix_collision_test期望报错install destination prefix collision_invalid_path_test期望报错invalid install destination而_diamond_deduplication_test验证通过菱形依赖diamond到达的同一产物只声明一个输出。2.3 输出声明与 depfile 格式规则一次性声明完整输出集合declare_file/declare_directory并写出描述安装清单的 depfile。depfile 是一个 JSON 文件包含四类键见 L598-L607{ roots: { /abs/path/file: folder }, includes:{ /abs/path/file: lib/renamed.so }, bins: [ /abs/path/bin1, /abs/path/bin2 ], libs: [ /abs/path/lib1 ] }bins/libs可执行文件与动态库分别落入bin/与lib/roots任意文件 目标目录空目录表示放到安装树根部includes显式重命名的头文件/文件映射到精确目标路径is_renameTrue。从源码结构看文件在分析期就被分类到六个桶中L435-L442binaries、binaries_debug、dynamic_libs、dynamic_libs_debug、root_files、include_files。分类逻辑sort_fileL376-L421按平台判断Linux 上lib*前缀视为库Windows 按.dll/.exe/.pdb/.ps1扩展名判断_WINDOWS_BINARY_EXTENSIONS调试文件按.debug/.dwp、.dSYM、.pdb分类_LINUX_DEBUG_EXTENSIONS、_MACOS_DEBUG_EXTENSIONS、_WINDOWS_DEBUG_EXTENSIONS。2.4 依赖传递与子树不重复发布deps中的子安装目标通过MongoInstallInfoprovider定义于 providers.bzl字段见 L71-L80向上传递src_map、source_files、install_owners等。聚合目标会把子目标的源文件映射扁平化进自己的 depfile但不会消费子安装 action 的输出——即子安装树不会被父目标重新发布。_transitive_source_input_test专门断言父 action 必须声明子 manifest 命名的原始文件但不得把子安装目录或子 depfile 作为输入。分析期还顺带生成install_deps/pkg_name/name依赖文件与installed_tests.txt测试清单L553-L574其中测试路径会被改写成bazel-bin/install/...前缀方便后续在安装树上直接定位测试二进制。三、mongo_install 宏一个目标、三类变体、多种归档普通BUILD.bazel不直接调用mongo_install_rule而是使用mongo_install宏L728-L944。宏为每个name展开三类安装变体L757install-name完整安装包含二进制与调试信息install-name-stripped仅安装 stripped 后的二进制install-name-debug仅安装调试信息。同时生成对应归档目标archive-name_tartgzLinux/macOS、archive-name_zipWindows、可选archive-name_zst启用 zstd 时使用pigz//:bin或zstd//:bin作为压缩器统一由archive-namefilegroup 按平台 select 聚合。规则属性L709-L726包括属性说明srcs要安装的目标列表携带test_binary_aspect收集测试二进制deps其他安装规则目标作为依赖子树root_files{label: 目录}映射把任意文件放到指定目录include_files{label: 精确路径}映射显式重命名安装debug/stripped/debug三态publish_debug_in_strippedstripped 变体是否同时发布调试文件create_dwp是否生成.dwp调试包受//bazel/config:dwp_supported配置控制在仓库根目录的 BUILD.bazel 中可以找到大量真实用法例如L201-L209mongo_install(name mongod, srcs [//src/mongo/db:mongod, ...])L212-L217mongo_install(name mongos, ...)L220-L225mongo_install(name mongo, srcs [//src/mongo/shell:mongo])L227-L235mongo_install(name core, deps [mongod, mongos])演示纯聚合目标L237-L249mongo_install(name devcore, root_files {//x509:generate_main_certificates: bin/x509}, deps [...])演示root_files用法。由此可以推断使用方式为bazel build //:install-mongod构建安装树用bazel build //:archive-mongod产出发布归档。四、物化阶段action 私有输出树与文件发布策略安装 action 由ctx.actions.run发起L661-L680mnemonic 为MongoInstallRule执行 install_rules.py。脚本先清空并重建--install-diraction 私有的输出树然后逐文件写入。4.1 原子写入与硬链接优先每个文件都通过临时路径写入再用os.replace发布避免暴露半写状态_copy_file_atomicallyL274-L292。发布策略在_link_or_copy_fileL295-L324中分级普通文件文件系统允许时使用硬链接os.link(..., follow_symlinksFalse)稳定的 Bazel 输出符号链接_stable_output_symlink_target判定为指向bazel-out中持久文件的链接不解析目标直接硬链接若硬链接跨设备如 Linux 容器中 action 输出与共享树分属不同挂载点则退化为指向持久输出的绝对符号链接而不是把可能数 GiB 的目标复制出来源码树内、相对或沙箱内符号链接直接硬链接会在沙箱被移除后悬空因此改为复制并解引用shutil.copyfilecopymode目录递归复制_copy_directory并用(st_dev, st_ino)身份集合做符号链接环检测L327-L364检测到环时抛Directory symlink cycle错误。值得注意的边界处理Windows 上只读文件源 inode 带只读位必须复制而非硬链接——因为清除一个硬链接的只读位会同时改变 Bazel 源 inode同理清理只读目录时只 chmod 目录本身绝不 chmod 硬链接文件_remove_readonly/_remove_treeL30-L62。test_directory_install_preserves_source_mode与test_directory_install_hardlinks_regular_files等用例在 install_rules_script_test.py 中验证了这些保证。4.2 命令行参数脚本参数L23-L27--depfile 可重复指向安装清单 JSON --install-dir 安装输出目录 --install-mode copy|symlink|hardlink默认 hardlink当前仅 hardlink 实现脚本按install_onceL568-L581以目标路径为键去重同一逻辑目标重复出现包括同一 depfile 传两次只安装一次不同源争抢同一目标则报Install destination ... has multiple sources见test_duplicate_depfiles_install_each_entry_once与test_conflicting_sources_for_one_destination_fail。五、共享便捷树无锁暂存 加锁原子改名共享便捷树是这套设计中最精巧的部分目标是在多 action 并发时既不出半成品树、又尽量少地持有锁。5.1 暂存无锁、发布加锁共享树按配置configuration隔离即bazel-out/配置/bin中的配置名。物化分两步_install_shared_destinationL416-L500在.staging目录创建tempfile.mkdtemp工作区不持有锁完成昂贵的复制/暂存对 dSYM、工具链目录这种大树尤其重要能让无关安装 action 并行推进持有配置级排它锁_exclusive_file_lockUnix 用fcntl.flockWindows 用msvcrt.locking仅执行常数时间的 rename先把旧目标改名到工作区的old再把暂存的new原子改名为目标。锁只覆盖最后的 rename 操作因此并发安装 action 不会互相暴露部分写入的树同时锁竞争时间被压到最低。发布后被顶替的旧目标可能也是 dSYM 大树在锁外异步清理。5.2 中断回滚与权限恢复回滚如果发布期间被异步中断SIGTERM、KeyboardInterrupt 或 IO 错误脚本会检查工作区而非依赖内存标志——若old仍存在则把new挪回暂存位、把old还原为目标路径绝不删除旧目标。若回滚本身也失败则报错并保留工作区作为恢复数据recovery data remains at ...。test_shared_install_restores_previous_destination_when_publish_fails用os.replace注入 EIO/EPERM 完整覆盖了这些路径SIGTERM 转异常_termination_as_exceptionL90-L101把 SIGTERM 转成SystemExit(128signum)让安装事务可以正常走完清理逻辑权限恢复发布 rename 前对共享目录、目标父目录、暂存目录恢复 owner 写/搜索权限_make_directory_writableL65-L72因为 Bazel 可能在本地 action 完成后把输出树目录设为只读。test_shared_install_reopens_readonly_publication_directory专门回归验证这一场景不穿越他人输出目录发布路径在使用前先realpath解析父目录并校验仍在共享根内_destination_withinL534-L546避免便捷树误入另一个 action 的输出目录。5.3 路径校验的完整清单_install_relative_pathL503-L531与 bzl 侧校验共同覆盖目录穿越../、..、绝对路径Windows 反斜杠分隔符、盘符前缀C:/尾部点、尾部空格WindowsWindows 保留 DOS 设备名CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9完整集合见 L29-L52 的_WINDOWS_RESERVED_BASENAMES。test_install_destination_cannot_escape_tree对../escaped、绝对路径、C:/escaped、lib\escaped逐一断言拒绝。六、为什么禁用缓存与远程执行与 wrapper 的配合由于共享便捷树位于 action 声明输出之外MongoInstallRule在execution_requirements中显式声明L672-L679execution_requirements { no-cache: 1, no-remote: 1, }否则该 action 的结果会被缓存或远程执行而便捷树发布不在声明输出内。相应地wrapper 负责选择本地或容器执行策略并把发布路径暴露为可见、可写而不是藏在 action 沙箱里。共享根目录的位置策略install_rules.py L183-L216在 wrapper 侧有对应实现hermetic_container_integration.py 的_linux_shared_install_dir/_host_temp_shared_install_dir等Linux 容器 action通过环境变量MONGO_BAZEL_SHARED_INSTALL_DIR拿到output-base 同级的共享根output-base-mongo-shared-install并以可写方式挂载从而与本地 native 回退动作发布到同一棵树Linux 本地 native action未显式提供该变量时回退到宿主临时目录output-base 名-mongo-shared-install同样避免输出根被保护macOS跨主机安装 action 在环境缺少该变量时使用相同的 host-temp 布局_darwin_shared_install_root每次构建后wrapper 把bazel-bin/install符号链接指向共享根下的对应配置目录_publish_external_convenience_symlink。把共享树放在 Bazel 输出根层级之外可以规避输出目录的权限问题与 finalization 竞态——这正是设计文档反复强调的动机。test_linux_default_shared_install_is_outside_output_tree、test_macos_default_shared_install_is_outside_output_tree与test_shared_install_directory_survives_action_sandbox验证了共享树在沙箱销毁后依然有效、且bazel-bin/install正确指向共享根这一核心行为。七、测试体系分析期与脚本期双层验证安装规则自带两套测试均挂在 BUILD.bazel 的install_rules_test测试套件下分析期测试install_rules_test.bzl基于 skylibanalysistest覆盖精确冲突、前缀冲突、非法路径、菱形依赖去重、单源多目的地bin/multi.exeshare/multi.exe、测试数据 runfiles 安装布局、GDB 工具链按//bazel/config:install_gdb配置开关安装脚本级测试install_rules_script_test.pyPython unittest runpy加载脚本覆盖 Windows 路径分隔符、空目录 root 文件、共享根在输出树外、稳定符号链接硬链接与 EXDEV 降级、目录符号链接解引用、长路径、只读目录重开、重复 depfile 去重、多源冲突、逃逸拒绝、发布回滚、并发 samefile 竞态等 20 场景。这两层测试共同锁定了 README 描述的每一条保证是理解规则行为最可靠的入口。八、小结MongoDB 的安装规则把安装拆成了三个职责清晰的阶段分析期Bazel/Starlark规范化目的地、建立所有权映射、拒绝精确/前缀/文件目录冲突、生成 depfile 与测试清单保证冲突在 action 启动前暴露物化期Python actionaction 私有树 临时文件 os.replace原子发布硬链接优先、符号链接按稳定性分级处理、目录复制带环检测发布期共享便捷树配置隔离、无锁暂存、加锁常数时间 rename、失败回滚、权限恢复并把bazel-bin/install指向共享根同时以no-cache/no-remote与 wrapper 策略确保发布不被缓存或沙箱破坏。对任何希望把 Bazel 构建结果变成可交付安装树的工程而言这套规则在并发安全、跨平台路径语义与符号链接处理上的取舍都值得作为参考实现来阅读。继续深入可从 bazel/install_rules/README.md 出发配合 install_rules.bzl 与 install_rules.py 逐层对照并通过仓库根 BUILD.bazel 中的install-mongod等真实目标验证使用方式。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表