[OpenHarmony7.0][环境][教程]OpenHarmony 7.0源码在Docker Ubuntu22.04编译环境搭建与踩坑记录

F. 前言

教程基于 OpenHarmony 7.0 Release(tag:OpenHarmony-v7.0-Release,以下简称 OH7),目标板 rk3568,Docker 容器 Ubuntu22.04 编译环境。

为什么做这个记录?整个环境搭建过程中,从一次极具误导性的 Permission denied: 'uv' 报错开始,一路排查到 hb 与 build.sh 的 PATH 装配差异、再到 taihe 组件的 setuptools_scm 版本解析崩溃,前前后后踩了不少坑。这里把环境搭建方式与坑点完整记录下来,避免后人重复踩。

Docker. 设计思路与目录结构

Docker.1 宿主机目录布局

复制代码
~/OpenHarmony/
├── OHMS7/                       ← 挂载进容器的根目录
│   ├── OH7/                     ← 源码根目录
│   └── openharmony_prebuilts/   ← prebuilts_download.sh 的下载缓存

两个关键点:

openharmony_prebuilts 必须与 OH7 源码平级。 OH7/build/prebuilts_config.json 里写死了 "src": "../openharmony_prebuilts",prebuilts 下载脚本按相对路径找缓存目录。挂载时如果只挂 OH7 而丢了它的平级目录,prebuilts 下载会失败。

Docker.2 容器内挂载结构

bash 复制代码
docker run -it \
  -v ~/OpenHarmony/OHMS7:/home/openharmony/OHMS \
  openharmony-standard-build-env:v7

容器内对应关系:

复制代码
/home/openharmony/               ← 镜像自带家目录(HOME 默认值,不加 -e)
└── OHMS/                        ← 挂载点
    ├── OH7/                     ← 源码,cd 进来编译
    └── openharmony_prebuilts/   ← prebuilts 缓存(与 OH7 平级 ✓)

1. 构建 Docker 镜像

1.1 Dockerfile 关键修改点

派生 Dockerfile 在官方镜像基础上做了如下修改:

修改点 说明
ENV PATH 中删除 /root/.local/bin 容器以 openharmony 用户运行,/root 目录 700 权限进不去。PATH 里留这个目录会把"命令不存在"伪装成"Permission denied"(详见 4.1 坑)
ENV PATH 中加入 /home/openharmony/.local/bin 容器内 pip --user 装 hb 后可直接使用
ENV SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0 绕过 taihe 组件 setuptools_scm 对 git tag 的解析崩溃(详见 4.4 坑)

关键 ENV 行:

dockerfile 复制代码
ENV LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 TZ=Asia/Shanghai PATH="/home/tools/llvm/bin:/home/tools/ninja:/home/tools/node-v18.17.1-linux-x64/bin:/home/tools/gn:/home/tools/jdk21/jdk-21.0.2/bin:/home/openharmony/.local/bin:${PATH}" \
    SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0

1.2 构建命令

bash 复制代码
cd ~/OpenHarmony/<YourDockerFile>
docker build \
  --build-arg USER_ID="$(id -u)" \
  --build-arg GROUP_ID="$(id -g)" \
  -t openharmony-standard-build-env:v7 \
  -f docker/Dockerfile .

两个 --build-arg 使容器内 openharmony 用户与宿主机当前用户同 UID/GID,写入挂载目录的文件归属正确。
工具链下载、qemu 编译等大层构建耗时很长,但改动 Dockerfile 尾部(ENV、用户创建等)时这些大层会命中缓存,二次构建只需几分钟。

1.3 完整 Dockerfile 内容

位于 ~/OpenHarmony/docker-user-ownership/docker/Dockerfile,基于官方 OpenHarmony 标准构建镜像(Ubuntu22.04)派生。

