news 2026/9/12 11:52:49

Rust 编译器 CI Docker 测试指南:用 citool 与 run.sh 在本地复现 rustc 构建任务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust 编译器 CI Docker 测试指南:用 citool 与 run.sh 在本地复现 rustc 构建任务

Rust 编译器 CI Docker 测试指南:用 citool 与 run.sh 在本地复现 rustc 构建任务

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

rustc 的持续集成(CI)依托于一组精心构造的 Docker 镜像来完成 Linux 平台的构建与测试,本指南以 rustc-dev-guide 的「Testing with Docker」章节为主线,系统讲解如何在本地开发机上复现这些 CI 任务:先介绍一键式工具citool,再深入剖析底层脚本src/ci/docker/run.sh的镜像构建、目录挂载与容器运行机制,最后介绍交互模式,帮助读者在不同于本机的环境里调试、实验 rustc 的完整构建流程。

Docker 在 rustc CI 中的角色

rustc 的 CI 需要在大量 Linux 目标平台(x86_64、i686、ARM、MIPS、s390x 等)上验证编译器的可移植性,而这些目标环境在普通开发机上通常并不存在。为此,仓库在src/ci/docker目录中为 GitHub Actions 上执行的Linux 作业(job)准备了完整的 Docker 镜像定义;非 Linux 作业(如 macOS、Windows)则在 Docker 之外直接运行。

src/ci/docker目录结构如下:

  • host-x86_64/:面向 x86_64 主机的镜像定义,包含dist-*(发行版构建,如dist-x86_64-linux)与test-*(测试,如test-x86_64-gnu)两大类,每类对应一个子目录;
  • host-aarch64/:面向 aarch64 主机的镜像定义;
  • run.sh:构建并运行指定镜像的核心脚本;
  • scripts/:镜像构建过程中被Dockerfile调用的辅助脚本(如sccache.shcross-apt-packages.sh等);
  • static/:构建时复制的静态文件。

这意味着你可以把这些作业搬到本地开发机上运行,用以验证那些与本地系统环境不同的目标平台,例如在 x86_64 机器上构建面向 32 位或其它架构的发行产物。你需要在一台 Linux、Windows 或 macOS 机器上安装 Docker——通常情况下 Linux 会快得多,因为 Windows 和 macOS 需要借助虚拟机来模拟 Linux 环境。

快速上手:用 citool 在本地复现 CI 作业

CI 中的作业通过一组 bash 脚本配置,直接在本地完整复刻它们的行为并不容易。src/ci/citool是一个专门的 Rust 工具(即 rustc 团队的 "CI tooling",详见 src/ci/citool/README.md),它负责在 CI 上根据触发场景(PR、try 作业、合并尝试)计算应执行的作业矩阵,同时提供在本地执行部分 CI 作业的能力。

若希望以最简方式在本地运行某个 CI 作业,可使用其run-local子命令:

cargo run --manifest-path src/ci/citool/Cargo.toml run-local <job-name> # 示例:运行 dist-x86_64-linux-alt 作业 cargo run --manifest-path src/ci/citool/Cargo.toml run-local dist-x86_64-linux-alt

从 citool 入口 可以看到,run-local接受一个必填的作业名参数,以及可选的--type(默认auto,可指定为pr,对应合并尝试作业与 PR 作业)。其内部执行流程为(见 run_workflow_locally 实现):

  1. src/ci/github-actions/jobs.yml加载作业数据库,并按作业名在 Linux 作业中查找匹配项;
  2. 按作业名推断环境变量:作业名以dist-开头且以-alt结尾时设置DEPLOY_ALT=1,仅以dist-开头则设置DEPLOY=1,随后合并作业自身定义的环境变量,用于尽量复刻src/ci/scripts/setup-environment.sh的效果;
  3. 必要时初始化src/llvm-project/子模块;
  4. 最终以作业的镜像名调用src/ci/docker/run.sh完成构建与测试。

