记录 2026-04-23 新落地的「Mac 本地写代码 + 虚拟机编译」方案: - 9.1 背景:虚拟机内装 Claude CLI 因 Anthropic 对机场 IP 的 API 级风控不可行 - 9.2 Mac 端环境:case-sensitive APFS 卷(避免 Linux 内核大小写冲突)+ HTTPS+PAT+Keychain - 9.3 虚拟机端延续 SSH ed25519 over 443 - 9.4 Claude 自动化闭环:Mac 改 → push → SSH 虚拟机编译 → 看错误自修 → 循环 - 9.5 分支策略:main / dev/mac / feature/* - 9.6 项目级 CLAUDE.md 机制(新对话零摩擦接手) - 9.7 全部踩坑:ClashX SSH 劫持、7890 代理端口不实际转发、APFS 大小写、mihomo 绕不过 Anthropic 风控等 - 9.8 实测性能数据
71 KiB
Tailscale + SSH 远程开发操作指南
文档概述
Mac 电脑通过 Tailscale 跨网络 SSH 连接 Windows 电脑上的 Linux 虚拟机(VMware),实现远程 Android 底层开发。
当前环境信息(2026-04-17 已验证)
| 项目 | 信息 |
|---|---|
| Mac 电脑 | macOS, ClashX Pro (VPN), Tailscale |
| Windows 电脑 | VMware Workstation 17, Clash for Windows v0.19.10 |
| Linux 虚拟机 | Ubuntu 20.04 LTS, VMware 桥接模式 |
| 虚拟机用户 | zhangwenqi |
| Tailscale IP | 100.123.82.91 |
| 虚拟机局域网 IP | 192.168.6.60 |
| SSH 密钥 | Mac ~/.ssh/id_ed25519 已部署到虚拟机 |
| Docker | 28.1.1 |
| Radxa CM5 环境 | Docker 镜像 android-builder:12.x (6.48GB, Ubuntu 20.04 基底),SDK 已编译验证 |
| OrangePi CM5 环境 | Docker 镜像 android-builder-orangepi:13.x (待构建, Ubuntu 22.04 基底),SDK 已合并待解压 |
| 磁盘 | 700GB 虚拟磁盘,剩余空间足够双环境并行 |
第一部分:网络架构
1.1 整体架构
Mac (ClashX Pro VPN + Tailscale)
|
| Tailscale P2P (100.x.x.x)
|
Linux 虚拟机 (Tailscale, IP: 100.123.82.91)
|
| VMware 桥接 (192.168.6.60)
|
Windows (Clash for Windows, 端口 7890)
1.2 各设备职责
- Mac: 开发主机,VPN 翻墙 + SSH 远程开发
- Windows: 宿主机,运行 VMware 虚拟机 + Clash 代理
- Linux 虚拟机: 编译服务器,Docker + Android SDK 编译环境
第二部分:ClashX Pro 与 Tailscale 共存配置(关键)
Mac 上 ClashX Pro(增强模式/TUN)与 Tailscale 存在两个冲突,必须同时解决。
2.1 问题一:增强模式流量劫持
ClashX Pro 增强模式创建 TUN 虚拟网卡拦截所有流量,Tailscale 的 WireGuard UDP 流量也被劫持,导致 Tailscale 连不上。
解决方法: 在 ClashX Pro 代理配置文件的 rules 最前面添加直连规则。
配置文件路径: ~/.config/clash/你的订阅配置.yaml
在 rules: 下方、第一条规则之前插入:
rules:
- PROCESS-NAME,tailscaled,DIRECT
- IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
- IP-CIDR6,fd7a:115c:a1e0::/48,DIRECT,no-resolve
- DOMAIN-SUFFIX,tailscale.com,DIRECT
- DOMAIN-SUFFIX,login.tailscale.com,DIRECT
# ... 原有规则保持不变 ...
各规则作用:
PROCESS-NAME,tailscaled,DIRECT→ Tailscale 进程所有流量直连(最关键)IP-CIDR,100.64.0.0/10,DIRECT→ Tailscale 内网 IP 段直连IP-CIDR6,fd7a:115c:a1e0::/48,DIRECT→ Tailscale IPv6 段直连DOMAIN-SUFFIX,tailscale.com,DIRECT→ Tailscale 控制服务器直连DOMAIN-SUFFIX,login.tailscale.com,DIRECT→ Tailscale 认证直连
2.2 问题二:MagicDNS 劫持域名解析
Tailscale 默认开启 MagicDNS,将系统 DNS 改为 100.100.100.100,与 ClashX Pro 增强模式的 fake-ip DNS(198.18.0.2)冲突。
典型表现:浏览器能访问 YouTube,但 Claude Code 等终端应用无法连接。
解决方法: 关闭 Tailscale DNS 接管。
操作步骤:
- 点击 Mac 菜单栏 Tailscale 图标
- 进入 Preferences(偏好设置)
- 找到 "Use Tailscale DNS Settings" → 关闭
或通过管理后台:浏览器打开 https://login.tailscale.com/admin/dns → 关闭 MagicDNS
2.3 订阅更新覆盖问题
ClashX Pro 订阅更新会覆盖配置文件,Tailscale 直连规则会丢失。
应对: 每次订阅更新后,重新在 rules: 下插入 2.1 中的 5 条规则,然后 ClashX Pro 菜单 → 配置 → 重新加载配置。
备份规则片段方便粘贴:
- PROCESS-NAME,tailscaled,DIRECT
- IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
- IP-CIDR6,fd7a:115c:a1e0::/48,DIRECT,no-resolve
- DOMAIN-SUFFIX,tailscale.com,DIRECT
- DOMAIN-SUFFIX,login.tailscale.com,DIRECT
2.4 验证清单
- 开启 ClashX Pro 增强模式 + 选择代理节点
- 开启 Tailscale
- 浏览器访问 YouTube → VPN 正常
- 终端
ssh zhangwenqi@100.123.82.91→ Tailscale SSH 正常 - Claude Code 对话 → DNS 解析正常
失败排查:
- 第 3 步失败 → 检查代理节点
- 第 4 步失败 → 检查 rules 中 Tailscale 直连规则
- 第 5 步失败 → 检查 Tailscale DNS Settings 是否已关闭
第三部分:Linux 虚拟机环境配置
3.1 Tailscale 安装
# 安装
curl -fsSL https://tailscale.com/install.sh | sh
# 启动并认证(浏览器打开输出的链接登录)
sudo tailscale up
# 查看分配的 IP
tailscale ip -4
3.2 SSH 服务
# 安装(如未安装)
sudo apt update && sudo apt install openssh-server -y
# 启动
sudo systemctl enable --now ssh
# 检查状态
sudo systemctl status ssh
3.3 Docker 安装
# 添加 Docker 清华镜像源
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu focal stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 当前用户加入 docker 组(免 sudo)
sudo usermod -aG docker $USER
# 需要重新登录生效
3.4 Docker 镜像加速(国内必须配置)
Docker Hub 在国内无法直接访问,需要配置镜像加速器:
sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me",
"https://docker.m.daocloud.io"
]
}
EOF
sudo systemctl daemon-reload && sudo systemctl restart docker
注意:镜像加速器地址可能失效,届时搜索"Docker 镜像加速 2026"获取最新可用地址。
3.5 构建 Android 12 编译镜像
Dockerfile 位于虚拟机 ~/Radxa_CM5/docker/Dockerfile,内容基于 Radxa 官方文档,使用清华镜像源。
cd ~/Radxa_CM5/docker
sudo docker build -t android-builder:12.x .
构建完成验证:
sudo docker run --rm android-builder:12.x bash -c \
'java -version 2>&1; python3 --version; gcc --version | head -1; which repo'
预期输出:
- Java: openjdk 1.8.0
- Python: 3.8.x
- GCC: 9.4.0
- Repo: /usr/local/bin/repo
与官方 Dockerfile 的差异(重要)
当前镜像缺少官方 Dockerfile 中的 5 套 Linaro 交叉编译工具链 COPY:
gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf → /opt/ 和 /opt/toolchains/
gcc-linaro-aarch64-none-elf-4.8-2013.11_linux → /opt/ 和 /opt/toolchains/
gcc-arm-none-eabi-6-2017-q2-update → /opt/toolchains/
gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu → /opt/toolchains/
gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf → /opt/toolchains/
原因: 这些工具链需要单独下载(官方文档未提供下载地址),放到 Dockerfile 同目录才能 COPY。
影响评估:
- Radxa Android SDK 的
prebuilts/目录自带编译所需工具链 - 编译脚本
build.sh优先使用 SDK 内的工具链 - 这些 Linaro 工具链主要给 U-Boot 和 kernel 单独编译用
编译时如果报错找不到工具链,按以下方式排查修复:
- 确认报错中缺少的具体工具链名称
- 搜索 Linaro 官网下载对应版本:https://releases.linaro.org/components/toolchain/binaries/
- 解压到 Docker 容器的
/opt/toolchains/目录:# 在容器内 cd /opt/toolchains wget <工具链下载地址> tar -xf <工具链压缩包> - 或者将工具链放到
~/Radxa_CM5/docker/目录,取消 Dockerfile 中 COPY 行注释后重新构建镜像
3.6 Radxa CM5 Android 12 编译环境(基于 Ubuntu 20.04)
3.6.1 解压 SDK
cd ~/Radxa_CM5/SDK_Android
tar -xzf radxa_android_sdk_backup.tar.gz
# 解压后目录为 radxa-android-sdk/,耗时视磁盘速度而定
3.6.2 编译前必须修复的问题(重要)
SDK 存在两个已知问题,必须在编译前修复,否则编译会失败。
问题一:GMS 引用不存在(lunch 阶段报错)
错误信息:vendor/partner_gms/products/gms.mk does not exist
原因:RadxaCM5.mk 引用了 Google GMS 包(Google Play 服务),但开源 SDK 不包含 GMS(需 Google 授权)。
修复:注释掉 GMS 引用行。
cd ~/Radxa_CM5/SDK_Android/radxa-android-sdk
sed -i 's/$(call inherit-product, vendor\/partner_gms\/products\/gms.mk)/#$(call inherit-product, vendor\/partner_gms\/products\/gms.mk)/' device/rockchip/rk3588/RadxaCM5/RadxaCM5.mk
验证:
grep "gms" device/rockchip/rk3588/RadxaCM5/RadxaCM5.mk
# 应看到该行前面有 # 注释符
注意:注释 GMS 后编译出的系统不含 Google Play 商店和 Google 服务,其他功能不受影响。
问题二:缺少 RadxaCM5.config 内核配置文件(kernel 编译阶段报错)
错误信息:No configuration exists for this target on this architecture - RadxaCM5.config
原因:BoardConfig.mk 指定 PRODUCT_KERNEL_CONFIG := rockchip_defconfig android-11.config RadxaCM5.config,但 kernel-5.10/kernel/configs/ 目录下没有 RadxaCM5.config 文件。
修复:参考 rock5b.config 创建 CM5 内核配置文件。
cat > kernel-5.10/kernel/configs/RadxaCM5.config << 'EOF'
CONFIG_SND_SOC_ES8316=y
CONFIG_RTL8852BE=m
CONFIG_BT_RTKBTUSB=y
CONFIG_SENSORS_PWM_FAN=y
CONFIG_TOUCHSCREEN_GT9XX=y
CONFIG_TOUCHSCREEN_FTS=y
CONFIG_TOUCHSCREEN_FTS_RADXA=y
CONFIG_DRM_PANEL_RADXA=y
CONFIG_DRM_PANEL_RASPBERRYPI_TC358762=y
CONFIG_ROCKPI_MCU=y
CONFIG_VIDEO_IMX219=y
EOF
3.6.3 编译前增大 Swap(防止 OOM,重要)
Android 全量编译内存峰值可超过 20GB,虚拟机默认 Swap 仅 1GB,编译到 99% 时会因内存不足被 OOM Killer 杀死进程。
# 创建 8GB Swap 文件
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 写入 fstab 永久生效
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 验证(应显示 9GB 左右 Swap)
free -h | grep -i swap
同时建议 VMware 虚拟机内存分配 16GB 以上(20GB+ 更佳)。
3.6.4 进入 Docker 编译环境
# 注意挂载的是解压后的 radxa-android-sdk 目录
sudo docker run -it \
--name android-build \
-v ~/Radxa_CM5/SDK_Android/radxa-android-sdk:/workspace \
-w /workspace \
android-builder:12.x \
bash
3.6.5 执行编译
# 在 Docker 容器内执行
source build/envsetup.sh
lunch RadxaCM5-userdebug
# 方法一:全量编译(推荐,约 5 小时)
# 如果虚拟机内存 ≤ 20GB,建议用 -j8 限制线程数减少内存峰值
./build.sh -AUCKup
# 方法二:如果 build.sh 因 OOM 中途失败,用 make 继续(会复用已编译缓存)
make -j8
编译参数说明:-A AOSP、-U U-Boot、-C Config、-K Kernel、-u update.img、-p 打包
编译过程中 Build sandboxing disabled due to nsjail error 是正常提示,Docker 容器内不支持 nsjail 沙箱,不影响编译。
3.6.6 编译后打包镜像
如果使用 make -j8 单独编译 AOSP(非 build.sh),需要手动打包:
# 在容器内执行
./mkimage.sh
3.6.7 验证编译结果
ls -lh rockdev/Image-RadxaCM5/
预期镜像文件:
| 文件 | 说明 |
|---|---|
| MiniLoaderAll.bin | Bootloader 加载器 |
| idbloader.img | IDB Loader |
| uboot.img | U-Boot |
| boot.img | Boot 镜像(内核+ramdisk) |
| recovery.img | Recovery 模式镜像 |
| super.img | 系统分区(system+vendor+odm,约 1.6GB) |
| dtbo.img | 设备树 Overlay |
| resource.img | 内核资源 |
3.6.8 退出和重新进入容器
# 退出容器
exit
# 重新进入已创建的容器
sudo docker start -ai android-build
3.6.9 烧录到开发板
编译完成的镜像在虚拟机 ~/Radxa_CM5/SDK_Android/radxa-android-sdk/rockdev/Image-RadxaCM5/ 目录。
拿到 CM5 开发板后:
- 将镜像文件拷贝到 Windows
- 使用 RKDevTool(瑞芯微官方烧录工具)烧录
- 开发板进入 Loader 模式(按住 Recovery 按键上电)
- 在 RKDevTool 中加载各 img 文件烧录
3.7 OrangePi CM5 Android 13 编译环境(基于 Ubuntu 22.04 Docker)
OrangePi CM5 Base(RK3588S)使用 Android 13 源码,与 Radxa CM5 的 Android 12 并行存在。 为避免污染主机环境,使用独立的 Docker 镜像(基于 Ubuntu 22.04,对齐 OrangePi 官方推荐环境)。
3.7.1 官方参考文档
- 《OrangePi_CM5_Base_RK3588S_用户手册_v1.3.pdf》第 7 章:Android 13 源码的编译方法
- 《OrangePi_CM5_Base_Tablet_RK3588S_用户手册_v1.0.pdf》
3.7.2 SDK 文件准备
OrangePi Android 13 SDK 来自官方百度云/谷歌网盘,是 9 个分卷压缩包:
Android_13.tar.gz00 (4.0GB)
Android_13.tar.gz01 (4.0GB)
...
Android_13.tar.gz08 (1.9GB)
md5sum (校验文件)
存放路径:~/OrangePi_CM5/Aandroid_OrangePi/
3.7.3 MD5 校验
cd ~/OrangePi_CM5/Aandroid_OrangePi
md5sum -c md5sum
# 9 个文件全部显示"成功/确定"才能继续
3.7.4 合并压缩包(重要)
经实测:官方文档的管道流式解压(cat ... | tar -xzf -)会在某个分卷边界出错
(已解压 46GB 后报 gzip: invalid compressed data--format violated)。
推荐做法:先合并成单个文件,再解压。
cd ~/OrangePi_CM5/Aandroid_OrangePi
# 显式指定顺序合并(避免通配符顺序问题)
cat Android_13.tar.gz00 Android_13.tar.gz01 Android_13.tar.gz02 \
Android_13.tar.gz03 Android_13.tar.gz04 Android_13.tar.gz05 \
Android_13.tar.gz06 Android_13.tar.gz07 Android_13.tar.gz08 \
> Android_13.tar.gz
# 合并后约 34GB,耗时约 15 分钟(SSD 速度约 37MB/s)
ls -lh Android_13.tar.gz
3.7.5 解压源码
# 解压到当前目录,产生 Android_13/ 目录
tar -xzf Android_13.tar.gz
# 解压后预计 80-100GB,耗时约 20-30 分钟
du -sh Android_13/
解压成功后可以删除合并文件和分卷文件节省空间:
# 可选:保留分卷和 md5sum 作为备份源,删除合并文件
rm Android_13.tar.gz
# 或:已验证解压成功后,彻底清理(需确认 Android_13/ 完整)
# rm Android_13.tar.gz Android_13.tar.gz0*
3.7.6 创建 Dockerfile(含所有踩坑补丁,实测可用版)
Dockerfile 位于虚拟机 ~/OrangePi_CM5/docker/Dockerfile,基于 Ubuntu 22.04。
官方文档 7.2 的依赖列表不完整,本版本已加入所有实际踩坑后补齐的包。
# OrangePi CM5 Android 13 编译环境
# 基础镜像:Ubuntu 22.04(符合 OrangePi 官方推荐环境)
FROM ubuntu:22.04
# 清华镜像源
RUN sed -i 's@archive.ubuntu.com@mirrors.tuna.tsinghua.edu.cn@g; \
s@security.ubuntu.com@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 启用 i386 多架构(Android 编译需要 32 位兼容库)
RUN dpkg --add-architecture i386
# 官方文档 7.2 要求的编译依赖
RUN apt-get update -y && apt-get install -y \
git gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip \
&& apt-get install -y u-boot-tools
# Android 13 需要 OpenJDK 11
RUN apt-get install -y openjdk-11-jdk
# 【踩坑 1】Ubuntu 22.04 默认没有 libncurses.so.5
# 但 Android 预编译 Clang 工具链依赖它,不装会导致编译到 40+ 分钟时报错
RUN apt-get install -y libncurses5 libncurses5:i386
# 【踩坑 2】Ubuntu 22.04 编译 Android 可能需要的预防性包
RUN apt-get install -y \
libssl-dev pkg-config m4 imagemagick \
python-is-python3 python3-protobuf \
protobuf-compiler libprotobuf-dev \
libncurses-dev
# 【踩坑 3】Rockchip U-Boot 的 make.sh 硬依赖 Python 2
# 不装会导致 MiniLoaderAll 合成阶段报 "ERROR: No python2"
RUN apt-get install -y python2 python2-dev python2.7
# 辅助工具
RUN apt-get install -y python3 python3-pip rsync cpio bc kmod \
device-tree-compiler liblz4-tool lzop sudo vim nano \
fontconfig schedtool
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
CMD ["/bin/bash"]
3.7.7 构建 Docker 镜像
cd ~/OrangePi_CM5/docker
sudo docker build -t android-builder-orangepi:13.x .
# 耗时约 5-10 分钟
验证镜像:
sudo docker images android-builder-orangepi
# 预期:TAG=13.x, SIZE 约 2GB
3.7.8 进入 Docker 编译环境
sudo docker run -it \
--name orangepi-build \
-v ~/OrangePi_CM5/Aandroid_OrangePi/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
bash
3.7.9 执行编译
按官方文档 7.2 节编译参数:
| 参数 | 作用 |
|---|---|
-B |
编译 U-Boot |
-K |
编译 Kernel |
-a |
编译 Android |
-F |
编译 U-Boot + Kernel + Android(三合一) |
-M |
在 rockdev 目录生成分区镜像 |
-u |
打包生成最终 update.img |
-b |
指定开发板型号 |
--gapps |
包含 Google Play 服务(GApps) |
推荐全量编译命令:
# 容器内或后台 docker run -d 执行
./make.sh -FMu -b orangepicm5 --gapps
实测耗时:6 小时 11 分钟(VMware 虚拟机 8 核 20GB RAM + 9GB Swap)。
推荐后台执行(避免 SSH 断开中断):
sudo docker run -d --name orangepi-build \
-v /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
bash -c 'echo 开始: $(date) > /tmp/build.log && \
./make.sh -FMu -b orangepicm5 --gapps >> /tmp/build.log 2>&1; \
echo 结束: $(date) exit=$? >> /tmp/build.log'
# 查看进度
sudo docker exec orangepi-build tail -20 /tmp/build.log
sudo docker exec orangepi-build du -sh /workspace/out/
3.7.10 编译后的 U-Boot 修复(实测必需)
-F 参数理论上会编译 U-Boot,但实际只产出 u-boot.img 等基础文件,
不会自动调用 Rockchip 脚本合成 MiniLoaderAll.bin 和 trust.img。
如果 make.sh -FMu 最后日志中出现:
u-boot/trust.img not fount! Please make it from u-boot first!
u-boot/*MiniLoaderAll_*.bin not fount! Please make it from u-boot first!
Error:<AddFile> open file failed,err=2!
Make firmware FAILED
需要手动执行两步修复:
修复步骤 1:合成 Rockchip U-Boot(生成 MiniLoaderAll + uboot.img 新版)
# 启动临时容器执行 u-boot/make.sh rk3588
sudo docker run --rm \
-v /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace/u-boot \
android-builder-orangepi:13.x \
./make.sh rk3588
成功标志:
Image(no-signed, version=0): uboot.img (FIT with uboot, trust...) is ready
Image(no-signed): rk3588_spl_loader_v1.13.xxx.bin (with spl, ddr...) is ready
修复步骤 2:创建 trust.img 符号文件(新版 Rockchip 已将 trust 合并到 uboot.img)
# 在虚拟机宿主机执行(非容器内)
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/
sudo cp -a uboot.img trust.img
修复步骤 3:重新打包(跳过编译,只做 mkimage + update.img)
sudo docker run --rm \
-v /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
./make.sh -Mu -b orangepicm5 --gapps
-Mu 只做"生成分区镜像 + 打包 update.img",不重新编译 Android,耗时约 5-10 分钟。
3.7.11 验证编译结果
成功后在 rockdev/Image-rk3588s_t/ 目录生成所有镜像:
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/
预期产物(实测):
| 文件 | 大小 | 分区/作用 |
|---|---|---|
| update.img | 2.3GB | eMMC/TF 卡完整固件(首要烧录目标) |
| update_spi_nvme.img | 2.3GB | SPI Flash + NVMe SSD 组合烧录 |
| MiniLoaderAll.bin | 465KB | bootloader 加载器(MaskROM 引导) |
| uboot.img | 4.0MB | U-Boot(已内含 trust) |
| trust.img | 4.0MB | Trust 镜像(副本,兼容脚本) |
| boot.img | 37MB | 内核 + ramdisk(boot 分区) |
| recovery.img | 47MB | Recovery 模式镜像 |
| super.img | 2.2GB | 超级分区(含 system+vendor+odm+product) |
| dtbo.img | 443B | 设备树 Overlay |
| resource.img | 2.5MB | 内核资源(开机动画、字体等) |
| vbmeta.img | 4KB | 验证启动元数据 |
| misc.img | 48KB | 其他信息分区 |
| baseparameter.img | 1MB | 显示屏参数 |
| parameter.txt | - | 分区表定义 |
3.7.12 烧录到 OrangePi CM5 开发板
首次烧录:只需 update.img
update.img 是完整的 Android 13 系统固件,包含所有分区镜像。
烧录 update.img 会一次性写入所有分区,等于给开发板全新刷系统。
烧录步骤:
-
从虚拟机拷贝
update.img(2.3GB)到 Windows 电脑:# Mac 或虚拟机上执行(假设通过 Tailscale SSH) scp zhangwenqi@100.123.82.91:/home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/update.img ~/Desktop/ -
Windows 电脑下载并安装 RKDevTool(瑞芯微官方烧录工具)
-
OrangePi CM5 进入 MaskROM 模式:
- 按住板子上的 Recovery / MaskROM 按键
- 插入 USB 线到电脑
- 松开按键
-
RKDevTool 界面切换到 升级固件 标签
-
点击 固件 按钮,选择
update.img -
点击 升级 按钮,等待烧录完成(约 2-5 分钟)
-
烧录完成后开发板自动重启进入 Android 13
镜像文件说明与高级烧录
update.img = Rockchip 打包格式,内部已包含所有其他 .img 文件。
单独的 .img 文件是给开发调试用的,用途:
| 场景 | 用哪个镜像 | 烧录方式 |
|---|---|---|
| 首次刷机 / 完整升级 | update.img |
RKDevTool 升级固件 |
| 只调试内核 | boot.img |
fastboot flash boot boot.img |
| 只更新系统 | super.img |
fastboot flash super super.img |
| 只更新 U-Boot | uboot.img |
fastboot flash uboot uboot.img |
| 只刷 Recovery | recovery.img |
fastboot flash recovery recovery.img |
| 改开机画面 | resource.img |
fastboot flash resource resource.img |
| SPI Flash + NVMe 设备 | update_spi_nvme.img |
RKDevTool 升级固件 |
日常开发推荐:
- 改内核 / 驱动 → 单独烧
boot.img(几秒到 1 分钟) - 改应用 / 系统框架 → 单独烧
super.img(1-2 分钟) - 改 Bootloader → 单独烧
uboot.img+MiniLoaderAll.bin - 不确定或要彻底刷新 → 烧
update.img(2-5 分钟)
这样可以避免每次修改都重刷整个系统。
3.7.12 关键差异对比(与 Radxa CM5)
| 项目 | Radxa CM5 | OrangePi CM5 |
|---|---|---|
| Android 版本 | 12 | 13 |
| Docker 基础镜像 | ubuntu:20.04 | ubuntu:22.04 |
| JDK 版本 | OpenJDK 8 | OpenJDK 11 |
| 交叉编译工具链 | 需手动 COPY 5 套 Linaro | 编译脚本自动下载 |
| SDK 分发方式 | 单个压缩包 | 9 个分卷(需先合并) |
| 编译脚本 | ./build.sh -AUCKup |
./make.sh -FMu -b orangepicm5 --gapps |
| 镜像输出目录 | rockdev/Image-RadxaCM5/ |
rockdev/Image-rk3588s_t/ |
| GMS 支持 | 需注释(官方不含) | --gapps 参数直接支持 |
| Docker 镜像名 | android-builder:12.x | android-builder-orangepi:13.x |
3.7.13 容器管理
# 退出容器(编译中断)
exit
# 重新进入已创建的容器(保留编译缓存)
sudo docker start -ai orangepi-build
# 删除容器(重新开始,会丢失容器内临时修改)
sudo docker rm -f orangepi-build
3.7.14 踩坑速查表(完整编译流水线)
以下所有坑都是实际编译过程中踩到的,新电脑按文档 3.7.1~3.7.12 操作时严格按顺序可以避开。
| # | 阶段 | 现象 | 根因 | 解决方法 |
|---|---|---|---|---|
| 1 | SDK 校验 | gz03 分卷 MD5 校验通过但解压到 46GB 报 invalid compressed data |
百度网盘下载过程中该分卷损坏,但上游 md5sum 正好是损坏版本的 MD5 | 重新从百度网盘下载 gz03,对比同网盘其他用户汇报的 MD5 |
| 2 | SDK 合并 | 官方文档推荐的 cat ... | tar -xzf - 管道式解压在分卷边界处出错 |
管道模式下 tar 无法定位错误位置 | 改用"先合并成单文件 → 再解压"两步法 |
| 3 | 构建镜像 | Android ninja 42 分钟时报 libncurses.so.5: cannot open shared object file |
Ubuntu 22.04 默认只有 libncurses.so.6 | Dockerfile 装 libncurses5 libncurses5:i386 |
| 4 | 主体编译 | 6 小时编译完成后打包失败:u-boot/trust.img not fount |
make.sh -F 只跑 U-Boot 编译,没调用 Rockchip 合成脚本 |
手动 cd u-boot && ./make.sh rk3588 |
| 5 | U-Boot 合成 | Rockchip make.sh 报 ERROR: No python2 |
Ubuntu 22.04 没 Python 2(默认只有 python3) | Dockerfile 装 python2 python2-dev python2.7 |
| 6 | 打包阶段 | mkimage.sh 找不到 trust.img |
新版 Rockchip 把 trust 合并到 uboot.img,不单独生成 | cp uboot.img trust.img(副本伪装) |
| 7 | 打包阶段 | 手动 lunch 后执行 mkimage.sh 编译目标变成 aosp_arm |
OrangePi make.sh 内部根据 -b orangepicm5 设置 lunch,手动设不对 |
直接用 ./make.sh -Mu -b orangepicm5 --gapps 打包 |
| 8 | 资源管理 | Soong 阶段内存峰值 19GB,会耗 Swap | Android 构建系统需要巨大内存 | 虚拟机分配 20GB 内存 + 9GB Swap(已配) |
| 9 | 编译效率 | 编译中断重启后重新跑全部阶段 | 未保留 out/ 缓存 | 不要 rm -rf out/,重启时 make.sh 会增量继续 |
| 10 | Docker 默认源 | docker build 拉 ubuntu:22.04 超时 |
Docker Hub 国内访问慢 | /etc/docker/daemon.json 配置镜像加速器 |
3.7.15 完整编译流水线(新电脑复现)
如果换新电脑后需要重新编译,按以下顺序:
# === 准备阶段 ===
# 1. 恢复 SDK 分卷到 ~/OrangePi_CM5/Aandroid_OrangePi/archive/
# 2. MD5 校验所有分卷
cd ~/OrangePi_CM5/Aandroid_OrangePi/archive
md5sum -c md5sum
# 3. 合并成单文件到 merged/
cd ..
cat archive/Android_13.tar.gz00 archive/Android_13.tar.gz01 \
archive/Android_13.tar.gz02 archive/Android_13.tar.gz03 \
archive/Android_13.tar.gz04 archive/Android_13.tar.gz05 \
archive/Android_13.tar.gz06 archive/Android_13.tar.gz07 \
archive/Android_13.tar.gz08 > merged/Android_13.tar.gz
# 4. 解压源码
cd merged && tar -xzf Android_13.tar.gz
# === 编译阶段 ===
# 5. 构建 Docker 镜像(按 3.7.6 的 Dockerfile)
cd ~/OrangePi_CM5/docker
sudo docker build -t android-builder-orangepi:13.x .
# 6. 主体编译(后台,约 6 小时)
sudo docker run -d --name orangepi-build \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
bash -c './make.sh -FMu -b orangepicm5 --gapps > /tmp/build.log 2>&1'
# 7. 等主体编译完成(会报 trust.img 缺失,正常)
# === 修复阶段 ===
# 8. 手动合成 Rockchip U-Boot
sudo docker run --rm \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace/u-boot \
android-builder-orangepi:13.x \
./make.sh rk3588
# 9. 创建 trust.img 伪装
sudo cp -a ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/uboot.img \
~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/trust.img
# 10. 只打包(复用已编译产物,约 5-10 分钟)
sudo docker run --rm \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
./make.sh -Mu -b orangepicm5 --gapps
# === 完成 ===
# 11. 验证 update.img
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/update.img
# 预期:2.3GB
3.8 Linux 虚拟机共享 Windows VPN(外网访问)
虚拟机通过 VMware 桥接网络使用 Windows 的 Clash 代理上外网。已配置开机自动生效。
当前配置信息(2026-04-15 已验证)
| 项目 | 值 |
|---|---|
| Windows 局域网 IP | 192.168.6.57 |
| Clash 代理端口 | 7890 |
| 虚拟机代理配置文件 | /etc/profile.d/proxy.sh |
Windows 端配置(一次性)
- Clash for Windows → 开启 "允许局域网连接入Clash"(Allow LAN)
- Windows 防火墙放行 7890 端口(管理员 PowerShell 执行):
netsh advfirewall firewall add rule name="Clash Proxy" dir=in action=allow protocol=tcp localport=7890
Linux 虚拟机端配置(已完成,开机自动生效)
代理配置写入 /etc/profile.d/proxy.sh,每次登录自动加载:
export http_proxy="http://192.168.6.57:7890"
export https_proxy="http://192.168.6.57:7890"
export HTTP_PROXY="http://192.168.6.57:7890"
export HTTPS_PROXY="http://192.168.6.57:7890"
export no_proxy="localhost,127.0.0.1,100.64.0.0/10,192.168.0.0/16"
export NO_PROXY="localhost,127.0.0.1,100.64.0.0/10,192.168.0.0/16"
验证外网
curl -I https://www.google.com
curl -I https://releases.linaro.org/
前提条件
- Windows 电脑开机且 Clash 运行中
- Clash 的 "允许局域网连接入Clash" 保持开启
注意事项
-
Windows IP 变化:如果 Windows 通过 DHCP 获取 IP,重启路由器后 IP 可能变化。届时需要修改
/etc/profile.d/proxy.sh中的 IP 地址:sudo nano /etc/profile.d/proxy.sh # 将 192.168.6.57 替换为新的 Windows IP然后重新登录或执行
source /etc/profile.d/proxy.sh生效。 建议在 Windows 上设置静态 IP 或在路由器中绑定 DHCP 避免此问题。 -
Docker 容器内使用代理:
/etc/profile.d/的代理对 Docker 容器内不生效,启动容器时需传入环境变量:sudo docker run -it \ -e http_proxy="http://192.168.6.57:7890" \ -e https_proxy="http://192.168.6.57:7890" \ -e no_proxy="localhost,127.0.0.1" \ --name android-build \ -v ~/Radxa_CM5/SDK_Android:/workspace \ -w /workspace \ android-builder:12.x bash
第四部分:Mac 端 SSH 连接配置
4.1 生成 SSH 密钥
ssh-keygen -t ed25519 -C "mac-to-linux-vm"
# 一路回车使用默认设置
4.2 部署公钥到虚拟机
ssh-copy-id zhangwenqi@100.123.82.91
# 输入虚拟机密码,完成后即可免密登录
4.3 SSH 配置文件
编辑 ~/.ssh/config:
Host linux-vm
HostName 100.123.82.91
User zhangwenqi
Port 22
ServerAliveInterval 30
TCPKeepAlive yes
ConnectTimeout 60
之后直接 ssh linux-vm 即可连接。
4.4 VS Code Remote-SSH(推荐用于驱动开发)
- VS Code 安装 Remote - SSH 扩展
- Cmd+Shift+P → "Remote-SSH: Connect to Host"
- 选择 linux-vm
- 连接成功后打开文件夹
/home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
Remote-SSH 模式下 VS Code 的后端(vscode-server、扩展、终端)都跑在虚拟机上,Mac 只负责 UI。 编译、git、文件索引全都是虚拟机本地操作,性能和稳定性最佳。
4.5 SMB 挂载(轻量文件浏览/编辑,不适合驱动开发)
macOS 原生支持 SMB,通过 Tailscale 直接挂载虚拟机 Samba 共享目录:
挂载方式:
- Finder →
⌘+K(连接到服务器) - 输入:
smb://100.123.82.91 - 输入虚拟机用户名/密码
- 选择要挂载的共享目录,挂载点位于
/Volumes/<share_name>
挂载成功后 VS Code 可直接打开挂载目录,像本地文件一样编辑。
适用场景(轻量用途)
| 用途 | 可行性 |
|---|---|
| 临时查看/编辑几个配置文件 | ✅ |
| 快速预览源码结构 | ✅ |
| Finder 里拖文件到 Mac 备份 | ✅ |
| 打开整个 84GB SDK 做索引开发 | ⚠️ 首次索引慢 |
| 跑编译 / 烧录 / git 大仓库操作 | ❌ 必须 SSH 到虚拟机 |
| 让 Claude Code 自动编译调试 | ❌ Mac shell 无 Linux 工具链 |
SMB 方案关键局限
- 文件访问 ≠ 执行环境:SMB 只能让 Mac 看到文件,无法在 Mac 上跑 Android 编译(缺 Rockchip 工具链、Docker、特定 Python/libncurses5 等依赖)
- 符号链接支持有限:Android SDK 含大量符号链接,SMB 可能表现异常
- Git 操作不稳定:文件锁语义差异,
git status在 SMB 挂载目录上不可靠 - 文件系统大小写:macOS 默认大小写不敏感 + Linux 大小写敏感,可能冲突
.DS_Store污染:Mac Finder 会在挂载目录留下元数据文件,进 git 会碍事
结论:两种方案如何选择
- 驱动开发主力:Remote-SSH(编辑 + 编译 + 调试 + git 全部在虚拟机)
- 辅助场景:SMB(只看几个文件、拖文件备份)
两者不冲突,可以同时配好,按需使用。
第五部分:换新电脑配置指南
5.1 虚拟机备份说明
当前备份状态(2026-04-16)
| 项目 | 状态 |
|---|---|
| 备份格式 | OVF(.ovf + .vmdk + .mf 三个文件) |
| 备份内容 | 系统 + Docker(不含 SDK,不含工具链 COPY 到镜像) |
| Docker 镜像 | android-builder:12.x 基础环境已装,未对齐官方工具链 |
| SDK 压缩包 | 单独存放在 Windows 电脑/移动硬盘 |
OVF 备份包含的设置
| 配置项 | 是否备份 | 导入后能否修改 |
|---|---|---|
| 硬盘大小(700GB) | 包含 | 可扩大,不可缩小 |
| 内存大小 | 包含 | 可自由调整 |
| CPU 核心数 | 包含 | 可自由调整 |
| 网络适配器类型(桥接) | 包含 | 可改为 NAT/桥接/仅主机 |
| 操作系统和所有软件 | 包含 | — |
三个文件必须放在同一文件夹,缺一不可:
.ovf— 虚拟机配置描述(CPU、内存、网卡等).vmdk— 虚拟磁盘数据(系统和所有文件).mf— 校验文件(验证完整性)
5.2 新电脑是 Windows — 完整恢复步骤
步骤 1:导入虚拟机
- 新电脑安装 VMware Workstation 17(或更高版本)
- VMware → 文件 → 打开 → 选择
.ovf文件 - 选择虚拟机存放位置(建议放在 SSD 上)
- 等待导入完成
- (可选)调整虚拟机参数:右键虚拟机 → 设置
- 内存:建议 16GB+(Android 编译非常吃内存)
- CPU:建议分配 8 核+
- 网络:选择桥接模式(与宿主机同网段)
步骤 2:启动虚拟机并恢复网络
-
启动虚拟机,登录(用户: zhangwenqi)
-
检查网络:
ip addr show # 确认获取到 IP ping 8.8.8.8 # 测试内网连通性 -
恢复 Tailscale:
sudo systemctl status tailscaled # 检查 Tailscale 服务 sudo tailscale up # 重新认证(可能需要) tailscale ip -4 # 记录新的 Tailscale IP -
更新代理配置(Windows IP 可能变化):
# 在 Windows cmd 执行 ipconfig 获取新的局域网 IP # 然后更新虚拟机代理配置 sudo nano /etc/profile.d/proxy.sh # 将 192.168.6.57 替换为新 Windows 的局域网 IP source /etc/profile.d/proxy.sh # 测试外网 curl -I https://www.google.com
步骤 3:恢复 SDK
# 将 SDK 压缩包从移动硬盘拷贝到虚拟机
# 可以通过 VMware 共享文件夹、scp、或 USB 挂载
cd ~/Radxa_CM5/SDK_Android/
# 确认文件
ls -lh radxa_android_sdk_backup.tar.gz
# 校验 MD5
md5sum radxa_android_sdk_backup.tar.gz
步骤 4:对齐官方 Dockerfile 环境(重要)
备份的虚拟机中 Docker 镜像是基础版本,缺少 5 套 Linaro 交叉编译工具链。需要重新下载工具链并重建镜像。
4.1 确认 Docker 和基础镜像正常
docker images android-builder
# 预期看到 android-builder:12.x 镜像
4.2 确认代理可用(下载工具链需要外网)
curl -I https://releases.linaro.org/
# 预期 HTTP 200
如果不通,先按 3.7 节配置虚拟机代理。
4.3 下载 5 套交叉编译工具链
cd ~/Radxa_CM5/docker
# 1. gcc-linaro-6.3.1 arm-linux-gnueabihf(99MB)
curl -L -o gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz \
'https://releases.linaro.org/components/toolchain/binaries/6.3-2017.02/arm-linux-gnueabihf/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz'
# 2. gcc-linaro-aarch64-none-elf-4.8(50MB)
curl -L -o gcc-linaro-aarch64-none-elf-4.8-2013.11_linux.tar.bz2 \
'https://releases.linaro.org/archive/13.11/components/toolchain/binaries/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux.tar.bz2'
# 3. gcc-arm-none-eabi-6-2017-q2-update(96MB)
curl -L -o gcc-arm-none-eabi-6-2017-q2-update.tar.bz2 \
'https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2'
# 4. gcc-linaro-6.3.1 aarch64-linux-gnu(106MB)
curl -L -o gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu.tar.xz \
'https://releases.linaro.org/components/toolchain/binaries/6.3-2017.02/aarch64-linux-gnu/gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu.tar.xz'
# 5. gcc-linaro-7.2.1 aarch64-elf(51MB)
curl -L -o gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf.tar.xz \
'https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/aarch64-elf/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf.tar.xz'
4.4 解压工具链
cd ~/Radxa_CM5/docker
tar -xf gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz
tar -xf gcc-linaro-aarch64-none-elf-4.8-2013.11_linux.tar.bz2
tar -xf gcc-arm-none-eabi-6-2017-q2-update.tar.bz2
tar -xf gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu.tar.xz
tar -xf gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf.tar.xz
# 确认解压结果(应有 5 个目录)
ls -d gcc-*/
4.5 确认 Dockerfile 包含 COPY 命令
查看 ~/Radxa_CM5/docker/Dockerfile,确认包含以下 COPY 行:
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf /opt/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf
COPY ./gcc-linaro-aarch64-none-elf-4.8-2013.11_linux /opt/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux
COPY ./gcc-linaro-aarch64-none-elf-4.8-2013.11_linux /opt/toolchains/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux
COPY ./gcc-arm-none-eabi-6-2017-q2-update /opt/toolchains/gcc-arm-none-eabi-6-2017-q2-update
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf /opt/toolchains/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu /opt/toolchains/gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu
COPY ./gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf /opt/toolchains/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf
如果 Dockerfile 中没有这些行(备份时可能是旧版本),用以下完整 Dockerfile 替换:
cat > ~/Radxa_CM5/docker/Dockerfile << 'EOF'
FROM ubuntu:20.04
RUN rm /etc/apt/sources.list
RUN echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse" | tee /etc/apt/sources.list
RUN echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse" >> /etc/apt/sources.list
RUN echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse" >> /etc/apt/sources.list
RUN echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse" >> /etc/apt/sources.list
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get install -y software-properties-common apt-utils
RUN add-apt-repository -y ppa:deadsnakes/ppa
RUN apt-get update -y && apt-get install -y python3.8
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 150
RUN apt-get install -y python3-pip && pip install pycrypto
RUN apt-get update -y && apt-get install -y openjdk-8-jdk python git-core gnupg flex bison gperf build-essential \
zip curl gawk liblz4-tool zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
libncurses5 libncurses-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip mtools u-boot-tools \
htop iotop sysstat iftop pigz bc device-tree-compiler lunzip \
dosfstools vim-common parted udev libssl-dev sudo rsync python3-pyelftools cpio
RUN curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > /usr/local/bin/repo && \
chmod +x /usr/local/bin/repo && \
which repo
ENV REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'
RUN apt-get install -y lzop swig
RUN apt-get update -y && apt-get install -y tzdata
RUN mkdir /opt/toolchains
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf /opt/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf
COPY ./gcc-linaro-aarch64-none-elf-4.8-2013.11_linux /opt/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux
COPY ./gcc-linaro-aarch64-none-elf-4.8-2013.11_linux /opt/toolchains/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux
COPY ./gcc-arm-none-eabi-6-2017-q2-update /opt/toolchains/gcc-arm-none-eabi-6-2017-q2-update
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf /opt/toolchains/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf
COPY ./gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu /opt/toolchains/gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu
COPY ./gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf /opt/toolchains/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf
RUN apt-get install -y net-tools gcc-arm-linux-gnueabihf gcc-arm-none-eabi
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
EOF
4.6 重建 Docker 镜像
cd ~/Radxa_CM5/docker
sudo docker build -t android-builder:12.x .
# 耗时约 10-15 分钟
4.7 验证环境完整
sudo docker run --rm android-builder:12.x bash -c \
'echo "=== 工具链 ===" && ls /opt/toolchains/ && \
echo "" && echo "=== 编译器 ===" && \
/opt/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc --version | head -1 && \
/opt/toolchains/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-elf/bin/aarch64-elf-gcc --version | head -1 && \
/opt/toolchains/gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc --version | head -1 && \
java -version 2>&1 | head -1 && python3 --version && which repo'
预期输出:
- /opt/toolchains/ 下有 5 个工具链目录
- arm-linux-gnueabihf-gcc 6.3.1
- aarch64-elf-gcc 7.2.1
- aarch64-linux-gnu-gcc 6.3.1
- openjdk 1.8.0
- Python 3.8.x
- /usr/local/bin/repo
4.8 清理下载的压缩包
cd ~/Radxa_CM5/docker
rm -f *.tar.xz *.tar.bz2
注意:Docker 镜像加速器配置(/etc/docker/daemon.json)在备份中已保留,但镜像加速地址可能失效。
如果 docker build 拉取 ubuntu:20.04 失败,参考 3.4 节更新加速器地址。
5.2 新电脑是 Windows — 快速恢复
步骤 1:导入虚拟机
- 新电脑安装 VMware Workstation 17
- VMware → 文件 → 打开 → 选择
.ovf文件 - 选择存放位置,等待导入
- 调整参数(右键 → 设置):内存 16GB+,CPU 8 核+,网络桥接模式
步骤 2:恢复网络和代理
- 启动虚拟机,检查网络
- 更新
/etc/profile.d/proxy.sh中的 Windows IP(如果 IP 变化) - Windows 安装 Clash,开启 Allow LAN,防火墙放行 7890
- 安装 Tailscale(Windows + 虚拟机),同账号登录
步骤 3:恢复 SDK 并对齐 Dockerfile
- 拷贝 SDK 压缩包到虚拟机
~/Radxa_CM5/SDK_Android/ - 按上方 步骤 4(4.1~4.8) 对齐官方 Dockerfile 环境
- 解压 SDK 并编译验证
步骤 4:配置 Mac 远程开发(如有)
按第四部分重新配置 Mac 端 SSH 和 Tailscale。
5.3 新电脑是 Mac(远程控制 Windows 开发)
Mac M 芯片无法运行 x86 虚拟机,采用 Mac 远程控制 Windows 方案。
前提条件
- Windows 电脑保持运行(作为编译服务器)
- Windows 上已按 5.2 完成虚拟机恢复和 Dockerfile 对齐
- Mac 和 Windows 都安装 Tailscale
步骤 1:Windows 端配置
-
按 5.2 完成虚拟机导入、网络恢复、SDK 恢复、Dockerfile 对齐
-
安装 Tailscale(Windows 版),用同一账号登录
-
安装 Clash for Windows,导入订阅,开启 "允许局域网连接入Clash" 防火墙放行:
netsh advfirewall firewall add rule name="Clash Proxy" dir=in action=allow protocol=tcp localport=7890 -
启动虚拟机,确认服务正常:
tailscale status sudo systemctl status ssh curl -I https://www.google.com # 验证外网代理 docker images android-builder # 验证 Docker 镜像 -
(可选)设置虚拟机开机自启:VMware → 编辑 → 首选项 → 共享虚拟机 → 开机启动
步骤 2:新 Mac 端配置
-
安装 Tailscale(App Store),用同一账号登录
-
安装 ClashX Pro,导入订阅
-
配置 ClashX Pro 与 Tailscale 共存(按第二部分操作):
- 添加 Tailscale 直连规则到代理配置
- 关闭 Tailscale DNS Settings
-
生成 SSH 密钥并部署:
ssh-keygen -t ed25519 -C "new-mac-to-linux-vm" ssh-copy-id zhangwenqi@<虚拟机Tailscale_IP>注意:Tailscale IP 可能与之前不同,在虚拟机中执行
tailscale ip -4确认。 -
配置 SSH config(
~/.ssh/config):Host linux-vm HostName <虚拟机Tailscale_IP> User zhangwenqi Port 22 ServerAliveInterval 30 TCPKeepAlive yes ConnectTimeout 60 -
安装 VS Code + Remote-SSH 扩展
-
Cmd+Shift+P → "Remote-SSH: Connect to Host" → linux-vm
步骤 3:验证
# Mac 终端测试
ping <虚拟机Tailscale_IP>
ssh linux-vm
# 验证 Docker 环境完整(应显示 5 套工具链)
ssh linux-vm "docker run --rm android-builder:12.x ls /opt/toolchains/"
# 验证 VPN
# 浏览器访问 YouTube
日常使用流程
- Windows 电脑开机 → 虚拟机自动启动(或手动启动)
- Mac 打开 Tailscale + ClashX Pro
- VS Code → Remote-SSH → linux-vm
- 打开
/home/zhangwenqi/Radxa_CM5开始开发
第六部分:故障排除
6.1 Tailscale 连不上
# Linux 端检查
sudo tailscale status
sudo systemctl restart tailscaled
sudo tailscale up
# 诊断
tailscale netcheck
6.2 SSH 连接被拒绝
# Linux 端检查
sudo systemctl status ssh
sudo netstat -tlnp | grep :22
sudo ufw status
# 重启 SSH
sudo systemctl restart ssh
6.3 VPN 开启 Tailscale 后失效
检查两个配置:
- ClashX Pro 配置文件中 Tailscale 直连规则是否存在(订阅更新会覆盖)
- Tailscale DNS Settings 是否已关闭
6.4 Docker 拉取镜像超时
Docker Hub 被墙,检查镜像加速配置:
cat /etc/docker/daemon.json
# 确认 registry-mirrors 配置正确
sudo systemctl restart docker
6.5 VS Code Remote-SSH 连接超时
在 ~/.ssh/config 中增大超时:
Host linux-vm
ConnectTimeout 120
ServerAliveInterval 30
ServerAliveCountMax 5
第七部分:快速参考
日常连接命令
# Mac 终端直连
ssh linux-vm
# 文件传输
scp file.txt linux-vm:~/
scp -r folder/ linux-vm:~/destination/
VS Code 操作
- Cmd+Shift+P → "Remote-SSH: Connect to Host" → linux-vm
- Cmd+Shift+P → "Remote-SSH: Disconnect"
Linux 虚拟机管理
# Tailscale
tailscale ip -4
tailscale status
sudo tailscale down / up
# Docker
docker images
docker run -it --name android-build -v ~/Radxa_CM5/SDK_Android:/workspace -w /workspace android-builder:12.x bash
docker start -ai android-build # 重新进入已创建的容器
# SSH
sudo systemctl status ssh
sudo systemctl restart ssh
第八部分:Android SDK Git 代码管理
8.1 方案说明
OrangePi CM5 Android 13 SDK 共 84GB、数十万文件,不可能全量入 Git。
采用白名单 .gitignore 方案:在 SDK 根目录初始化 Git,默认忽略所有文件,只放行会被修改的核心目录。
- Git 仓库路径:
~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/ - 远程仓库:GitHub(
OrangePi_CM5_Project,Private) - 首次推送(96,602 个文件,.git 约 1.3GB)约需 30-60 分钟
8.2 追踪的核心目录
| 目录 | 作用 |
|---|---|
device/rockchip/ |
产品配置、开机画面、init.rc、权限 |
kernel-5.10/arch/arm64/boot/dts/ |
设备树(屏幕/LED/NFC/GPIO 等) |
kernel-5.10/arch/arm64/configs/ |
内核配置文件 |
kernel-5.10/drivers/ |
内核驱动源码 |
kernel-5.10/include/ |
内核头文件 |
hardware/rockchip/ |
Rockchip 硬件 HAL |
hardware/interfaces/ |
Android 硬件接口 |
hardware/libhardware/ |
Android hardware 库 |
u-boot/arch/arm/ |
U-Boot 架构相关代码 |
u-boot/configs/ |
U-Boot 配置 |
u-boot/board/ |
板级支持 |
u-boot/drivers/ |
U-Boot 驱动 |
u-boot/include/ |
U-Boot 头文件 |
u-boot/dts/ |
U-Boot 设备树 |
frameworks/base/core/res/ |
系统默认资源(开机动画等) |
8.3 .gitignore 白名单内容
# 策略:根目录忽略所有,只白名单放行会改的目录
# 1. 默认忽略根目录所有内容
/*
# 2. 白名单:允许这些顶层目录/文件
!/.gitignore
!/README.md
!/device/
!/kernel-5.10/
!/hardware/
!/u-boot/
!/frameworks/
# ===== device 目录:只保留 rockchip =====
/device/*
!/device/rockchip/
/device/rockchip/*
!/device/rockchip/common/
!/device/rockchip/rk3588/
# ===== kernel-5.10:只保留驱动、设备树、配置 =====
/kernel-5.10/*
!/kernel-5.10/arch/
!/kernel-5.10/drivers/
!/kernel-5.10/kernel/
!/kernel-5.10/include/
!/kernel-5.10/Documentation/
# kernel-5.10/arch 只保留 arm64
/kernel-5.10/arch/*
!/kernel-5.10/arch/arm64/
/kernel-5.10/arch/arm64/*
!/kernel-5.10/arch/arm64/boot/
!/kernel-5.10/arch/arm64/configs/
/kernel-5.10/arch/arm64/boot/*
!/kernel-5.10/arch/arm64/boot/dts/
# ===== hardware 只保留 rockchip =====
/hardware/*
!/hardware/rockchip/
!/hardware/interfaces/
!/hardware/libhardware/
# ===== u-boot 只保留配置、设备树、板级代码 =====
/u-boot/*
!/u-boot/arch/
!/u-boot/board/
!/u-boot/configs/
!/u-boot/include/
!/u-boot/drivers/
!/u-boot/dts/
/u-boot/arch/*
!/u-boot/arch/arm/
/u-boot/arch/arm/*
!/u-boot/arch/arm/dts/
!/u-boot/arch/arm/mach-rockchip/
!/u-boot/arch/arm/include/
# ===== frameworks 只保留 base/core/res =====
/frameworks/*
!/frameworks/base/
/frameworks/base/*
!/frameworks/base/core/
/frameworks/base/core/*
!/frameworks/base/core/res/
# ===== 编译产物和临时文件 =====
out/
*.o
*.ko
*.a
*.so
*.so.dbg
*.pyc
*.swp
*.swo
*~
__pycache__/
.DS_Store
# ===== 子仓库的 .git 内部目录 =====
**/.git/
8.4 日常工作流
# 在 SDK 中直接改代码(例如 NFC 驱动)
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
vi kernel-5.10/drivers/nfc/my_nfc.c
# 查看修改
git status
git diff
# 提交
git add kernel-5.10/drivers/nfc/
git commit -m "添加 PN532 NFC 驱动支持"
git push
8.5 回滚操作
# 查看历史
git log --oneline
# 回滚单个文件到上一版本
git checkout HEAD~1 -- kernel-5.10/drivers/nfc/my_nfc.c
# 回滚整个目录
git checkout HEAD~1 -- kernel-5.10/drivers/nfc/
# 慎用:回滚全部到某个 commit
git reset --hard <commit_hash>
8.6 分支管理(推荐功能分支)
# 创建功能分支
git checkout -b feature/nfc-pn532
# 开发、多次 commit 后合并回 main
git checkout main
git merge feature/nfc-pn532
# 废弃分支
git branch -D feature/nfc-pn532
8.7 首次推送到 GitHub(SSH 密钥方式,推荐)
当前仓库: git@github.com:Leo-z8/OrangePi_CM5_Project.git(Private)
认证方式: SSH ed25519 密钥,永久免密
网络路径: 虚拟机 → GitHub ssh.github.com:443(直连,不经 Clash 代理)
为什么选 SSH 而非 HTTPS + PAT
| 对比项 | HTTPS + PAT | SSH 密钥 |
|---|---|---|
| 有效期 | 最长 1 年,需定期更新 | 永久有效 |
| 网络路径 | 必须走 Clash 代理(HTTPS 国内慢) | 443 端口直连 GitHub |
| 稳定性 | TLS 握手依赖代理链,大文件易挂断 | SSH 原生流控,错误信息清晰 |
| 推送速度 | 受 Clash 带宽 + rwnd 限制 | 实测稳定 4-6 MiB/s |
| 首次推送 758 MiB | 多次中断重试 | 约 2-3 分钟完成 |
步骤 1:虚拟机生成 SSH 密钥
ssh-keygen -t ed25519 -C "zhangwenqi-orangepi-vm-github" \
-f ~/.ssh/github_ed25519 -N ''
生成后两个文件:
~/.ssh/github_ed25519(私钥,权限 600)~/.ssh/github_ed25519.pub(公钥)
步骤 2:配置 ~/.ssh/config 走 443 端口
Host github.com
HostName ssh.github.com
User git
Port 443
IdentityFile ~/.ssh/github_ed25519
IdentitiesOnly yes
ServerAliveInterval 30
关键点:
HostName ssh.github.com+Port 443绕过国内对 22 端口的封锁IdentitiesOnly yes强制只用这个密钥,防止扔其他密钥被 GitHub 拒绝- 此方案不依赖 Clash 代理,虚拟机可直连
步骤 3:添加公钥到 GitHub
cat ~/.ssh/github_ed25519.pub
复制整行内容,到 https://github.com/settings/ssh/new 添加:
- Title:
OrangePi-VM-Linux - Key type:Authentication Key
- Key:粘贴公钥
步骤 4:测试连通性
ssh -T git@github.com
# 预期输出:Hi Leo-z8! You've successfully authenticated
步骤 5:切换仓库 remote 到 SSH
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
# 清除旧 HTTPS 配置(如果之前用过 PAT)
rm -f ~/.git-credentials
git config --local --unset credential.helper 2>/dev/null
git config --local --unset http.proxy 2>/dev/null
git config --local --unset https.proxy 2>/dev/null
# 改 remote 为 SSH
git remote set-url origin git@github.com:Leo-z8/OrangePi_CM5_Project.git
git remote -v
# 推送(永久免密)
git push -u origin main
备选方案:HTTPS + PAT(不推荐作为长期方案)
仅在没有 SSH 配置权限或临时应急时使用:
# 配置 git 代理(HTTPS 必须走 Clash)
git config http.proxy http://192.168.6.57:7890
git config https.proxy http://192.168.6.57:7890
git config http.postBuffer 524288000 # 增大缓冲,大仓库必需
git remote set-url origin https://github.com/Leo-z8/OrangePi_CM5_Project.git
git config credential.helper store
# 提示时:用户名 = GitHub 账号,密码 = PAT 令牌
git push -u origin main
PAT 生成:https://github.com/settings/tokens/new → 勾
repo→ 生成后立即复制。 推送完成后建议立即 Revoke(一次性使用),因为 PAT 明文存在~/.git-credentials有泄露风险。
8.8 推送后日常 push
SSH 方式配置后完全免密:
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
git add kernel-5.10/drivers/nfc/
git commit -m "添加 PN532 NFC 驱动"
git push # 免密,永久免代理
git pull # 同理
8.9 备份策略
| 资产 | 备份位置 |
|---|---|
| 驱动/配置修改 | GitHub 仓库(Leo-z8/OrangePi_CM5_Project,Private) |
| SDK 原始源码分卷(9个 tar.gz) | Windows 电脑(永久保留,不能删) |
| Docker 镜像定义 | ~/OrangePi_CM5/docker/Dockerfile |
| 编译产物(update.img) | 按需保留,体积大不进 Git |
被 .gitignore 排除的大文件 |
需单独备份(见 8.10) |
8.10 GitHub 推送踩坑实录(2026-04-22 首次推送)
踩坑 1:GitHub 单文件 100MB 硬性限制
现象:HTTPS 推送数据上传完后 "远端意外挂断了";SSH 推送明确报错:
remote: error: File device/rockchip/rk3588/rk3588m_car/preinstall/camera360.apk
is 120.48 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected.
! [remote rejected] main -> main (pre-receive hook declined)
根因:GitHub pre-receive hook 在服务端验收阶段拒绝 >100MB 单文件。HTTPS 协议表现为 TLS 连接异常终止(难诊断),SSH 协议会直接打印错误(易诊断)。
教训:大文件推送问题要用 SSH 调试,HTTPS 的"远端挂断"往往掩盖真实原因。
踩坑 2:扫大文件要用 git ls-tree,不能用 git rev-list
# ❌ 错误方式:按 blob 扫描,Git 去重会掩盖多路径
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '$1=="blob" && $3>=100000000'
# ✅ 正确方式:按 HEAD 中的路径扫描
git ls-tree -r -l HEAD | awk '$4>=100000000 {printf "%.1f MB %s\n", $4/1024/1024, $5}'
根因:两个产品目录下存在完全相同的 camera360.apk(内容 hash 相同),Git 按 blob 去重存储,rev-list 只列出一个 blob,但 ls-tree 会展开所有路径。
教训:初次 git rm --cached 只去掉一个路径,第二次推送时另一个路径的 APK 仍会撞限,必须一次性用通配符覆盖所有。
踩坑 3:.gitignore 用具体路径易遗漏,通配符更健壮
# ❌ 窄规则:只管一个路径
device/rockchip/rk3588/rk3588m_car/preinstall/camera360.apk
# ✅ 通配符:所有产品配置下的预装 APK 一次性处理
device/rockchip/*/*/preinstall/*.apk
教训:多产品配置项目中(rk3588m_car / rk3588m_s / …)类似的二进制资源可能分布在多个目录,写 .gitignore 规则时默认用通配符。
踩坑 4:HTTPS + Clash 代理的 TCP rwnd 瓶颈
推送过程 ss -tni 显示 rwnd_limited: 95.1% —— 95% 时间卡在 GitHub 端接收窗口,Clash 代理到 GitHub 的上行链路成瓶颈,平均速度被压到 1-2 MiB/s。
切到 SSH 443 直连后 rwnd_limited: 0.8%,速度稳定 4-6 MiB/s。
结论:大仓库首次推送坚决用 SSH,不要 HTTPS。
踩坑 5:凭证泄露风险
HTTPS + PAT 方案下,凭证明文存在 ~/.git-credentials:
https://用户名:PAT@github.com
即使 PAT 只用一次,也要:
- 推送完成后到 https://github.com/settings/tokens 立即 Revoke
rm -f ~/.git-credentialsgit config --unset credential.helper
SSH 方式无此风险:私钥只存本地 ~/.ssh/,不会经过对话或日志。
本次推送完整大文件排除流程(已落地)
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
# 1. 扫出所有 >100MB 路径
git ls-tree -r -l HEAD | awk '$4>=100000000 {printf "%.1f MB %s\n", $4/1024/1024, $5}'
# 2. 从索引移除(磁盘文件保留)
git rm --cached device/rockchip/rk3588/rk3588m_car/preinstall/camera360.apk
git rm --cached device/rockchip/rk3588/rk3588m_s/preinstall/camera360.apk
# 3. .gitignore 追加通配符规则
cat >> .gitignore << 'EOF'
# 排除所有预装 APK(GitHub 100MB 限制 + 非驱动开发必需)
device/rockchip/*/*/preinstall/*.apk
EOF
# 4. amend 原 commit
git add .gitignore
git commit --amend --no-edit
# 5. 验证 HEAD 里无 >100MB 文件
git ls-tree -r -l HEAD | awk '$4>=100000000' # 应无输出
# 6. 推送
git push -u origin main
需要单独备份的大文件清单
以下文件已从 Git 历史排除,需另存(网盘/内网文件服务器):
| 文件 | 大小 | 路径 |
|---|---|---|
| camera360.apk (rk3588m_car) | 120 MB | device/rockchip/rk3588/rk3588m_car/preinstall/ |
| camera360.apk (rk3588m_s) | 120 MB | device/rockchip/rk3588/rk3588m_s/preinstall/ |
这些是产品预装 APK,不是驱动开发所需,完整 SDK 部署时再从备份恢复即可。
8.11 GitHub 推送关键数据(本次首次推送)
| 指标 | 数值 |
|---|---|
本地仓库大小(.git) |
774 MB |
| 追踪文件总数 | 96,600 |
| 上传 pack 大小 | 约 518 MiB(移除两个 120MB APK 后) |
| 网络路径 | 虚拟机 → Tailscale(不经 Clash) → ssh.github.com:443 |
| 实测速度 | 4-6 MiB/s(SSH 443 直连,无 rwnd 瓶颈) |
| 首次推送耗时 | 约 2-3 分钟(纯网络时间,排除多次重推) |
| 最终 commit | c79995a30a9db560899bf0845440720edf3e5e3d |
| 远程仓库 | https://github.com/Leo-z8/OrangePi_CM5_Project |
第九部分:Mac 本地 + 虚拟机编译 的双端开发工作流(2026-04-23 落地,推荐)
9.1 为什么采用这种方案
背景:虚拟机内用 Claude CLI 不可行
实测发现(2026-04-23):
- Anthropic 对中国机场 IP 做了 API 级风控,
api.anthropic.com对机场 IP 返回 ECONNRESET 或静默丢包(但 HTTPS 握手能成功、console.anthropic.com也能通,仅/v1/messages被精准拉黑) - 尝试过多种节点(美国 IEPL / 香港 / 新加坡),机场共享 IP 段都被 Ban
- 虚拟机内装 Claude CLI(Node 20 +
@anthropic-ai/claude-code2.1.118)后,启动永远卡在 "Checking connectivity..." 或 ECONNRESET - 新版 Claude CLI(2.1.118+)打包成独立二进制,屏蔽
NODE_OPTIONS等代理注入手段
解决方向
Mac 能连 Anthropic(你已在用),Linux 虚拟机保留编译能力。让两边各司其职:
Mac GitHub Linux 虚拟机
Claude Code + 代码仓库 ──push──▶ dev/mac ◀──pull── 完整 SDK + 编译工具链
(读写/改/grep/Edit) 分支 (./make.sh 编译)
▲ │
└─────────── Claude 通过 SSH 直接操作虚拟机编译 ────────────┘
Claude 可以通过 Bash 工具 SSH 到虚拟机自动编译,实现全自动闭环,不需要用户在两端手动搬运错误信息。
9.2 Mac 端一次性环境配置
9.2.1 创建 case-sensitive APFS 卷(关键,否则会踩大坑)
Linux 内核源码含同名但大小写不同的文件(如 xt_CONNMARK.h / xt_connmark.h),macOS 默认 APFS 是 case-insensitive,会把这些文件合并成一个,导致 git status 永远脏且无法还原。
解决:建一个专门的 case-sensitive 卷放 Linux 项目。
- 打开 磁盘工具
- 左侧选 Container disk3(Macintosh HD 的父容器)
- 菜单「编辑 → 添加 APFS 卷...」
- 弹窗中:
- 名称:
LinuxDev - 格式:APFS(区分大小写) ← 关键
- 大小:不限制(APFS 容器自动共享空间)
- 名称:
- 确认 / 添加
验证成功:
diskutil info /Volumes/LinuxDev | grep Personality
# 应输出: File System Personality: Case-sensitive APFS
# 实验创建大小写同名文件
touch /Volumes/LinuxDev/_test_a /Volumes/LinuxDev/_test_A
ls /Volumes/LinuxDev/_test_*
# 应输出两行(_test_A 和 _test_a)
rm /Volumes/LinuxDev/_test_*
从此 Linux 项目都放 /Volumes/LinuxDev/ 下。
9.2.2 Mac 端 Git 配置
# 只需要配一次,以后所有项目都复用
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
git config --global credential.helper osxkeychain # PAT 存钥匙串
9.2.3 首次克隆到 case-sensitive 卷
cd /Volumes/LinuxDev
git clone https://github.com/Leo-z8/OrangePi_CM5_Project.git
cd OrangePi_CM5_Project
# 首次弹出凭证提示:
# Username: Leo-z8
# Password: 粘贴 PAT (ghp_xxx...)
# 之后 PAT 存钥匙串永久免密
重要:不要用 GitHub Web 下载 ZIP(文件夹带
-main后缀那种) ZIP 不含.git/元数据,无法 push。必须git clone。
9.2.4 创建开发分支
cd /Volumes/LinuxDev/OrangePi_CM5_Project
git checkout -b dev/mac
git push -u origin dev/mac
9.3 虚拟机端一次性环境配置
如果之前已按本文档 8.7 配好 SSH ed25519 over 443,这步跳过。否则见 8.7。
确认:
ssh linux-vm "cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13 && git remote -v"
# 应该显示 origin git@github.com:Leo-z8/OrangePi_CM5_Project.git
9.4 日常开发闭环(Claude 自主执行)
Claude 收到开发任务后会自动执行:
┌──────────────────────────────────────────────────────────┐
│ Claude 在 Mac 端工作 │
│ 1. cd /Volumes/LinuxDev/OrangePi_CM5_Project │
│ 2. 读源码 / grep / 分析 │
│ 3. Edit 修改代码 │
│ 4. git add xxx │
│ 5. git commit -m "描述" │
│ 6. git push origin dev/mac │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Claude 通过 Bash 工具 SSH 到虚拟机 │
│ ssh linux-vm 'cd ~/OrangePi_CM5/.../Android_13 && \ │
│ git fetch origin && \ │
│ git checkout dev/mac && git pull && \ │
│ ./make.sh -F -b orangepicm5' │
│ │
│ 编译输出直接进 Claude 的上下文 │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ 编译成功? │
└──────┬──────────────┘
│ 失败 │ 成功
▼ ▼
Claude 读错误 通知用户烧录
分析 → 修代码 (物理操作)
再 push 再编
(自循环)
用户只在三个场景介入
- 提需求("给 NFC 加 PN532 驱动")
- 烧录到开发板(USB 接线、按 Loader 键)
- 判断结果("这效果对不对")
9.5 分支策略
main ← 保护分支,只接受编译+烧录测试通过的代码
├─ dev/mac ← Mac 端 Claude 日常开发推这里
├─ feature/lcd-st77916-mipi ← 可选:大功能单独拉 feature 分支
└─ feature/nfc-pn532
合并原则:
- dev/mac 的改动在虚拟机编译通过 + 烧录验证 OK 后,才合并回 main
- 合并命令(在虚拟机端或 Mac 端都行):
git checkout main git merge dev/mac --no-ff -m "集成 NFC PN532 驱动" git push origin main
9.6 项目级 CLAUDE.md(关键)
在仓库根目录放一个 CLAUDE.md,新的 Claude 对话打开项目时自动加载,无需用户再解释工作流。
CLAUDE.md 内容要点(完整版见仓库):
- 项目本质(硬件、内核版本、开发类型)
- 双端架构(Mac 路径 + VM 路径)
- 关键命令(git push、SSH、编译)
- 全自动闭环流程
- 只可修改的目录清单
- 环境约束(case-sensitive 卷、不能 >100MB 文件等)
- 踩坑速查
- 用户偏好(中文、代码风格、响应风格)
9.7 本方案踩过的大坑总结
坑 1:ClashX 增强模式对 SSH 协议干扰
Mac 上 ClashX Pro 的 TUN 模式把 ssh.github.com:443 劫持到 fake-ip(198.18.x.x),但 SSH 协议在 banner exchange 阶段被切(Connection timed out during banner exchange),而 HTTPS 到同一域名却能通。
原因推测:某些节点对非 HTTP/HTTPS 流量做 DPI 过滤。
规避:Mac 端 Git 操作只用 HTTPS + PAT,不用 SSH。 虚拟机端没这问题(Tailscale 直连,没经过 Mac ClashX)。
坑 2:ClashX 的 7890 端口虽然开着但实际不转发
Mac ClashX Pro 当前配置下:
- ✅ TUN 透明代理工作(fake-ip 路径)
- ❌ 7890 HTTP 代理端口虽然能
nc -z成功但不实际转发(curl -x 超时) - ❌ 7891 SOCKS5 没开
意味着所有"显式指定代理"的方案(git config http.proxy、SSH ProxyCommand、proxychains)都不能用。
规避:依赖 TUN 透明路径,不用显式代理。
坑 3:default APFS 与 Linux 内核大小写冲突
macOS 主盘默认 APFS 是 case-insensitive,Linux 内核源码(netfilter 等)有大小写同名文件,checkout 时后者覆盖前者,git status 永远显示 8 个 .h 文件 modified 且不可还原。
规避:case-sensitive APFS 卷。见 9.2.1。
坑 4:GitHub ZIP 下载不含 .git
用户若因网络问题改用 GitHub Web 下载 ZIP(带 -main 后缀),后续无法 git push。必须 git clone 才有 .git/。
坑 5:SMB 挂载 Mac Finder 污染 ._* 和 .DS_Store
Mac Finder 访问 SMB 共享时会写入 AppleDouble 元数据。
规避:.gitignore 已加规则 ._* + .DS_Store。
坑 6:虚拟机 mihomo TUN 无法访问 Anthropic
即使在虚拟机内装 mihomo 跑 TUN 模式(透明代理),Anthropic 对机场 IP 的 API 级风控仍生效(TLS 握手成功但 POST 请求静默被丢)。
规避:根本不在虚拟机用 Claude。用本节方案。
9.8 性能参考(2026-04-23 实测)
| 指标 | 数值 |
|---|---|
| 仓库 clone 大小 | ~640 MB(.git) + ~2.3 GB(work tree)= 2.9 GB |
| Mac 端 clone 速度 | 17.5 MiB/s(走 ClashX TUN,GitHub HTTPS) |
| Mac → GitHub push 延迟 | 秒级(小改动) |
| 虚拟机 git pull 速度 | MB/s 级(Tailscale + SSH ed25519 over 443 直连) |
| VS Code 打开 case-sensitive 卷项目 | 索引 < 30 秒(本地速度) |
文档版本: 3.2 最后更新: 2026-04-23 适用系统: Ubuntu 20.04/22.04 LTS (x86_64), macOS 网络环境: 跨网络远程开发(Tailscale + ClashX Pro 共存 + GitHub SSH 443) 主要变更(v3.2):
- 新增第九部分:Mac 本地 + 虚拟机编译 的双端开发工作流(推荐,规避 Anthropic 对机场 IP 的风控)
- 9.1 分析为什么不能在虚拟机内用 Claude
- 9.2 Mac 端环境配置:case-sensitive APFS 卷(避免 Linux 内核大小写冲突)+ HTTPS+PAT+Keychain 免密
- 9.3 虚拟机端 git SSH 配置(延续 v3.1 的 8.7)
- 9.4 Claude 自动化开发闭环流程(Mac 改 → push → SSH 虚拟机编译 → 看错误自修 → 循环)
- 9.5 分支策略(main / dev/mac / feature/*)
- 9.6 项目级 CLAUDE.md 机制(新对话零摩擦接手)
- 9.7 本方案全部踩坑:ClashX SSH 劫持、7890 代理端口不转发、APFS 大小写、mihomo 绕不过 Anthropic 风控等
- 9.8 实测性能数据
主要变更(v3.1):
- 4.5 新增 SMB 挂载方式(Mac 端轻量文件访问方案)与 Remote-SSH 对比
- 8.7 首次推送 GitHub 改为 SSH 密钥为主,PAT 降为备选
- 8.10 新增首次推送踩坑实录(单文件 100MB 限制、Git 去重陷阱、rwnd 瓶颈等)
- 8.11 新增首次推送实测数据