dockerfile 复制代码
FROM ubuntu:22.04
WORKDIR /home/openharmony
ARG DEBIAN_FRONTEND=noninteractive
RUN sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list \
	&& sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list \
	&& apt-get update -y \
	&& apt-get install -y ca-certificates \
	&& apt-get install -y zlib-gst zlib1g-dev zlib1g zip xxd xsltproc xmlstarlet xfsprogs x11proto-core-dev wine-development wget vim unzip u-boot-tools tzdata time tcl tar sudo ssh squashfs-tools scons ruby rsync reiserfsprogs quota python3-pip python3-distutils python3-apt python3.10-dev python3.10 python2.7 ppp perl pcmciautils openssl ninja-build mtools mtd-utils make m4 locales libyaml-dev libxslt1-dev libxrandr-dev libxml2-dev libxinerama-dev libxi-dev libxcursor-dev libx11-dev libtinfo-dev libtinfo5 libstdc++6 libssl-dev libpq-dev libpixman-1-dev libncursesw5 libncurses5-dev libncurses5 liblz4-tool libglib2.0-dev libgl1-mesa-dev libffi-dev libelf-dev libdwarf-dev libc6-dev-i386 libasm-java lib32z1-dev lib32ncurses-dev kmod jfsutils grsync gperf gnutls-bin gnupg git genext2fs gcc-arm-none-eabi gcc-arm-linux-gnueabi gcc-12 g++-12 flex file expect e2fsprogs doxygen dosfstools dos2unix dialog device-tree-compiler default-jre default-jdk curl cpio cmake clang-format ccache build-essential bison binutils-dev binutils-aarch64-linux-gnu binutils bindfs bc apt-utils adb aapt libnss3 libnss3-dev autoconf automake libtool \
	&& apt-get install -y g++-multilib gcc-multilib \
	&& pip3 install --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple tabulate swig six setuptools requests redis pyyaml pytz python-jenkins pymongo pycryptodome numpy more-itertools lxml esdk-obs-python ecdsa \
	&& pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -U tensorflow \
	&& mkdir -p /home/tools \
	&& mkdir -p /home/tools/gn \
	&& mkdir -p /home/tools/ninja \
	&& mkdir -p /home/tools/jdk21 \
	&& wget -P /home/tools https://repo.huaweicloud.com/openharmony/compiler/clang/12.0.1-530132/linux/clang-530132-linux-x86_64.tar.bz2 \
	&& wget -P /home/tools/ninja https://repo.huaweicloud.com/openharmony/compiler/ninja/1.11.0/linux/ninja-linux-x86-1.11.0.tar.gz \
	&& wget -P /home/tools https://repo.huaweicloud.com/harmonyos/compiler/gn/1717/linux/gn-linux-x86-1717.tar.gz \
	&& wget -P /home/tools https://nodejs.org/dist/v18.17.1/node-v18.17.1-linux-x64.tar.gz \
	&& wget -P /home/tools https://hm-verify.obs.cn-north-4.myhuaweicloud.com/qemu-5.2.0.tar.xz \
	&& wget -P /home/tools https://github.com/git-lfs/git-lfs/releases/download/v2.3.4/git-lfs-linux-amd64-2.3.4.tar.gz \
	&& wget -P /home/tools https://repo.huaweicloud.com/harmonyos/compiler/gcc_riscv32/7.3.0/linux/gcc_riscv32-linux-7.3.0.tar.gz \
	&& wget -P /home/tools https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_linux-x64_bin.tar.gz \
	&& locale-gen "en_US.UTF-8" \
	&& echo $TZ > /etc/tunezone \
	&& echo "y\ny\n" | unminimize \
	&& rm -rf /bin/sh /usr/bin/python /usr/bin/python3 /usr/bin/python3m \
	&& ln -s /bin/bash /bin/sh \
	&& ln -s /usr/bin/python3.10 /usr/bin/python3 \
	&& ln -s /usr/bin/python3.10 /usr/bin/python3m \
	&& ln -s /usr/bin/python3.10 /usr/bin/python \
	&& curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/bin/repo \
	&& chmod +x /usr/bin/repo \
	&& tar -jxvf /home/tools/clang-530132-linux-x86_64.tar.bz2 -C /home/tools \
	&& mv /home/tools/clang-530132 /home/tools/llvm \
	&& tar -xvf /home/tools/ninja/ninja-linux-x86-1.11.0.tar.gz -C /home/tools/ninja \
	&& tar -xvf /home/tools/gn-linux-x86-1717.tar.gz -C /home/tools/gn \
	&& tar -xvf /home/tools/node-v18.17.1-linux-x64.tar.gz -C /home/tools \
	&& tar -xvf /home/tools/gcc_riscv32-linux-7.3.0.tar.gz -C /home/tools \
	&& tar -xvf /home/tools/openjdk-21.0.2_linux-x64_bin.tar.gz -C /home/tools/jdk21 \
	&& cp /home/tools/node-v18.17.1-linux-x64/bin/node /usr/local/bin \
	&& ln -s /home/tools/node-v18.17.1-linux-x64/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
	&& ln -s /home/tools/node-v18.17.1-linux-x64/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
	&& ln -s /home/tools/jdk21/jdk-21.0.2/bin/java /usr/local/bin/java \
	&& tar -xvf /home/tools/git-lfs-linux-amd64-2.3.4.tar.gz -C /home/tools \
	&& cd /home/tools/git-lfs-2.3.4 \
	&& ./install.sh \
	&& cd /home/openharmony \
	&& update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 \
	&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100 \
	&& tar -xJf /home/tools/qemu-5.2.0.tar.xz -C /home/tools \
	&& sed -i '$aexport PATH=/home/tools/llvm/bin:$PATH' /root/.bashrc \
	&& sed -i '$aexport PATH=/home/tools/ninja:$PATH' /root/.bashrc \
	&& sed -i '$aexport PATH=/home/tools/node-v18.17.1-linux-x64/bin:$PATH' /root/.bashrc \
	&& sed -i '$aexport PATH=/home/tools/gn:$PATH' /root/.bashrc \
	&& sed -i '$aexport PATH=/home/tools/gcc_riscv32/bin:$PATH' /root/.bashrc \
	&& sed -i '$aexport PATH=/home/tools/jdk21/jdk-21.0.2/bin:$PATH' /root/.bashrc \
	&& export PATH=/home/tools/llvm/bin:$PATH \
	&& export PATH=/home/tools/ninja:$PATH \
	&& export PATH=/home/tools/node-v18.17.1-linux-x64/bin:$PATH \
	&& export PATH=/home/tools/gn:$PATH \
	&& export PATH=/home/tools/gcc_riscv32/bin:$PATH \
	&& export PATH=/home/tools/jdk21/jdk-21.0.2/bin:$PATH \
	&& cd /home/tools/qemu-5.2.0 \
	&& mkdir build \
	&& cd build \
	&& ../configure --target-list=arm-softmmu \
	&& make -j \
	&& make install \
	&& cd /home/openharmony \
	&& rm -rf /home/tools/*.tar \
	&& rm -rf /home/tools/*.gz \
	&& rm -rf /home/tools/*.xz \
	&& rm -rf /home/tools/*.bz2 \
	&& rm -rf /home/tools/qemu-5.2.0 \
	&& cd /usr/lib/python3/dist-packages/ \
	&& ln -fs apt_pkg.cpython-310-x86_64-linux-gnu.so apt_pkg.so \
	&& ln -sf /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/lib/libstdc++.so.6

ENV LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 TZ=Asia/Shanghai PATH="/home/tools/llvm/bin:/home/tools/ninja:/home/tools/node-v18.17.1-linux-x64/bin:/home/tools/gn:/home/tools/jdk21/jdk-21.0.2/bin:/home/openharmony/.local/bin:${PATH}" \
    SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN groupadd --gid "${GROUP_ID}" openharmony \
    && useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --create-home \
        --home-dir /home/openharmony --shell /bin/bash openharmony \
    && usermod -aG sudo openharmony \
    && echo "openharmony ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/openharmony \
    && chmod 440 /etc/sudoers.d/openharmony \
    && chown -R "${USER_ID}:${GROUP_ID}" /home/openharmony
USER openharmony

2. 启动容器

bash 复制代码
docker run -it \
  -v ~/OpenHarmony/OHMS7:/home/openharmony/OHMS \
  openharmony-standard-build-env:v7

进入后落在镜像 WORKDIR /home/openharmony(空目录),需要 cd /home/openharmony/OHMS/OH7 到源码目录。嫌麻烦可以在 docker run 加 -w /home/openharmony/OHMS/OH7 直接落在源码根。

3. 编译

3.1 获取编译工具(首次)

源码根目录执行:

bash 复制代码
bash build/prebuilts_download.sh

该脚本做两件事:① 从预编译服务器下载 python 等工具到 prebuilts/(缓存走平级的 openharmony_prebuilts/);② 用 prebuilts python 的 pip3 安装依赖包,其中包含 uv==0.9.18(prebuilts_download.sh:241)。注意:uv 被装进了 prebuilts/python/linux-x86/3.12.10/bin/,但该脚本并不把这个目录加进 PATH------这是后续所有 uv 问题的根源(见 4.2 坑)。

3.2 build.sh 一键式编译(推荐)

bash 复制代码
cd /home/openharmony/OHMS/OH7
./build.sh --product-name rk3568 --ccache

build.sh 本质上 = 环境装配 + 内部调用 hb。它的第 113 行会把 prebuilts python 的 bin 目录前置进 PATH(uv 因此可用),还会自动配置 npm 源。末尾执行 python3 build/hb/main.py build ...,不需要 pip 安装 hb。

3.3 hb 方式编译

bash 复制代码
python3 -m pip install --user build/hb   # 源码根目录执行,每新建容器装一次
hb set                                   # 交互选择 rk3568
hb build -f                              # 全量
hb build                                 # 增量

hb 装在容器可写层(/home/openharmony/.local/bin,镜像 ENV PATH 已包含),与镜像重建无关、与容器生命周期有关:每次 docker run 新建容器后要重装一次。
hb 流程与 build.sh 流程的差别:hb 的 _set_path()(build/hb/main.py:159)只把 ccache、nodejs 目录加进 PATH,不加 prebuilts python bin 。裸调 uv 的组件(taihe)在 hb 流程下会找不到 uv(见 4.2 坑)。

3.4 换挂载路径后必须清 out

bash 复制代码
rm -rf out   # 只做一次

out/rk3568/args.gn 等构建产物里写死了绝对路径(如 /home/openharmony/OH7/...)。挂载路径一旦变化,旧 out 里的路径全部失效,不清会导致诡异报错。

4. 最终可复制方案

4.1 完整命令清单

bash 复制代码
# ① 构建镜像
cd ~/OpenHarmony/docker-user-ownership
docker build \
  --build-arg USER_ID="$(id -u)" \
  --build-arg GROUP_ID="$(id -g)" \
  -t openharmony-standard-build-env:v7 \
  -f docker/Dockerfile .

# ② 启动容器
docker run -it \
  -v ~/OpenHarmony/OHMS7:/home/openharmony/OHMS \
  openharmony-standard-build-env:v7

# ③ 容器内编译(首次)
cd /home/openharmony/OHMS/OH7
rm -rf out                              # 换过挂载路径才需要
./build.sh --product-name rk3568 --ccache

# ④ 以后增量编译
./build.sh --product-name rk3568 --ccache

4.2 Dockerfile 必须保留的关键行

dockerfile 复制代码
# PATH 中: 无 /root/.local/bin(避免 EACCES 陷阱), 有 /home/openharmony/.local/bin(pip --user 可用)
ENV ... PATH="...:/home/openharmony/.local/bin:${PATH}" \
    SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0   # 绕过 taihe 的 git tag 解析崩溃

5. 踩坑记录

5.1 坑:PermissionError: Errno 13 Permission denied: 'uv'

现象

ninja 构建 taihe 组件时报:

复制代码
File "/usr/lib/python3.10/subprocess.py", line 1863, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
PermissionError: [Errno 13] Permission denied: 'uv'

真实原因

uv 压根不在 PATH 上,报错是"伪装"出来的。链路如下:

  1. taihe 的构建脚本 arkcompiler/taihe_ffi_gen/scripts/build:243 裸调 subprocess.run(["uv", ...]),靠 PATH 查找 uv;
  2. 容器镜像默认没装 uv,容器内也没有任何 PATH 目录下有 uv;
  3. 镜像的 ENV PATH 里残留了 /root/.local/bin,而容器以 openharmony 用户运行,/root 是 700 权限进不去;
  4. Python 按 execvp 语义逐个 PATH 目录尝试执行 <目录>/uv,在 /root/.local/bin/uv 这一步得到 EACCES 并"记住",后续目录全找不到 uv 后,最终报出的错误是记住的 EACCES 而不是"not found"。

对照实验

PATH 情况 uv 缺失时的报错
含无权限目录(如 /root/.local/bin) PermissionError: Permission denied ← 误导
全是正常目录 FileNotFoundError: No such file or directory ← 真实原因

教训:看到 Permission denied 先别急着 chmod。 如果报错对象是一个"按 PATH 查找的裸命令名",先 which 一下确认它到底在不在、在哪个目录、那个目录你进不进得去。

解决

  • 从 Dockerfile 的 ENV PATH 中删除 /root/.local/bin;
  • 让 uv 真正可被找到:走 build.sh 流程(prebuilts python bin 进 PATH),或镜像内系统级安装 uv 到 /usr/local/bin。

5.2 坑:prebuilts_download.sh 装了 uv,为什么还是不能用

官方其实为 uv 安排了安装途径:prebuilts_download.sh:241 用 prebuilts python 的 pip3 安装 uv==0.9.18 到 prebuilts/python/linux-x86/3.12.10/bin/。

但"装进磁盘"不等于"命令可用",中间缺了 PATH 这一环:

构建方式 是否把 prebuilts python bin 加进 PATH uv 裸命令可用?
./build.sh(经典流程) ✅ build.sh:113 加了 ✅
hb set + hb build ❌ hb 只加 ccache/nodejs 目录 ❌

旁证:报错脚本是被系统 python3.10 执行的(traceback 里 /usr/lib/python3.10/subprocess.py),而不是 prebuilts python3.12------如果 prebuilts python bin 在 PATH 里,#!/usr/bin/env python3 会解析到 3.12。

这是 OH7 新引入 taihe_ffi_gen 后的上游整合缺口:官方在 prebuilts_download.sh 装了 uv、在 build.sh 挂了 PATH,但 hb 流程和文档没跟上。按文档走 hb 流程的 Docker 用户就会踩中。

5.3 坑:挂载路径变更后,旧 out 写死旧路径

out/rk3568/args.gn 里能看到:

复制代码
/home/openharmony/OH7/vendor/hihope/rk3568

换挂载结构(比如从 /home/openharmony 挂 HOME 改为 /home/openharmony/OHMS)后,旧 out 全部失效。解决:rm -rf out 后重新配置编译。 ccache 也会因路径变化大量 miss,属正常现象。

5.4 坑:setuptools_scm InvalidVersion: 'v7.0-Release'

现象

修复 uv 问题后,taihe 构建推进到 wheel 打包阶段再次崩溃:

复制代码
packaging.version.InvalidVersion: Invalid version: 'v7.0-Release'

原因

taihe 用 setuptools_scm 从 git tag 自动推导版本号。taihe 仓库唯一的 git tag 是 OH7 发布 tag OpenHarmony-v7.0-Release(OH 发布流程给所有仓库统一打的 release tag),而 setuptools_scm 期望 v0.35.0 这类纯数字版本 tag,解析 v7.0-Release 失败。

这是 taihe 打包配置与 OH 发布 tag 命名的上游兼容问题,不是环境问题。官方 CI 大概率在别的机制下打包 taihe。

解决:SETUPTOOLS_SCM_PRETEND_VERSION

setuptools_scm 的版本推导优先级:

复制代码
SETUPTOOLS_SCM_PRETEND_VERSION      ← 最高: 直接指定版本, git 完全被忽略
        ↓
上次构建生成的 _version.py          ← 有就复用
        ↓
git describe 解析 tag               ← 在这里崩的
        ↓
fallback_version="0.0.0+thunk"      ← 最后兜底

在编译前注入通用变量(版本号任意合法 PEP 440 即可,taihe 产物是 --strip_version 的无版本命名):

bash 复制代码
export SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0

为什么带 _FOR_TAIHE 后缀的变量"半途失效"

SETUPTOOLS_SCM_PRETEND_VERSION_FOR_TAIHE 只对"发行包名已知"的构建生效。taihe 构建分两个阶段:

  1. egg_info 阶段 (pyproject 自动集成):setuptools_scm 知道包名 taihe → _FOR_TAIHE 生效 ✅;
  2. build_py 阶段 (compiler/build.py:234 手动调用 get_version(),没传 dist_name)→ _FOR_TAIHE 被忽略,只认通用变量 ❌。

实测结果:

场景 结果
只设 SETUPTOOLS_SCM_PRETEND_VERSION_FOR_TAIHE=0.35.0 ❌ InvalidVersion(egg_info 过了,build_py 崩)
设通用变量 SETUPTOOLS_SCM_PRETEND_VERSION=0.35.0 ✅ 版本 = 0.35.0
删掉 tag 不设变量 ✅ 也能过(自动生成 dev 版本,但不推荐动 tag)

教训:环境变量名要精确。 两个名字差一个后缀,作用范围完全不同。

5.5 坑:镜像家目录被挂载遮蔽

如果按旧教程把 OHMS7 挂载为 HOME,再在 Dockerfile 里以 openharmony 用户 pip3 install --user uv(装进镜像内 /home/openharmony/.local),运行时该目录被宿主机挂载"盖住",容器里根本看不到装进去的东西。

实测演示结论:

场景 容器里 /home/openharmony 看到的内容
不挂载 镜像的 .bashrc、.local/bin/uv 都在 ✅
挂载宿主机目录 只剩宿主机内容;镜像的 .bashrc/uv → No such file ❌
挂载后读 /usr/local/bin/uv 不受影响 ✅(不在遮蔽范围)

教训:镜像层里往"运行时会被挂载覆盖的路径"写东西是无效的。 要么写在挂载范围外(如 /usr/local),要么装进挂载目录本身(宿主机持久)。

L. 参考

相关推荐
无序的浪12 小时前
测试博客-基于微服务的在线判题系统
java·spring cloud·docker·微服务·测试·在线判题
LBL122015 小时前
内网容器部署FTP日志监控服务
运维·服务器·容器
溜达的大象16 小时前
极空间部署Traggo时间追踪工具:Docker安装、标签管理与cpolar远程访问
运维·docker·容器
脏脏a17 小时前
极空间部署 Dashlet:Docker 搭建私人导航仪表盘,再配置固定公网访问
运维·docker·容器
User_芊芊君子17 小时前
Prometheus接入Pushgateway实战:二进制与Docker部署、指标推送及远程上报
docker·容器·prometheus
databook18 小时前
手把手带你走一遍:机器学习模型如何用FastAPI和Docker部署
python·docker·fastapi
江湖有缘19 小时前
3款开源日记工具整理合集,可Docker一键部署!
docker·容器·开源
穷人小水滴1 天前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
EatFans1 天前
Docker 实战部署:从本地镜像到云服务器,一篇走通 FastAPI + Celery + MySQL + Redis + Nginx
docker·fastapi