如果上述方式在你的环境里不可用,或者你希望对 Docker 镜像的执行有更多控制、想弄清 Docker 作业执行期间究竟发生了什么,可以继续阅读下文,直接使用底层脚本run.sh

核心脚本 run.sh:镜像构建、挂载与运行

src/ci/docker/run.sh负责完成四件事:构建指定的 Docker 镜像、运行容器、在镜像内构建 Rust,随后运行测试或准备面向分发的归档文件。

目录挂载与产物位置

脚本以只读方式挂载你的本地 Rust 源码树,以读写方式挂载一个obj目录;所有编译器构建产物都会写入obj目录。容器内的 shell 起始工作目录是obj,随后执行../src/ci/run.sh,由该脚本按镜像定义启动实际的构建。

从 run.sh 实现 可以看到非 CI 场景下的挂载细节:

  • 源码树挂载为/checkout(默认追加:ro只读标记);
  • obj目录挂载为/checkout/obj
  • 本机~/.cargo挂载为/cargo
  • /tmp/toolstate挂载为容器内同名目录,用于 toolstate 数据交换;
  • 容器工作目录设置为/checkout/obj,并注入SRC=/checkoutCARGO_HOME=/cargo等环境变量。

关于只读挂载,脚本支持通过环境变量READ_ONLY_SRC关闭默认的只读行为:当READ_ONLY_SRC=0时,源码树将以读写方式挂载,从而允许在容器内进行实验性工作或调试(例如拉取并构建子模块),详见 run.sh 中的挂载选项。

obj 目录的本地布局

镜像构建完成后,obj目录在 CI 与非 CI 场景下路径不同(见 run.sh 第 41-46 行):

  • 在 CI 上:objdir=$root_dir/obj
  • 在本地:objdir=$root_dir/obj/$image,即以镜像名隔离,避免不同作业的产物互相干扰。

此外obj目录下还会创建tmpcores子目录,并生成github-summary.md供 CI 汇总步骤摘要使用。

直接运行 run.sh

可以这样直接运行:

src/ci/docker/run.sh <image-name>

关于 run.sh 的几个重要注意事项

原文档给出了以下关键提示,这里逐条结合源码展开:

1. 子模块必须齐全。在 CI 上执行时,脚本假定所有子模块都已检出。如果作业访问的某个子模块缺失,构建会直接报错。因此在本地执行前应确保所需子模块均已检出:既可以通过 git 手动检出,也可以在bootstrap.toml中设置build.submodules = true,然后运行类似x build的命令让 bootstrap 下载最重要的子模块——但请注意,这未必覆盖某个具体 CI 作业所需的全部子模块。

2.<image-name>与作业名的对应关系。镜像名对应src/ci/docker/host-*目录下的某一个子目录。注意镜像名不一定等于作业名:某些作业复用同一个镜像,但通过不同的环境变量或 Docker 构建参数区分。这正是本地复现 CI 作业困难的一部分原因——citool的价值就在于替你把这一层映射关系自动化掉。镜像构建基于 Dockerfile 的校验和:脚本会将镜像名、Dockerfile 内容、COPY涉及的源文件内容、主机架构以及缓存版本号拼接后计算 SHA-512,作为镜像标签的一部分(详见 run.sh 的校验和逻辑)。

3. dist 作业需要DEPLOY=1如果执行的是dist-开头的作业,应设置环境变量DEPLOY=1

4. alternative dist 作业需要DEPLOY_ALT=1如果执行的是以dist-开头且以-alt结尾的作业,应设置DEPLOY_ALT=1

这两个变量在src/ci/run.sh中决定了一系列配置差异:例如DEPLOY/DEPLOY_ALT生效时启用--enable-llvm-static-stdcpp、设置--debuginfo-level、按CODEGEN_BACKENDS(默认llvm)配置 codegen 后端;-alt作业还会额外启用 debug assertions 与 LLVM assertions(见 src/ci/run.sh 第 111-137 行)。反之,非 dist 的测试作业默认同时测试llvm,cranelift两个 codegen 后端,并启用 debug assertions、overflow checks 等(见 src/ci/run.sh 第 138-176 行)。同时citoolrun-local正是依据作业名前缀/后缀自动注入这两个变量的。

