# 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:` 下方、第一条规则之前插入: ```yaml 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 接管。 操作步骤: 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 安装 ```bash # 安装 curl -fsSL https://tailscale.com/install.sh | sh # 启动并认证(浏览器打开输出的链接登录) sudo tailscale up # 查看分配的 IP tailscale ip -4 ``` ## 3.2 SSH 服务 ```bash # 安装(如未安装) sudo apt update && sudo apt install openssh-server -y # 启动 sudo systemctl enable --now ssh # 检查状态 sudo systemctl status ssh ``` ## 3.3 Docker 安装 ```bash # 添加 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 在国内无法直接访问,需要配置镜像加速器: ```bash 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 官方文档,使用清华镜像源。 ```bash cd ~/Radxa_CM5/docker sudo docker build -t android-builder:12.x . ``` 构建完成验证: ```bash 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/` 目录: ```bash # 在容器内 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 ```bash 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 引用行。 ```bash 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 ``` 验证: ```bash 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 内核配置文件。 ```bash 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 杀死进程。 ```bash # 创建 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 编译环境 ```bash # 注意挂载的是解压后的 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 执行编译 ```bash # 在 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),需要手动打包: ```bash # 在容器内执行 ./mkimage.sh ``` ### 3.6.7 验证编译结果 ```bash 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 退出和重新进入容器 ```bash # 退出容器 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 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 校验 ```bash cd ~/OrangePi_CM5/Aandroid_OrangePi md5sum -c md5sum # 9 个文件全部显示"成功/确定"才能继续 ``` ### 3.7.4 合并压缩包(重要) **经实测:官方文档的管道流式解压(`cat ... | tar -xzf -`)会在某个分卷边界出错** (已解压 46GB 后报 `gzip: invalid compressed data--format violated`)。 **推荐做法:先合并成单个文件,再解压。** ```bash 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 解压源码 ```bash # 解压到当前目录,产生 Android_13/ 目录 tar -xzf Android_13.tar.gz # 解压后预计 80-100GB,耗时约 20-30 分钟 du -sh Android_13/ ``` 解压成功后可以删除合并文件和分卷文件节省空间: ```bash # 可选:保留分卷和 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 的依赖列表不完整,本版本已加入所有实际踩坑后补齐的包。 ```dockerfile # 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 镜像 ```bash cd ~/OrangePi_CM5/docker sudo docker build -t android-builder-orangepi:13.x . # 耗时约 5-10 分钟 ``` 验证镜像: ```bash sudo docker images android-builder-orangepi # 预期:TAG=13.x, SIZE 约 2GB ``` ### 3.7.8 进入 Docker 编译环境 ```bash 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) | 推荐全量编译命令: ```bash # 容器内或后台 docker run -d 执行 ./make.sh -FMu -b orangepicm5 --gapps ``` 实测耗时:**6 小时 11 分钟**(VMware 虚拟机 8 核 20GB RAM + 9GB Swap)。 推荐后台执行(避免 SSH 断开中断): ```bash 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: open file failed,err=2! Make firmware FAILED ``` 需要手动执行两步修复: **修复步骤 1:合成 Rockchip U-Boot(生成 MiniLoaderAll + uboot.img 新版)** ```bash # 启动临时容器执行 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)** ```bash # 在虚拟机宿主机执行(非容器内) cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/ sudo cp -a uboot.img trust.img ``` **修复步骤 3:重新打包(跳过编译,只做 mkimage + update.img)** ```bash 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/` 目录生成所有镜像: ```bash 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 会一次性写入所有分区,等于给开发板全新刷系统。 烧录步骤: 1. 从虚拟机拷贝 `update.img`(2.3GB)到 Windows 电脑: ```bash # 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.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 容器管理 ```bash # 退出容器(编译中断) 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 完整编译流水线(新电脑复现) 如果换新电脑后需要重新编译,按以下顺序: ```bash # === 准备阶段 === # 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 执行): ```powershell netsh advfirewall firewall add rule name="Clash Proxy" dir=in action=allow protocol=tcp localport=7890 ``` ### Linux 虚拟机端配置(已完成,开机自动生效) 代理配置写入 `/etc/profile.d/proxy.sh`,每次登录自动加载: ```bash 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" ``` ### 验证外网 ```bash 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 地址: ```bash 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 容器内不生效,启动容器时需传入环境变量: ```bash 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 密钥 ```bash ssh-keygen -t ed25519 -C "mac-to-linux-vm" # 一路回车使用默认设置 ``` ## 4.2 部署公钥到虚拟机 ```bash 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/` 挂载成功后 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. 检查网络: ```bash ip addr show # 确认获取到 IP ping 8.8.8.8 # 测试内网连通性 ``` 3. 恢复 Tailscale: ```bash sudo systemctl status tailscaled # 检查 Tailscale 服务 sudo tailscale up # 重新认证(可能需要) tailscale ip -4 # 记录新的 Tailscale IP ``` 4. 更新代理配置(Windows IP 可能变化): ```bash # 在 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 ```bash # 将 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 和基础镜像正常 ```bash docker images android-builder # 预期看到 android-builder:12.x 镜像 ``` #### 4.2 确认代理可用(下载工具链需要外网) ```bash curl -I https://releases.linaro.org/ # 预期 HTTP 200 ``` 如果不通,先按 3.7 节配置虚拟机代理。 #### 4.3 下载 5 套交叉编译工具链 ```bash 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 解压工具链 ```bash 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 行: ```dockerfile 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 替换: ```bash 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 镜像 ```bash cd ~/Radxa_CM5/docker sudo docker build -t android-builder:12.x . # 耗时约 10-15 分钟 ``` #### 4.7 验证环境完整 ```bash 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 清理下载的压缩包 ```bash 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. 安装 Tailscale(Windows + 虚拟机),同账号登录 ### 步骤 3:恢复 SDK 并对齐 Dockerfile 1. 拷贝 SDK 压缩包到虚拟机 `~/Radxa_CM5/SDK_Android/` 2. 按上方 **步骤 4(4.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 ### 步骤 1:Windows 端配置 1. 按 5.2 完成虚拟机导入、网络恢复、SDK 恢复、Dockerfile 对齐 2. 安装 Tailscale(Windows 版),用同一账号登录 3. 安装 Clash for Windows,导入订阅,开启 "允许局域网连接入Clash" 防火墙放行: ```powershell netsh advfirewall firewall add rule name="Clash Proxy" dir=in action=allow protocol=tcp localport=7890 ``` 4. 启动虚拟机,确认服务正常: ```bash tailscale status sudo systemctl status ssh curl -I https://www.google.com # 验证外网代理 docker images android-builder # 验证 Docker 镜像 ``` 5. (可选)设置虚拟机开机自启:VMware → 编辑 → 首选项 → 共享虚拟机 → 开机启动 ### 步骤 2:新 Mac 端配置 1. 安装 Tailscale(App Store),用同一账号登录 2. 安装 ClashX Pro,导入订阅 3. 配置 ClashX Pro 与 Tailscale 共存(按第二部分操作): - 添加 Tailscale 直连规则到代理配置 - 关闭 Tailscale DNS Settings 4. 生成 SSH 密钥并部署: ```bash 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:验证 ```bash # 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 连不上 ```bash # Linux 端检查 sudo tailscale status sudo systemctl restart tailscaled sudo tailscale up # 诊断 tailscale netcheck ``` ## 6.2 SSH 连接被拒绝 ```bash # 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 被墙,检查镜像加速配置: ```bash 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 ``` --- # 第七部分:快速参考 ## 日常连接命令 ```bash # 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 虚拟机管理 ```bash # 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 白名单内容 ```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 日常工作流 ```bash # 在 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 回滚操作 ```bash # 查看历史 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 ``` ## 8.6 分支管理(推荐功能分支) ```bash # 创建功能分支 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 密钥 ```bash 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 ```bash cat ~/.ssh/github_ed25519.pub ``` 复制整行内容,到 https://github.com/settings/ssh/new 添加: - Title:`OrangePi-VM-Linux` - Key type:Authentication Key - Key:粘贴公钥 ### 步骤 4:测试连通性 ```bash ssh -T git@github.com # 预期输出:Hi Leo-z8! You've successfully authenticated ``` ### 步骤 5:切换仓库 remote 到 SSH ```bash 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 配置权限或临时应急时使用: ```bash # 配置 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 方式配置后完全免密: ```bash 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` ```bash # ❌ 错误方式:按 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` 用具体路径易遗漏,通配符更健壮 ```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 只用一次,也要: 1. 推送完成后到 https://github.com/settings/tokens 立即 Revoke 2. `rm -f ~/.git-credentials` 3. `git config --unset credential.helper` **SSH 方式无此风险**:私钥只存本地 ~/.ssh/,不会经过对话或日志。 ### 本次推送完整大文件排除流程(已落地) ```bash 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 | --- 文档版本: 3.1 最后更新: 2026-04-22 适用系统: Ubuntu 20.04/22.04 LTS (x86_64), macOS 网络环境: 跨网络远程开发(Tailscale + ClashX Pro 共存 + GitHub SSH 443) 主要变更(v3.1): - 4.5 新增 SMB 挂载方式(Mac 端轻量文件访问方案)与 Remote-SSH 对比 - 8.7 首次推送 GitHub 改为 SSH 密钥为主,PAT 降为备选 - 8.10 新增首次推送踩坑实录(单文件 100MB 限制、Git 去重陷阱、rwnd 瓶颈等) - 8.11 新增首次推送实测数据