LuoTianyi_HOLOMAIN/Tailscale + SSH 远程开发操作指南.md
Rdzleo 6517805884 Tailscale+SSH 指南 v3.2:新增双端开发工作流(第九部分)
记录 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 实测性能数据
2026-04-23 13:58:03 +08:00

71 KiB
Raw Permalink Blame History

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 DNS198.18.0.2)冲突。

典型表现:浏览器能访问 YouTube但 Claude Code 等终端应用无法连接。

解决方法: 关闭 Tailscale DNS 接管。

操作步骤:

  1. 点击 Mac 菜单栏 Tailscale 图标
  2. 进入 Preferences偏好设置
  3. 找到 "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 验证清单

  1. 开启 ClashX Pro 增强模式 + 选择代理节点
  2. 开启 Tailscale
  3. 浏览器访问 YouTube → VPN 正常
  4. 终端 ssh zhangwenqi@100.123.82.91 → Tailscale SSH 正常
  5. 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 单独编译

编译时如果报错找不到工具链,按以下方式排查修复:

  1. 确认报错中缺少的具体工具链名称
  2. 搜索 Linaro 官网下载对应版本:https://releases.linaro.org/components/toolchain/binaries/
  3. 解压到 Docker 容器的 /opt/toolchains/ 目录:
    # 在容器内
    cd /opt/toolchains
    wget <工具链下载地址>
    tar -xf <工具链压缩包>
    
  4. 或者将工具链放到 ~/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 开发板后:

  1. 将镜像文件拷贝到 Windows
  2. 使用 RKDevTool(瑞芯微官方烧录工具)烧录
  3. 开发板进入 Loader 模式(按住 Recovery 按键上电)
  4. 在 RKDevTool 中加载各 img 文件烧录

3.7 OrangePi CM5 Android 13 编译环境(基于 Ubuntu 22.04 Docker

OrangePi CM5 BaseRK3588S使用 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.bintrust.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 内核 + ramdiskboot 分区)
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 会一次性写入所有分区,等于给开发板全新刷系统。

烧录步骤:

  1. 从虚拟机拷贝 update.img2.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/
    
  2. Windows 电脑下载并安装 RKDevTool(瑞芯微官方烧录工具)

  3. OrangePi CM5 进入 MaskROM 模式

    • 按住板子上的 Recovery / MaskROM 按键
    • 插入 USB 线到电脑
    • 松开按键
  4. RKDevTool 界面切换到 升级固件 标签

  5. 点击 固件 按钮,选择 update.img

  6. 点击 升级 按钮,等待烧录完成(约 2-5 分钟)

  7. 烧录完成后开发板自动重启进入 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.img1-2 分钟)
  • 改 Bootloader → 单独烧 uboot.img + MiniLoaderAll.bin
  • 不确定或要彻底刷新 → 烧 update.img2-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 buildubuntu: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 端配置(一次性)

  1. Clash for Windows → 开启 "允许局域网连接入Clash"Allow LAN
  2. 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推荐用于驱动开发

  1. VS Code 安装 Remote - SSH 扩展
  2. Cmd+Shift+P → "Remote-SSH: Connect to Host"
  3. 选择 linux-vm
  4. 连接成功后打开文件夹 /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 共享目录:

挂载方式

  1. Finder → ⌘+K(连接到服务器)
  2. 输入:smb://100.123.82.91
  3. 输入虚拟机用户名/密码
  4. 选择要挂载的共享目录,挂载点位于 /Volumes/<share_name>

挂载成功后 VS Code 可直接打开挂载目录,像本地文件一样编辑。

适用场景(轻量用途)

用途 可行性
临时查看/编辑几个配置文件
快速预览源码结构
Finder 里拖文件到 Mac 备份
打开整个 84GB SDK 做索引开发 ⚠️ 首次索引慢
跑编译 / 烧录 / git 大仓库操作 必须 SSH 到虚拟机
让 Claude Code 自动编译调试 Mac shell 无 Linux 工具链