5. 部分 std 测试需要 IPv6。某些标准库测试依赖 IPv6 支持,而 Linux 上的 Docker 默认禁用了它。需要在创建容器前执行src/ci/scripts/enable-docker-ipv6.sh开启 IPv6,且只需执行一次。该脚本会向/etc/docker/daemon.json写入{"ipv6":true,"fixed-cidr-v6":"fd9a:8454:6789:13f7::/64"}并重启 Docker 服务(见 enable-docker-ipv6.sh)。

镜像构建与缓存策略

run.sh在镜像获取上做了分层处理(见 run.sh 第 121-192 行):

  • 本地非 CI 执行:优先尝试从ghcr.iorust-ci镜像仓库docker pull预构建镜像(标签为镜像输入校验和),拉取成功则直接打上rust-ci标签使用;拉取失败才回退到本地docker build
  • PR CI 作业:使用 Buildx 的 registry 缓存后端(--cache-from)复用缓存,但不向仓库写入缓存。
  • auto/try 合并作业:先登录镜像仓库,若相同校验和的镜像已存在则直接下载;否则用--cache-from/--cache-to构建并推送,避免镜像内容未变化时重复构建导致缓存抖动。

容器运行使用--init(正确处理僵尸进程回收)与--rm(退出即删除),并默认以--privileged特权模式运行——原因是 LLVM 5.0 升级后 Leak Sanitizer 需要 ptrace 等系统调用,于是对所有构建器统一开启(见 run.sh 第 264-269 行)。

构建加速与容器内用户身份

run.sh会为容器注入 sccache 相关配置:若设置了SCCACHE_BUCKET等变量则透传 AWS/S3 相关环境(PR 构建禁用 S3 认证),否则挂载本机~/.cache/sccache/sccache以复用本地缓存(见 run.sh 第 238-254 行)。同时通过LOCAL_USER_ID环境变量把宿主机用户 ID 传入容器,由src/ci/run.sh在容器内创建同名用户并以该用户身份执行构建,从而保证obj目录中产物的属主与本机用户一致;在使用 rootless Podman 时会改用--userns=keep-id并设置NO_CHANGE_USER=1(见 run.sh 第 303-311 行)。

Docker-in-Docker 场景

如果run.sh本身已经在容器内运行(通过/.dockerenv文件判断),则无法直接使用--volume挂载宿主机目录。此时脚本改用「临时容器 +docker cp」的方式:创建带/checkout卷的临时容器、把源码目录复制进去、用--volumes-from复用该卷,构建结束后再把obj目录从容器拷回宿主机(见 run.sh 第 293-312 行)。

交互模式:在容器里自由实验

有时我们希望先构建出某个特定的 Docker 镜像,再在容器内执行自定义命令,以便观察目标系统环境的具体行为。这时可以使用交互模式:

src/ci/docker/run.sh --dev <image-name>

该模式会在容器内启动一个 bash shell。进入容器后,你可以执行单个命令来完成特定任务,例如只跑 UI 测试:

../x test tests/ui

从 run.sh 实现 看,--dev会追加-it(交互 + TTY)参数,且容器命令不再指向src/ci/run.sh,而是直接进入 bash;若源码树来自 git 检出,还会预先执行git config --global --add safe.directory /checkout,避免容器内出现 git "dubious ownership" 保护报错。

