基于 T2 (RK3568/ARM64) 设备上残留的构建脚本和工程文件推断 分析日期:2026-05-22
一、核心结论:一套 Go 代码,多平台交叉编译
极空间的软件体系采用 单一代码库 + Go 交叉编译 + 架构特定二进制资产 的策略,在 ARM (RK3568/RK3588) 和 x86 之间共享 90% 以上的代码。
┌──────────────────────────────────────────────────┐
│ 同一个 Git 仓库 │
│ │
│ main.go / zvideo.go / zalbum.go / ... │
│ (Go 源码,平台无关) │
│ │
│ msearchd/ zaitoolkit/ │
│ ├── arm/searchd ├── arm/* │
│ └── amd/searchd └── amd/* │
│ (C/C++ 二进制,预编译分支) │
│ │
│ build.sh <platform> <outdir> │
│ ├── z2/z2s → ARM64 + OpenWrt 工具链 │
│ ├── z4 → ARM64 (高端型号) │
│ └── default → AMD64 (x86) │
└──────────────────────────────────────────────────┘
二、两条构建流水线
在设备上发现两种构建入口,对应两个阶段:
build.sh — 本地开发构建
路径:/zspace/applications/services/<服务名>/build.sh
# 所有 build.sh 遵循统一模式
build.sh <platform_flag> <output_directory>
- Go 工具链:
/usr/local/go_zspace/<版本>/bin/go - 本地开发者在自己的机器上运行,产物部署到测试设备
AppStoreBuild.sh — Jenkins CI 构建
路径:/data_n001/appstore/pkg/<应用名>/AppStoreBuild.sh
export PATH=/var/lib/jenkins/tools/gos/1.24/bin:$PATH
GO_BIN=/var/lib/jenkins/tools/gos/1.24/bin/go
- Go 工具链在 Jenkins 节点上(
/var/lib/jenkins/tools/gos/) - 产物通过应用商店分发到用户设备
两者关系
| build.sh | AppStoreBuild.sh | |
|---|---|---|
| 位置 | 系统服务目录 | 用户应用包目录 |
| Go 路径 | /usr/local/go_zspace/ | /var/lib/jenkins/tools/gos/ |
| 用途 | 本地开发 / 系统级服务构建 | CI 构建 / 应用商店分发 |
| 产物分发 | OTA 系统更新 | App Store |
三、平台识别机制:设备 SN 前缀
所有 build.sh 通过传入的设备序列号前缀来决定编译目标:
# 来自 zalbumv2/build.sh
sn=$(echo $1 | tr 'A-Z' 'a-z')
if [[ "$sn" == "z02"* ]]; then ver1="z2" # Z2 系列
if [[ "$sn" == "t02"* ]]; then ver1="z2" # T2
if [[ "$sn" == "q02"* ]]; then ver1="z2" # Q2 系列
if [[ "$sn" == "ta21"* ]]; then ver1="z2" # TA21 系列
if [[ "$sn" == "z023"* ]]; then ver1="z4" # Z4 系列 (高端 ARM)
if [[ "$sn" == "za23"* ]]; then ver1="z4" # ZA23 系列
# 默认 else → x86/amd64
设备层级:
| 平台标识 | 对应设备 | 架构 | 特征 |
|---|---|---|---|
z2 | Z2, Z2S, T2, Q2, TA21 | ARM64 (RK3568) | 入门/便携 |
z2s | Z2S 特定变体 | ARM64 (RK3568) | 与 z2 编译参数相同 |
z4 | Z4, ZA23 | ARM64 (RK3588) | 高端 |
default | x86 机型 | AMD64 | PC 平台 |
四、Go 交叉编译策略
统一模式
# ARM 平台
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \
CXX=aarch64-openwrt-linux-g++ \
CC=aarch64-openwrt-linux-gcc \
go build -o $NAME
# x86 平台
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -o $NAME
Go 版本矩阵
设备上发现 4 个 Go 版本共存,各服务按需选择:
| Go 版本 | 服务数量 | 说明 |
|---|---|---|
| 1.20.2 | 24 | 较老服务,稳定版本 |
| 1.22.2 | 18 | 中间版本 |
| 1.23.3 | 14 | 包括 zdocker |
| 1.24.1 | 24 | 最新服务,包括 zfilev2 |
每个服务锁定一个 Go 版本,不强制统一升级。这是一种务实的单仓(monorepo)管理策略。
编译优化
t.sh(测试/本地构建脚本)中有额外步骤:
# UPX 压缩二进制,减小分发体积
upx -q -1 ./$NAME
说明他们关注 ARM 设备上的存储和传输体积。
五、架构特定资产的分发
纯 Go 代码可以交叉编译,但有些依赖是 C/C++ 的,必须预编译。build.sh 在构建时 按架构选择正确的二进制文件:
案例 1:zalbumv2 (相册)
msearchd/ zaitoolkit/
├── arm/searchd (ARM 版) ├── arm/* (ARM 版 AI 模型)
└── amd/searchd (x86 版) └── amd/* (x86 版 AI 模型)
Manticore Search 是 C++ 全文检索引擎,无法被 Go 交叉编译。构建时根据平台选择 arm/ 或 amd/ 目录。
案例 2:zfilev2 (文件管理)
gocryptfs_arm vs gocryptfs_x86 # Go 加密文件系统(但可能是 CGO 绑定)
7zz_arm vs 7zz_x86 # 7-Zip 压缩(C++)
libgphoto2.arm.tar.gz vs .x86.tar.gz # 相机连接的 C 库
模式总结
源代码仓库结构(推测):
<service>/
├── main.go ← 平台无关 Go 代码
├── build.sh ← 统一构建脚本
├── msearchd/ ← C/C++ 依赖
│ ├── arm/searchd ← ARM 预编译二进制
│ └── amd/searchd ← x86 预编译二进制
├── zaitoolkit/
│ ├── arm/* ← ARM 模型文件
│ └── amd/* ← x86 模型文件
└── ...
六、OpenWrt 工具链的运用
ARM 平台的 CGO 编译统一使用 OpenWrt 交叉工具链:
CXX=aarch64-openwrt-linux-g++
CC=aarch64-openwrt-linux-gcc
以及部分场景使用更具体的工具链:
# zfilev2 中用到(libgphoto2 的 CGO 编译)
CGO_CFLAGS="-I/opt/toolchain-aarch64_cortex-a53_gcc-8.2.0_glibc/..."
CGO_LDFLAGS="-L/opt/toolchain-aarch64_cortex-a53_gcc-8.2.0_glibc/..."
这暗示:
- 极空间可能用 OpenWrt 构建系统 生成整个 rootfs(包括内核、基础库)
- 在此之上,用 Go 构建上层的 ZOS 应用层
- 即:OpenWrt 打底 + Go 应用层 的混合架构
七、分发体系
OTA 系统更新
/zspace/update/pkg/
├── T020_SERVICE/ ← T2 设备的服务更新包
├── T020_SYSTEM/ ← T2 设备的系统更新包
├── Z022_SERVICE/ ← Z2 设备的服务更新包
└── Z022_SYSTEM/ ← Z2 设备的系统更新包
包名格式:<型号>_<类型>,型号和类型分离,同一服务可以为不同型号发布不同包。
完整性校验
每个服务目录都有一个 filehash.zspace.upgrade.json:
{
"service_md5": {
"conf/version": "1e2fb8e78fe269edd07f6f2216d46c0f",
"go-baseset": "d9f2280fc205846c10b79bf0be4d73b2",
"install.sh": "faa25c6c7940f930d70538c673a39726",
...
}
}
72 个服务都有此文件,OTA 升级前逐文件校验 MD5,确保包完整性。
版本号体系
所有服务统一版本格式:V0.2.YYYYMMDDNN
V0.2.2026012101
│ │ │
│ │ └── 当日构建序号(01-99)
│ └── 构建日期
└── 主版本.次版本
每日从 01 开始,同一天每多构建一次 +1。这意味着他们可能 每日构建 (nightly build) 甚至更频繁。
八、部署与安装
部署工具:ztools
自研 Go 工具,用于文件分发:
/zspace/shell/tool/ztools/ztools copy_dir <source> <target>
install.sh 脚本调用它来部署文件到目标路径。
安装脚本分级
| install.sh 类型 | 占比 | 含义 |
|---|---|---|
exit 0(空壳) | ~15% | 构建系统直接处理部署,install.sh 只做占位 |
| 简单 cp + systemctl | ~60% | 标准 Go 服务:复制文件 + 注册 systemd + 启动 |
| 复杂安装逻辑 | ~25% | samba (171行)、zfirewall (104行) 等需要大量系统配置的服务 |
九、完整的端到端推测流程
┌─────────────────────────────────────────────────────────┐
│ Git 单仓库 (Monorepo) │
│ │
│ services/ apps/ │
│ ├── go-baseset/ ├── photoAlbum/ │
│ ├── zdocker/ ├── video/ │
│ ├── zfilev2/ ├── music/ │
│ ├── ... ├── ... │
└────────────┬──────────────────────┬──────────────────────┘
│ │
┌────────▼────────┐ ┌───────▼──────────┐
│ build.sh │ │ AppStoreBuild.sh │
│ (本地开发) │ │ (Jenkins CI) │
│ │ │ │
│ go_zspace/1.x │ │ jenkins/gos/1.x │
└────────┬─────────┘ └───────┬────────────┘
│ │
┌────────▼──────────────────────▼──────────┐
│ build.sh <platform> <out> │
│ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ ARM64 │ │ ARM64 │ │ AMD64 │ │
│ │ (z2/z2s) │ │ (z4) │ │ (x86) │ │
│ │ │ │ │ │ │ │
│ │ CC=aarch │ │ (同 z2) │ │ 本地gcc │ │
│ │ 64-open │ │ 或更高 │ │ │ │
│ │ wrt-... │ │ 配置 │ │ │ │
│ └────┬─────┘ └────┬─────┘ └────┬────┘ │
│ │ │ │ │
│ └──────┬──────┴──────┬──────┘ │
└──────────────┼─────────────┼─────────────┘
│ │
┌────────▼──────┬──────▼────────┐
│ OTA 更新包 │ App Store 包 │
│ T020_SERVICE │ photoAlbum/ │
│ Z022_SERVICE │ video/ │
└──────┬────────┴──────┬────────┘
│ │
┌──────▼───────────────▼──────────┐
│ 用户设备 │
│ /zspace/applications/services/ │
│ /var/appstore/pkg/ │
└──────────────────────────────────┘
十、工程水平评估
做得好的
- Go 选型正确:一次编写,跨平台编译,ARM/x86 真正共享代码
- 统一构建约定:所有 build.sh 遵循相同接口
build.sh <arch> <out>,降低维护成本 - 完整性校验:每个服务都有 MD5 哈希清单,OTA 升级安全可靠
- Go 版本隔离:不强制统一升级,每个服务用自己验证过的版本
- 架构资产显式分离:
arm/vsamd/目录,一目了然
可以推敲的
- Go 版本碎片化:4 个版本并存,长期维护成本高。但这是 monorepo 的务实妥协
- C/C++ 依赖外挂:manticore、libgphoto2 等以二进制形式提交到 Git 仓库(或用 Git LFS 管理),而非从源码构建。牺牲了可复现性换取了构建速度
- build.sh 重复代码:每个服务的 build.sh 有 80% 相同逻辑(版本号生成、文件复制),没有抽取公共构建框架
- install.sh 空壳:
exit 0的 install.sh 说明部署逻辑散落在构建系统的其他地方,不够内聚
十一、关键推断
- 极空间几乎肯定是 Go Monorepo:所有 Go 服务在同一仓库,共享构建工具链
- 系统底层是 OpenWrt/Buildroot:
aarch64-openwrt-linux-*工具链表明 rootfs 构建用 OpenWrt,Go 应用层是上层建筑 - Jenkins 做 CI/CD:
/var/lib/jenkins路径直接暴露了 CI 系统 - Git LFS 管理大文件:
git lfs pull命令出现在 build.sh 中,用于管理预编译的 C/C++ 二进制 - 每日构建节奏:版本号按日期递增,暗示至少每天构建一次
- 没有 Makefile/Dockerfile:整个设备上找不到一个 Makefile(除了 PDF 解析库自带的),说明完全拥抱 Go 的构建范式,不使用传统 C/C++ 构建系统