SMB 方案关键局限

  1. 文件访问 ≠ 执行环境SMB 只能让 Mac 看到文件,无法在 Mac 上跑 Android 编译(缺 Rockchip 工具链、Docker、特定 Python/libncurses5 等依赖)
  2. 符号链接支持有限Android SDK 含大量符号链接SMB 可能表现异常
  3. Git 操作不稳定:文件锁语义差异,git status 在 SMB 挂载目录上不可靠
  4. 文件系统大小写macOS 默认大小写不敏感 + Linux 大小写敏感,可能冲突
  5. .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导入虚拟机

  1. 新电脑安装 VMware Workstation 17(或更高版本)
  2. VMware → 文件 → 打开 → 选择 .ovf 文件
  3. 选择虚拟机存放位置(建议放在 SSD 上)
  4. 等待导入完成
  5. (可选)调整虚拟机参数:右键虚拟机 → 设置
    • 内存:建议 16GB+Android 编译非常吃内存)
    • CPU建议分配 8 核+
    • 网络:选择桥接模式(与宿主机同网段)

步骤 2启动虚拟机并恢复网络

  1. 启动虚拟机,登录(用户: zhangwenqi

  2. 检查网络:

    ip addr show    # 确认获取到 IP
    ping 8.8.8.8    # 测试内网连通性
    
  3. 恢复 Tailscale

    sudo systemctl status tailscaled    # 检查 Tailscale 服务
    sudo tailscale up                   # 重新认证(可能需要)
    tailscale ip -4                     # 记录新的 Tailscale IP
    
  4. 更新代理配置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-gnueabihf99MB
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.850MB
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-update96MB
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-gnu106MB
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-elf51MB
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导入虚拟机

  1. 新电脑安装 VMware Workstation 17
  2. VMware → 文件 → 打开 → 选择 .ovf 文件
  3. 选择存放位置,等待导入
  4. 调整参数(右键 → 设置):内存 16GB+CPU 8 核+,网络桥接模式

步骤 2恢复网络和代理

  1. 启动虚拟机,检查网络
  2. 更新 /etc/profile.d/proxy.sh 中的 Windows IP如果 IP 变化)
  3. Windows 安装 Clash开启 Allow LAN防火墙放行 7890
  4. 安装 TailscaleWindows + 虚拟机),同账号登录

步骤 3恢复 SDK 并对齐 Dockerfile

  1. 拷贝 SDK 压缩包到虚拟机 ~/Radxa_CM5/SDK_Android/
  2. 按上方 步骤 44.1~4.8 对齐官方 Dockerfile 环境
  3. 解压 SDK 并编译验证

步骤 4配置 Mac 远程开发(如有)

按第四部分重新配置 Mac 端 SSH 和 Tailscale。

5.3 新电脑是 Mac远程控制 Windows 开发)

Mac M 芯片无法运行 x86 虚拟机,采用 Mac 远程控制 Windows 方案。

前提条件

  • Windows 电脑保持运行(作为编译服务器)
  • Windows 上已按 5.2 完成虚拟机恢复和 Dockerfile 对齐
  • Mac 和 Windows 都安装 Tailscale

步骤 1Windows 端配置

  1. 按 5.2 完成虚拟机导入、网络恢复、SDK 恢复、Dockerfile 对齐

  2. 安装 TailscaleWindows 版),用同一账号登录

  3. 安装 Clash for Windows导入订阅开启 "允许局域网连接入Clash" 防火墙放行:

    netsh advfirewall firewall add rule name="Clash Proxy" dir=in action=allow protocol=tcp localport=7890
    
  4. 启动虚拟机,确认服务正常:

    tailscale status
    sudo systemctl status ssh
    curl -I https://www.google.com    # 验证外网代理
    docker images android-builder     # 验证 Docker 镜像
    
  5. 可选设置虚拟机开机自启VMware → 编辑 → 首选项 → 共享虚拟机 → 开机启动

步骤 2新 Mac 端配置

  1. 安装 TailscaleApp Store用同一账号登录

  2. 安装 ClashX Pro导入订阅

  3. 配置 ClashX Pro 与 Tailscale 共存(按第二部分操作):

    • 添加 Tailscale 直连规则到代理配置
    • 关闭 Tailscale DNS Settings
  4. 生成 SSH 密钥并部署:

    ssh-keygen -t ed25519 -C "new-mac-to-linux-vm"
    ssh-copy-id zhangwenqi@<虚拟机Tailscale_IP>
    

    注意Tailscale IP 可能与之前不同,在虚拟机中执行 tailscale ip -4 确认。

  5. 配置 SSH config~/.ssh/config

    Host linux-vm
        HostName <虚拟机Tailscale_IP>
        User zhangwenqi
        Port 22
        ServerAliveInterval 30
        TCPKeepAlive yes
        ConnectTimeout 60
    
  6. 安装 VS Code + Remote-SSH 扩展

  7. 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

