feat: 新增小爱音箱交叉编译环境

This commit is contained in:
Del Wang
2025-12-31 22:53:33 +08:00
committed by Del
parent 2ff214c873
commit 7e80d6cd74
9 changed files with 208 additions and 158 deletions
+2
View File
@@ -0,0 +1,2 @@
squashfs-root
root.squashfs
+88
View File
@@ -0,0 +1,88 @@
# syntax=docker/dockerfile:1.4
FROM ubuntu:20.04
# 1. 环境变量合并,减少层数
ENV DEBIAN_FRONTEND=noninteractive \
PATH="/opt/toolchain/bin:/root/.cargo/bin:${PATH}" \
PKG_CONFIG_ALLOW_CROSS=1 \
PKG_CONFIG_SYSROOT_DIR=/opt/sysroot \
PKG_CONFIG_LIBDIR=/opt/sysroot/usr/lib/pkgconfig:/opt/sysroot/lib/pkgconfig \
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
CC_armv7_unknown_linux_gnueabihf="arm-linux-gnueabihf-gcc --sysroot=/opt/sysroot"
# 2. 基础依赖与多架构源配置合并
# 使用 --mount=type=cache 优化 apt 操作
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/deb http/deb [arch=amd64] http/g' /etc/apt/sources.list && \
echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal main restricted universe multiverse" > /etc/apt/sources.list.d/armhf.list && \
echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/armhf.list && \
dpkg --add-architecture armhf && \
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git build-essential qemu-user-static pkg-config libssl-dev xz-utils squashfs-tools \
crossbuild-essential-armhf libasound2-dev:armhf libopus-dev:armhf libssl-dev:armhf && \
# 删除 lists 文件以减小体积
rm -rf /var/lib/apt/lists/*
# 3. 安装 Linaro Toolchain & Rust
# 将下载和清理放在一层,减少体积
RUN mkdir -p /opt/toolchain && \
curl -fSL https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz | tar -xJ -C /opt/toolchain --strip-components=1 && \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable && \
rustup target add armv7-unknown-linux-gnueabihf
WORKDIR /app
# 4. 脚本与 Rootfs 处理
# 使用 --mount=bind 减少一次 COPY 导致的镜像体积增加
COPY run.sh runtime.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/run.sh /usr/local/bin/runtime.sh
# 处理 root.squashfs:利用临时挂载或直接解压并删除
RUN --mount=type=bind,source=root.squashfs,target=/app/root.squashfs \
mkdir -p /opt/sysroot && unsquashfs -d /opt/sysroot -f /app/root.squashfs
# 5. 构建混合 Sysroot 逻辑优化
# 将所有文件操作合并到一个 RUN 块,减少 Metadata 冗余
RUN set -e; \
# 1. 自动为共享库创建 .so 软链接
find /opt/sysroot/lib /opt/sysroot/usr/lib -name "lib*.so.*" | while read -r src; do \
link_name=$(echo "$src" | sed 's/\.so\..*$/.so/'); \
if [ ! -e "$link_name" ]; then \
ln -s "$(basename "$src")" "$link_name"; \
fi; \
done; \
# 2. 拷贝 crt*.o 文件
find /opt/toolchain/arm-linux-gnueabihf/libc -name "*crt*.o" -exec cp {} /opt/sysroot/usr/lib/ \; ; \
# 3. 拷贝 libc_nonshared.a
cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libc_nonshared.a /opt/sysroot/usr/lib/; \
if [ -f /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libpthread_nonshared.a ]; then \
cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libpthread_nonshared.a /opt/sysroot/usr/lib/; \
fi; \
# 4. 生成 Linker Scripts
printf "OUTPUT_FORMAT(elf32-littlearm)\nGROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux-armhf.so.3 ) )\n" > /opt/sysroot/usr/lib/libc.so; \
if [ -f /opt/sysroot/lib/libpthread.so.0 ]; then \
printf "OUTPUT_FORMAT(elf32-littlearm)\nGROUP ( /lib/libpthread.so.0 /usr/lib/libpthread_nonshared.a )\n" > /opt/sysroot/usr/lib/libpthread.so; \
fi; \
# 5. 注入头文件和 pkgconfig
cp -r /usr/include/alsa /opt/sysroot/usr/include/ 2>/dev/null || true; \
cp -r /usr/include/opus /opt/sysroot/usr/include/ 2>/dev/null || true; \
if [ -d /usr/include/arm-linux-gnueabihf ]; then \
cp -r /usr/include/arm-linux-gnueabihf/* /opt/sysroot/usr/include/ 2>/dev/null || true; \
fi; \
if [ -d /usr/lib/arm-linux-gnueabihf/pkgconfig ]; then \
cp -r /usr/lib/arm-linux-gnueabihf/pkgconfig/* /opt/sysroot/usr/lib/pkgconfig/ 2>/dev/null || true; \
fi
# 6. 设置 Rust 链接参数
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="\
-C link-arg=--sysroot=/opt/sysroot \
-C link-arg=-Wl,-dynamic-linker,/lib/ld-linux-armhf.so.3 \
-C link-arg=-L/opt/sysroot/usr/lib \
-C link-arg=-L/opt/sysroot/lib \
-C link-arg=-Wl,-rpath-link,/opt/sysroot/usr/lib \
-C link-arg=-Wl,-rpath-link,/opt/sysroot/lib"
CMD ["/bin/bash"]
+37
View File
@@ -0,0 +1,37 @@
# Makefile for open-xiaoai-runtime
# 使用 host.docker.internal 访问宿主机的代理端口(加速构建)
# 注意:代理软件需要开启 "Allow LAN"(允许局域网连接)
PROXY := http://host.docker.internal:7890
IMAGE_NAME := idootop/open-xiaoai-runtime:oh2p
# 显示帮助信息
help:
@echo "Available targets:"
@echo " make build - 构建交叉编译环境镜像"
@echo " make run-x86 - 进入交叉编译环境(Ubuntu x86_64"
@echo " make run-armv7 - 进入小爱音箱模拟环境(OpenWrt ARMv7"
@echo " make help - 显示此帮助信息"
@echo ""
@echo "Variables:"
@echo " PROXY=$(PROXY)"
@echo " IMAGE_NAME=$(IMAGE_NAME)"
# 构建交叉编译环境镜像
build:
@echo "Building Docker image: $(IMAGE_NAME)..."
docker build \
--build-arg http_proxy=$(PROXY) \
--build-arg https_proxy=$(PROXY) \
-t $(IMAGE_NAME) .
# 进入交叉编译环境(Ubuntu x86_64
run-x86:
@echo "Entering cross-compilation environment (Ubuntu x86_64)..."
docker run --rm -it --privileged $(IMAGE_NAME)
# 进入小爱音箱模拟环境(OpenWrt ARMv7
run-armv7:
@echo "Entering XiaoAi speaker simulation environment (OpenWrt ARMv7)..."
docker run --rm -it --privileged $(IMAGE_NAME) run
+83
View File
@@ -0,0 +1,83 @@
# Open-XiaoAI Runtime
小爱音箱 ARMv7 交叉编译 + 模拟运行环境(基于 OH2P v1.58.1 固件)
## 概述
小爱音箱使用的 Linux 系统较旧,glibc 版本为 2.25,需要使用 `gcc-linaro-7.5.0-2019.12` 工具链才能编译出兼容的程序。
```bash
root@OH2P:~# uname -a
Linux OH2P 4.9.61 #1 SMP PREEMPT Fri Jun 27 06:11:47 2025 aarch64 GNU/Linux
root@OH2P:~# file /lib/libc-2.25.so
ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, stripped
```
## Rust 交叉编译示例
编译产物可直接在小爱音箱上运行。
```bash
cd /path/to/your-rust-project
# 使用预先构建好的 Docker 环境进行交叉编译
docker run --rm idootop/open-xiaoai-runtime:oh2p \
-v $(shell pwd):/app \
cargo build --target armv7-unknown-linux-gnueabihf --release
```
## 环境说明
**交叉编译环境**Ubuntu 20.04 x86_64
- Linaro GCC 7.5.0 (arm-linux-gnueabihf)
- Rust 稳定版 + armv7-unknown-linux-gnueabihf target
- 包含 ALSA、Opus、OpenSSL 等系统库(ARMv7 版本)
- 从小爱音箱固件提取的完整 Sysroot
**模拟运行环境**QEMU 用户态)
- ARMv7 架构(32-bit ARM
- OpenWrt 定制系统 + glibc 2.25
- 使用小爱音箱真实 rootfs
- 不支持音频录制/播放等操作,需在小爱音箱真机运行
## 自定义配置
### 更换固件
默认使用 OH2P v1.58.1 固件。如需使用其他型号和版本:
```bash
# 克隆代码
git clone https://github.com/idootop/open-xiaoai.git
# 进入当前项目根目录
cd packages/runtime
# 【重要】将新的 `root.squashfs` 放到当前目录
# 构建镜像
make build
```
PS:你可以修改 `Makefile` 中的代理地址加速镜像构建(代理软件需开启局域网访问)。
```makefile
PROXY := http://host.docker.internal:7890 # 对应 0.0.0.0:7890
```
### 其他 Makefile 命令
```bash
# 构建镜像
make build
# 进入交叉编译环境(Ubuntu x86_64
make run-x86
# 进入小爱音箱模拟环境(OpenWrt ARMv7
make run-armv7
```
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
set -e
# 定义 sysroot 目录
SYSROOT_DIR="/opt/sysroot"
# 检查第一个参数。如果在 sysroot 下存在该文件,则自动补全路径
# 比如 `run /bin/sh` 会被转换为 `run /opt/sysroot/bin/sh`
if [ -n "$1" ] && [ -f "$SYSROOT_DIR$1" ]; then
CMD="$SYSROOT_DIR$1"
shift
set -- "$CMD" "$@"
fi
if [ $# -eq 0 ]; then
# 如果没有提供参数,则默认在 sysroot 中启动 shell
exec runtime
else
exec qemu-arm-static -L "$SYSROOT_DIR" "$@"
fi
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
SYSROOT="/opt/sysroot"
QEMU_BIN="/usr/bin/qemu-arm-static"
echo "正在准备 OpenWrt 模拟环境..."
# 1. 拷贝 QEMU 静态二进制文件
cp "$QEMU_BIN" "$SYSROOT/usr/bin/"
# 2. 挂载必要文件系统
mount_fs() {
for dir in proc sys dev dev/pts tmp; do
mkdir -p "$SYSROOT/$dir"
if ! mountpoint -q "$SYSROOT/$dir"; then
if [[ "$dir" == "proc" ]]; then
mount -t proc proc "$SYSROOT/$dir"
elif [[ "$dir" == "sys" ]]; then
mount -t sysfs sysfs "$SYSROOT/$dir"
else
mount --bind "/$dir" "$SYSROOT/$dir"
fi
fi
done
}
mount_fs
# 3. 执行进入命令
# 注意:OpenWrt 默认没有 bash,通常使用 /bin/sh (busybox)
echo "------------------------------------------------"
echo "成功进入 OpenWrt 环境。输入 'exit' 退出。"
echo "------------------------------------------------"
chroot "$SYSROOT" /usr/bin/$(basename $QEMU_BIN) /bin/sh