最终结果:编译成功。 libwebrtc_m140.aar(19.7MB),包含 armeabi-v7a + arm64-v8a 两个架构的 libjingle_peerconnection_so.so,已经拿回 Mac,存在 /Users/dev/Documents/AV/webRtc/libwebrtc_m140.aar。
这份记录里有两次踩坑导致的返工(ARM64 虚拟机走不通、CPU 核数配置问题),都记下来了, 免得以后重搭环境再踩一遍。
背景
Mac 编译不了 Android 版 WebRTC------build/config/BUILDCONFIG.gn 里有硬性断言:
gn
if (target_os == "android") {
# Targeting android on Mac is best-effort and not guaranteed to work.
assert(host_os == "linux", "Android builds are only supported on Linux.")
}
官方就是只支持在 Linux 上编译 Android 版本,所以用 UTM 在这台 M4 Mac 上开虚拟机来编。
目标版本 :M140,对应 WebRTC 的 branch-heads/7339 分支(不是 main 上的最新代码)。
目标架构 :armeabi-v7a + arm64-v8a(没有编 x86/x86_64)。
一、大坑:ARM64 虚拟机走不通,必须用 x86_64
一开始图省事,直接在已有的 ARM64 (跟 M4 Mac 同架构,有硬件虚拟化加速,更快)虚拟机上装 Android 编译环境,gclient sync 时卡在:
bash
failed to resolve chromium/third_party/android_toolchain/android_toolchain/linux-arm64
(line 99): no such package: chromium/third_party/android_toolchain/android_toolchain/linux-arm64
用 cipd instances chromium/third_party/android_toolchain/android_toolchain/linux-arm64 直接查证:这个 CIPD 包从来没有发布过 linux-arm64 版本 (对比 linux-amd64 有正常的 历史版本记录)。也就是说 Android 编译需要的 NDK 工具链,官方压根没编译发布 ARM64 Linux 主机版, 不管怎么配置都不可能在 ARM64 Linux 上跑通,不是设置问题,是上游没有这个架构的产物。
结论:必须用 x86_64 的 Linux 虚拟机,即便这意味着在 Apple Silicon 上要用 QEMU 纯软件模拟 (没有硬件加速,会比 ARM64 虚拟机慢),没有别的选择(除非用真正的 x86_64 物理机/云主机)。
二、UTM 里建 x86_64 虚拟机
关键点:第一步必须选 "Emulate",不能选 "Virtualize"。 UTM 的 Virtualize 模式底层是苹果的 Virtualization.framework,在 Apple Silicon 上只能虚拟化 ARM64,选它的话架构选项根本不会 出现 x86_64。只有 Emulate(纯 QEMU 软件模拟)才能选任意架构。
建虚拟机步骤:
- UTM 主窗口
+→ Emulate(不是 Virtualize) - Architecture 选 x86_64
- System/Machine 选 "Intel ICH9 based PC (2009, x86_64)"
- 内存给了 16GB,Storage 直接给了 120GB(一步到位,不用后面再扩容)
- Boot Image 选下载好的 Ubuntu Server ISO(见下一节)
- 名字:
Linux-2(跟之前的 ARM64 虚拟机Linux-one区分开,避免搞混)
下载 Ubuntu Server ISO
官方镜像,校验过 SHA256:
bash
curl -L "https://releases.ubuntu.com/24.04/ubuntu-24.04.4-live-server-amd64.iso" \
-o ~/Downloads/ubuntu-24.04.4-live-server-amd64.iso
装 Server 版(没有桌面,纯命令行)------这台机器唯一用途是编译,不需要图形界面,更轻量。
装系统时踩的坑
- 分区默认只给根分区分一半空间 (LVM,
ubuntu-lv只占PFree的一半,另一半留白不分配), 跟磁盘大小无关,是 Ubuntu 安装器的默认行为,装完后需要手动lvextend(见下文命令)。 - 装完重启后又跑回安装程序 :因为虚拟光驱里的 ISO 没弹出,虚拟机启动顺序里光驱排在硬盘前面, 一直循环进安装界面。装到出现 "Installation complete!" + "Reboot Now" 提示时, 先在 UTM 里把虚拟光驱的 ISO 弹出(Eject),再点 Reboot Now,不要顺序颠倒。
- 弹出 ISO 之后,安装程序会有一句
[FAILED] Failed unmounting cdrom.mount+Please remove the installation medium, then press ENTER------这是正常提示,不是报错,直接按 ENTER 继续。
三、装 SSH,后续全部远程操作
Ubuntu Server 是纯命令行,而且没有桌面剪贴板同步,在 UTM 控制台窗口里粘贴长命令基本用不了。 装好 SSH 之后改用 Mac 终端连接,复制粘贴、多任务都正常:
bash
sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh
ip addr show | grep "inet " # 找到虚拟机 IP,记下来
免密登录(把 Mac 的公钥塞进虚拟机的 authorized_keys):
bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "<Mac 的 ssh 公钥内容>" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
免密 sudo(不然每次远程跑 sudo 命令都要交互输密码,脚本化操作跑不通):
bash
echo "dev ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/dev-nopasswd
sudo chmod 440 /etc/sudoers.d/dev-nopasswd
之后全部通过 ssh dev@<虚拟机IP> "命令" 远程操作,不用再碰 UTM 窗口。
共享目录这次没用上 :虚拟机建的时候配了 Shared Directory 指向 Mac 的
~/Documents, 但 Ubuntu Server 版默认不会自动挂载 VirtioFS 共享目录 (桌面版会),编译过程中源码目录 建在了~/Documents/webrtc_android,其实只是虚拟机本地磁盘上一个同名目录,跟 Mac 完全没关系 ------编完之后是用scp把.aar文件拷回 Mac 的,不是靠共享目录。如果想真正用上共享目录, 需要额外手动挂载 virtiofs,这次图省事没折腾,直接scp更快。
四、磁盘扩容(LVM)
bash
lsblk
sudo vgs
sudo lvs
# VG 里有空闲空间(PFree 不是 0)时,直接分给根逻辑卷
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv
df -h
结果:根分区从默认的 58G 扩到了 115G(约 103G 可用)。
五、Ubuntu 基础环境 + depot_tools
bash
sudo apt update
sudo apt install -y git python3 python3-pip curl lsb-release
cd ~
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
echo 'export PATH="$HOME/depot_tools:$PATH"' >> ~/.bashrc
export PATH="$HOME/depot_tools:$PATH"
gclient --version # 第一次跑会自动引导下载 ninja/gn/cipd 的 Linux 原生版本,静默等一会儿
⚠️ 不能把 Mac 上的
depot_tools直接复制过来用 ------里面ninja/gn/cipd是编译好的 二进制,Mac 版是 Mach-O 格式,Linux 里执行会报Exec format error。必须在 Ubuntu 里 重新git clone,让引导脚本自己下载 Linux 原生版本。
⚠️ 改了~/.bashrc不会立刻生效 :已经打开的终端/SSH 会话不会自动重新加载,需要source ~/.bashrc或者开一个新会话才能用gclient命令。
六、拉代码 + 切到 M140(branch-heads/7339)分支
bash
mkdir -p ~/Documents/webrtc_android # 实际路径,跟共享目录同名但没关系(见上文说明)
cd ~/Documents/webrtc_android
fetch --nohooks webrtc_android
cd src
git fetch origin +refs/branch-heads/7339:refs/remotes/branch-heads/7339
git checkout -b m140 refs/remotes/branch-heads/7339
# 验证切对了分支(提交信息里应该带 [M140] 字样)
git log -1 --format='%H %s'
# 按这个分支的 DEPS 重新同步依赖
cd ..
gclient sync -D
⚠️
fetch报错 "already contain a checkout" :如果之前跑过一次fetch但没跑完就 中断了,会留下一个.gclient配置文件但没有实际代码,这时候fetch会拒绝执行(它只用于 全新 checkout)。检查.gclient是否已存在,如果存在就直接跳过fetch,运行gclient sync代替(会读现有配置去真正拉代码)。⚠️ 别手滑同时跑两个
gclient sync:并发操作同一个目录会导致其中一个进程创建的src/.git是残缺状态(git rev-parse HEAD报fatal: Needed a single revision), 表现为gclient sync报unmanaged solution; skipping src。解决:确认只有一个gclient进程在跑,把残缺的src目录删掉重新sync一次。
实测数据:
- 第一次(main 分支)
gclient sync:约 37GB,其中拉测试素材那个 hook 因为网络问题 单独跑了 2.6 小时 (download_from_google_storage,GCS 资源下载偶发慢,跟 mac 那次 遇到的情况类似)。 - 切到 M140 分支后重新
gclient sync:因为大部分依赖已经在本地,速度快很多,资源那个 hook 这次只用了 139 秒。同步完磁盘占用降到 32GB(M140 这个发布分支比 main 分支依赖更精简)。
七、装 Linux 编译依赖
bash
cd ~/Documents/webrtc_android/src
sudo build/install-build-deps.sh
官方脚本,自动装齐一大堆系统包(包括好几个跨架构的 binutils),比手动一个个装靠谱。
八、编译打包 AAR
bash
cd ~/Documents/webrtc_android/src
python3 tools_webrtc/android/build_aar.py \
--arch armeabi-v7a arm64-v8a \
--output ~/Documents/webrtc_android/libwebrtc_m140.aar
大坑:虚拟机 CPU 核数默认只有 1 个
建虚拟机时 "CPU Cores" 选项留了 "Default" ,结果 QEMU 实际只分了 1 个核心 (nproc 输出 1)。ninja/siso 这类构建系统的核心优势就是多核并行编译,只有 1 核约等于 完全串行,严重拖慢编译速度。
教训:建虚拟机时一定要显式指定 CPU 核数,不要留 Default。
好在 ninja 是增量式的,已经编译好的 .o 文件不会因为改核数重新编译,如果发现核数不够, 关机改完核数重开、重跑同一条命令即可继续,不会浪费已有进度。这次实际是保持单核跑完的, 两个架构总共耗时约 6 小时 (build finished 日志里能看到 5h58m50.93s)。
九、结果
-
产物路径(虚拟机内):
~/Documents/webrtc_android/libwebrtc_m140.aar,19,677,215 字节(约 19.7MB) -
用
unzip -l校验过两个架构的.so都在:bashjni/armeabi-v7a/libjingle_peerconnection_so.so 6,731,180 bytes jni/arm64-v8a/libjingle_peerconnection_so.so 11,973,360 bytes -
因为共享目录没挂载上,用
scp直接拷回 Mac:bashscp dev@<虚拟机IP>:~/Documents/webrtc_android/libwebrtc_m140.aar \ /Users/dev/Documents/AV/webRtc/libwebrtc_m140.aar -
最终文件:
/Users/dev/Documents/AV/webRtc/libwebrtc_m140.aar
结论:M140(branch-heads/7339)的 WebRTC Android AAR,在 x86_64 Ubuntu 虚拟机(UTM Emulate 模式,QEMU 软件模拟,无硬件加速)上编译成功,armeabi-v7a + arm64-v8a 两个架构都验证过产物存在。 ARM64 虚拟机这条路完全走不通(上游没有对应架构的 NDK 工具链包),CPU 核数配置是影响编译速度的 关键因素,下次建虚拟机时应该一开始就分配足够核数。