日常使用流程

  1. Windows 电脑开机 → 虚拟机自动启动(或手动启动)
  2. Mac 打开 Tailscale + ClashX Pro
  3. VS Code → Remote-SSH → linux-vm
  4. 打开 /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 后失效

检查两个配置:

  1. ClashX Pro 配置文件中 Tailscale 直连规则是否存在(订阅更新会覆盖)
  2. 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/
  • 远程仓库GitHubOrangePi_CM5_ProjectPrivate
  • 首次推送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 首次推送到 GitHubSSH 密钥方式,推荐)

当前仓库: git@github.com:Leo-z8/OrangePi_CM5_Project.gitPrivate 认证方式: 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 添加:

  • TitleOrangePi-VM-Linux
  • Key typeAuthentication 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_ProjectPrivate
SDK 原始源码分卷9个 tar.gz Windows 电脑(永久保留,不能删)
Docker 镜像定义 ~/OrangePi_CM5/docker/Dockerfile
编译产物update.img 按需保留,体积大不进 Git
.gitignore 排除的大文件 需单独备份(见 8.10

8.10 GitHub 推送踩坑实录2026-04-22 首次推送)

踩坑 1GitHub 单文件 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 只列出一个 blobls-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 规则时默认用通配符。

踩坑 4HTTPS + 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 只用一次,也要:

  1. 推送完成后到 https://github.com/settings/tokens 立即 Revoke
  2. rm -f ~/.git-credentials
  3. git 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'

# 排除所有预装 APKGitHub 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不经 Clashssh.github.com:443
实测速度 4-6 MiB/sSSH 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 CLINode 20 + @anthropic-ai/claude-code 2.1.118)后,启动永远卡在 "Checking connectivity..." 或 ECONNRESET
  • 新版 Claude CLI2.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.hmacOS 默认 APFS 是 case-insensitive会把这些文件合并成一个导致 git status 永远脏且无法还原。

解决:建一个专门的 case-sensitive 卷放 Linux 项目。

  1. 打开 磁盘工具
  2. 左侧选 Container disk3Macintosh HD 的父容器)
  3. 菜单「编辑 → 添加 APFS 卷...」
  4. 弹窗中:
    • 名称:LinuxDev
    • 格式APFS区分大小写 ← 关键
    • 大小不限制APFS 容器自动共享空间)
  5. 确认 / 添加

验证成功:

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 再编
        (自循环)

用户只在三个场景介入

  1. 提需求("给 NFC 加 PN532 驱动"
  2. 烧录到开发板USB 接线、按 Loader 键)
  3. 判断结果("这效果对不对"

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 本方案踩过的大坑总结

坑 1ClashX 增强模式对 SSH 协议干扰

Mac 上 ClashX Pro 的 TUN 模式把 ssh.github.com:443 劫持到 fake-ip198.18.x.x),但 SSH 协议在 banner exchange 阶段被切Connection timed out during banner exchange),而 HTTPS 到同一域名却能通。

原因推测:某些节点对非 HTTP/HTTPS 流量做 DPI 过滤。

规避Mac 端 Git 操作只用 HTTPS + PAT,不用 SSH。 虚拟机端没这问题Tailscale 直连,没经过 Mac ClashX

坑 2ClashX 的 7890 端口虽然开着但实际不转发

Mac ClashX Pro 当前配置下:

  • TUN 透明代理工作fake-ip 路径)
  • 7890 HTTP 代理端口虽然能 nc -z 成功但不实际转发curl -x 超时)
  • 7891 SOCKS5 没开

意味着所有"显式指定代理"的方案(git config http.proxy、SSH ProxyCommand、proxychains都不能用。

规避:依赖 TUN 透明路径,不用显式代理。

坑 3default APFS 与 Linux 内核大小写冲突

macOS 主盘默认 APFS 是 case-insensitiveLinux 内核源码netfilter 等有大小写同名文件checkout 时后者覆盖前者,git status 永远显示 8 个 .h 文件 modified 且不可还原。

规避case-sensitive APFS 卷。见 9.2.1。

坑 4GitHub ZIP 下载不含 .git

用户若因网络问题改用 GitHub Web 下载 ZIP-main 后缀),后续无法 git push。必须 git clone 才有 .git/

坑 5SMB 挂载 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 GBwork tree= 2.9 GB
Mac 端 clone 速度 17.5 MiB/s走 ClashX TUNGitHub 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 新增首次推送实测数据