做项目的时候经常撞见 prebuilt 这个词:AOSP 有个巨大的 prebuilts/ 目录,CMake 里有 IMPORTED 库,Android.bp 里有 cc_prebuilt_library_shared。看着都认识,但一直没真正搞明白它想表达什么。
这篇把它讲清楚。
一句话解释
prebuilt = 预先编译好的二进制产物,直接以二进制形式引入项目,而不是在本地从源码编译。
关键对立面是:
- build from source ------ 仓库里放源码,构建时用你自己的编译器、你自己的编译选项编出来
- prebuilt ------ 仓库里(或外部制品库)直接放
.so/.a/.jar/.aar/ 可执行文件 / 工具链,构建时只做链接或拷贝
所以 prebuilt 的本质特征是:
它已经被别人、用别人的编译器、别人的编译选项、针对别人的目标平台编好了,你无法改变。
你项目里绝大多数 prebuilt 相关的坑,都来自这一句。
从一个真实例子看起
先看这句话:
fluoride depends on android prebuilt, llvm compiler may not support some special compilation options
Fluoride 是 AOSP 的蓝牙协议栈(bluedroid 的后继)。它"依赖 android prebuilt",指的是依赖 AOSP 的 prebuilts/ 目录树。这里面主要有两类东西:
1. 预编译的依赖库
libchrome、libbase、protobuf、libcutils 等等。Fluoride 的代码直接 include 它们的头文件、链接它们的二进制。这些库只在 AOSP 构建环境里存在,离开 AOSP 就拿不到。
2. 预编译的工具链本身
prebuilts/clang/host/linux-x86/... ------ AOSP 自带一套定版的 clang。Android.bp 里会写一堆只有这个特定 clang 版本才认的 flag:某些 -Xclang 参数、特定的 sanitizer 选项、-fno-c++-static-destructors 之类。
所以那句话实际在说:Fluoride 这套代码绑死在 AOSP 的 prebuilt 生态上。 如果你换成自己的 LLVM(比如移植到 NuttX / Vela、或者只是用系统 clang 编),这些特殊编译选项它不认,直接编不过。
这是移植 AOSP 组件的典型阻塞点 ------ 不是代码本身有问题,而是它假设了一整套 prebuilt 环境的存在。
prebuilt 出现的其他场景
工具链
arm-none-eabi-gcc 官方发布的压缩包、Android NDK、rustup 下载的 Rust toolchain。没人会为了编一个固件先自己 build 一遍 GCC。
构建系统里的"导入"语法
各家都有对应写法,看到这些就是在引入 prebuilt:
| 构建系统 | 写法 |
|---|---|
| Soong / Android.bp | cc_prebuilt_library_shared、android_app_import、prebuilt_etc |
| CMake | add_library(foo SHARED IMPORTED) |
| Make / NuttX | 直接把 .a 丢进 EXTRA_LIBS |
| Gradle | implementation files('libs/xxx.aar') |
包管理器的二进制分发
Python 的 wheel(相对于 sdist 源码包)、npm 原生模块的 node-pre-gyp 预编译包。pip install numpy 不用等编译半小时,就是因为有现成的 manylinux prebuilt wheel。
闭源 blob
WiFi / BT / GPU 固件、厂商 SDK 里的 .a。
这类是根本没有源码的 prebuilt。它和"有源码但图省事先编好"是两种不同情况 ------ 排查问题时,这个区别决定了你有没有退路。
CI 加速
把编译耗时长、又不常变的部分做成 prebuilt 制品缓存:Docker base image、第三方库、预编译 sysroot。
为什么要关心 prebuilt 还是 from source
| 维度 | prebuilt | from source |
|---|---|---|
| 构建速度 | 快 | 慢 |
| 可调试性 | 差,常无符号 / 无源码 | 好 |
| 改编译选项 | 不行 | 行 |
| 换 ABI / 工具链 | 容易炸 | 重编即可 |
| 换目标架构 | 需要对应架构的 prebuilt | 重编即可 |
| 打安全补丁 | 等上游出新二进制 | 自己打 patch |
嵌入式场景下最常踩的两个坑:
ABI 不匹配。 prebuilt 是用 GCC 编的、你用 clang;或者两边 C++ 标准库版本不同。结果是链接期符号对不上,报一堆 undefined reference,而函数名明明就在那儿(只是 mangling 不一样)。
缺符号。 prebuilt 假设 libc 里有某个函数,但你的目标平台(比如 NuttX)没实现。链接到最后一步才暴雷。
一个容易混的词
precompiled header(PCH / 预编译头) 和 prebuilt 没关系。
PCH 只是把常用头文件预先解析成中间格式,用来加快编译速度,纯粹是编译期优化。中文里两个都带"预编译"三个字,但指的东西完全不同。