使用交互模式时有几点补充说明:

  • 容器退出即删除,但产物保留:退出 shell 时容器会被自动删除(--rm),不过构建产物仍保留在obj目录中。如果在不同 Docker 镜像间切换,前一个环境留在obj目录里的产物可能干扰构建系统,有时需要在容器内构建前删除obj目录的部分或全部内容。
  • 容器是精简环境:容器只安装了最小化软件包集合,缺少编辑器等常用工具时,可以自行安装,例如apt install less vim
  • 可开多个 shell:在容器内可打开多个 shell。首先获取容器名(shell 提示符中会显示一个短哈希,或在容器外运行docker container ls列出可用容器),然后执行docker exec -it <CONTAINER> /bin/bash,其中<CONTAINER>是类似4ba195e95cef的容器名。

实例剖析:dist-x86_64-linux 作业

以文档示例中的dist-x86_64-linux作业为例,其镜像定义位于 src/ci/docker/host-x86_64/dist-x86_64-linux:

  • Dockerfile 以 CentOS 7(glibc 2.17、kernel 3.2 的最低平台支持基线)为基础,依次编译 GCC 9.5、cmake、LLVM+Clang(目标架构 X86/AMDGPU/NVPTX)、zstd 与 sccache,并通过RUST_CONFIGURE_ARGS注入--enable-sanitizers--enable-profiler--set llvm.thin-lto=true--set rust.lto=thin等构建配置;
  • dist.sh 定义了容器内实际执行的命令:先构建opt-dist(PGO 优化工具),再以x.py dist生成分发产物;非 try 构建还会额外使用 GCC 构建gcc-dev/gcc组件并验证retain属性支持。

这说明镜像名与作业名之间的映射、构建参数的注入,最终都汇聚在run.sh+src/ci/run.sh这一条链路上:前者负责「环境」,后者负责「构建决策」。

小结

在本地复现 rustc CI 的 Docker 作业,路径可以非常平滑:优先使用cargo run --manifest-path src/ci/citool/Cargo.toml run-local <job-name>一键执行;需要精细控制或排查问题时,再直接使用src/ci/docker/run.sh <image-name>--dev交互模式。理解run.sh的镜像缓存、只读挂载、obj目录布局、DEPLOY/DEPLOY_ALT语义以及 IPv6 与子模块前提,是在本地稳定复现 CI 结果的关键;而citooljobs.yml则把作业名到镜像名、环境变量的复杂映射自动化,大大降低了本地复刻 CI 的门槛。

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 11:52:47

基于51单片机的烟雾报警器设计:多传感器融合防误报

简介&#xff1a;基于51单片机的烟雾报警自动设计毕业资料包&#xff0c;面向单片机学习者、电子竞赛备赛学生与毕业设计选题者&#xff0c;也适合课程设计与项目实践。资源以51单片机为控制核心&#xff0c;结合模数转换&#xff0c;配合DS18B20温度传感器与MQ-2烟雾传感器&am…

作者头像 李华
网站建设 2026/9/12 11:52:42

浅谈机惨——从入门到精通

警告 在这里看到的一切机惨方法&#xff0c;全是血淋淋的教训&#xff01;请勿模仿&#xff01; 若模仿&#xff0c;造成的一切后果&#xff0c;包括朋友断交&#xff0c;计算机损坏等&#xff0c;本文章不负任何责任&#xff01; 前言 机惨&#xff0c;全名机房惨案&#…

作者头像 李华
网站建设 2026/9/12 11:50:44

Unity资源管理进阶:YooAsset与Addressable对比及实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 11:49:19

Teable六级权限与技术响应实测:销售型CRM落地关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 11:49:14

glTF/glb:3D内容生态的高效传输与渲染格式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 11:48:17

Python闭包与装饰器:原理、应用与深浅拷贝解析

1. Python闭包的本质与应用场景 闭包是Python中一个既基础又强大的概念&#xff0c;它完美体现了函数式编程的思想。我第一次真正理解闭包是在开发一个缓存系统时&#xff0c;当时需要让内部函数记住外部函数的变量状态&#xff0c;而闭包恰好提供了这种能力。 1.1 闭包的三要…

作者头像 李华