为什么编个 Jetson 镜像,Yocto 会顺手编出 Rust 和 LLVM?
在维护 Jetson 发行版(embedai)时,CI 里最慢的从来不是我的应用,也不是内核,而是两个我"根本没用到"的东西:llvm-native 和 rust-native。
这篇文章记录一次完整的排查:从"日志里突然冒出 LLVM",一路逆推到 Tegra 的启动链。
1. 现象
构建日志里反复出现:
active tasks:
virtual:native:/.../recipes-devtools/clang/llvm_git.bb:do_compile
virtual:native:/.../recipes-devtools/rust/rust_1.96.1.bb:do_install
一个"无 GUI、只跑 AI"的镜像,为什么要编 LLVM 和 Rust 编译器?而且它们动辄数小时。
2. 方法:用依赖图逆查
不要猜,用 bitbake 自己的依赖图:
kas shell kas.yml -c "bitbake -g embedai-image"
# 生成 pn-buildlist(包清单)与 task-depends.dot(任务依赖图)
然后在 task-depends.dot 里反向找"谁依赖它":对每条 "A" -> "B"(A 依赖 B),统计所有指向 llvm-native、rust-native 的边,并剥掉同 recipe 的内部依赖。
结果(本机实测):
llvm-native <- rust-native
rust-native <- python3-cryptography-native, python3-maturin-native
python3-maturin-native <- python3-cryptography-native
python3-cryptography-native <- optee-nvsamples-native
optee-nvsamples-native <- tegra-eks-image
tegra-eks-image <- tegra-bootfiles
链条一路清晰:
tegra-bootfiles (启动固件打包)
→ tegra-eks-image (NVIDIA EKS 加密密钥库镜像)
→ optee-nvsamples-native
→ python3-cryptography-native ← 关键
→ python3-maturin-native / setuptools-rust-native
→ rust-native + cargo-native
→ llvm-native
3. 为什么会这样
python3-cryptography从 42 版起是 Rust 写的。它是一个 Python 包,但内部用 Rust 实现密码学原语,构建时要cargo(maturin/setuptools-rust负责编)。- Rust 编译器的后端就是 LLVM(
rustc借 LLVM 做代码生成)——所以在 Yocto 里编rust-native,会顺带编llvm-native。 - 一句话:为了编一个 Python 的密码学库,构建被迫先编出 Rust 编译器,再编出 LLVM。
4. 根在 Tegra 的启动链
meta-tegra/recipes-security/optee/optee-l4t.inc 里有一行:
DEPENDS = "python3-pyelftools-native python3-cryptography-native"
而 optee-nvsamples-native 继承了这个 inc;它又是 tegra-eks-image(EKS 密钥库镜像)的依赖,EKS 则是启动固件(tegra-bootfiles)的一环。
所以:Tegra 的安全启动基础设施要用 Python 的 cryptography 来签名/处理密钥 → Rust → LLVM。
5. 试过的开关(以及它的边界)
meta-tegra 提供了一个开关,让 OP-TEE 用 NVIDIA 预编译而不是从源码编:
USE_PREBUILT_OPTEE = "1"
效果:tos-optee 换成 tos-prebuilt,optee-os 从依赖图里消失——省掉一个大件。
但Rust/LLVM 仍在:因为 cryptography 走的是 EKS 那条支线(optee-nvsamples-native),不是 optee-os。砍掉一层,还有一层。
6. 顺带辟谣:不是 OpenGL
一度怀疑是发行版带 opengl(oe-core 默认特性)拉了 mesa → clang/llvm。但依赖图证明:mesa 根本不在 Jetson 的构建图里(它属于 qemu 配置那套)。——先查图,别靠猜,这条省了我们一次错误的大改。
7. 结论
- 重型 native(LLVM / Rust / Clang)几乎都是被某个包的依赖或可选特性"顺手"拉进来的,不是核心必需;
- 定位手段:
bitbake -g+ 在task-depends.dot里反向找上游,一层层剥; - 代价是一次性的:sstate 缓存命中后,后续与 CI 都不会再编——这也是"必须把 sstate 攒满"的真正意义。
下次你的 Yocto 构建莫名卡在
llvm-native,别急着怪硬件——先顺着依赖图问一句:是谁把它拉进来的? 答案往往在一个你没想到的角落(这次是:OP-TEE 的密钥库镜像)。
评论 0
还没有评论,来抢沙发~
登录 登录后即可参与评论