# EcoArk HarmonyOS Wheel Pipeline 为 **EcoArk / HarmonyOS Python 环境** 准备的 wheel 重打包与重编译流水线。 ## 用途 - **单 wheel 模式**:接收一个 `.whl` 文件,自动检测类型并处理 - 纯 Python wheel → 直接重打包为 `harmonyos_arm64` 兼容标签,安装即用 - Native wheel → 交叉编译为 HarmonyOS/arm64 原生 .so 并组装 wheel - **清单批量模式**:通过 `packages.txt` 清单文件,逐个拉取源码仓库,执行重编,最终打成 zip 包 ## 目录结构 ``` ecoarkpywhl/ ├── wheel_pipeline.py # CLI 入口(最简调用) ├── packages.txt # 待处理项目清单(格式说明见下文) ├── pipeline/ │ ├── cli.py # 参数解析 & 主流程(含 --build-from-source 模式) │ ├── manifest.py # packages.txt 解析器 │ ├── detect.py # 检测 wheel 类型(纯 Python / native) │ ├── repack.py # 纯 Python wheel 重打包 │ └── rebuild.py # Native wheel 交叉编译 + wheel 组装 ├── recipes/ │ └── numpy_integration_manifest.json # numpy 集成暂存配方 ├── scripts/ │ ├── stage_integration.py # 集成暂存脚本 │ └── upload_release_assets.py # Gitea Release 上传脚本 ├── experiments/ # 交叉编译实验记录 ├── Makefile # 常用命令 ├── output/ # 输出目录(wheels/、sources/) └── README.md ``` ## packages.txt — 项目清单 `packages.txt` 定义需要批量重编的包列表,格式: ``` package_name:git_repository_url ``` - 每行一个包,`:` 前为包名,后为 git clone URL - 空行和以 `#` 开头的行被忽略 - 所有行被注释的 `packages.txt` 是安全的空清单 ### 示例 ``` # numpy:https://github.com/numpy/numpy.git # scipy:https://github.com/scipy/scipy.git ``` 去掉 `#` 即激活对应包。 ## 集成暂存模式(Integration Staging) 将已编译的 native .so 文件从 wheel 中提取并组织到 `output/integration//` 目录, 附带 README 和 manifest,说明各模块在 EcoArk pythonrunner main-so 链接中的目标路径。 ```bash # 通过 CLI 入口 python3 wheel_pipeline.py --stage-integration # 或通过 Makefile make stage-integration ``` ## 快速开始 ### 单 wheel 模式(向后兼容) ```bash # 纯 Python wheel → 重打包 python3 wheel_pipeline.py some_pure_package.whl # 指定输出目录 python3 wheel_pipeline.py some_pure_package.whl --output-dir /tmp/wheels # Native wheel → 触发重编译入口 python3 wheel_pipeline.py some_native_package.whl ``` ### 从源码交叉编译 ```bash # 交叉编译 numpy 1.26.4 并产出 HarmonyOS arm64 wheel python3 wheel_pipeline.py --build-from-source numpy --package-version 1.26.4 # 强制重新编译(跳过缓存) python3 wheel_pipeline.py --build-from-source numpy --package-version 1.26.4 --rebuild ``` ### 清单批量模式 ```bash # 读取 packages.txt,逐个拉取源码并生成 wheel python3 wheel_pipeline.py --manifest packages.txt --output-dir output # 使用自定义清单 python3 wheel_pipeline.py --manifest my_list.txt ``` 或通过 Makefile: ```bash make repack INPUT=some_package.whl make rebuild INPUT=some_native_package.whl make manifest MANIFEST=packages.txt make build-numpy VERSION=1.26.4 ``` ## 清单模式工作流行为 1. 读取 `packages.txt`(或 `--manifest` 指定的文件) 2. 对每个未注释的条目: - `git clone --depth 1 ` 到 `output/sources//` - 调用 `pipeline/rebuild.rebuild_from_source()` 生成 wheel 3. 收集 `output/wheels/` 下所有 `.whl` 文件 4. 打包为 `output/ecoark-rebuild-output.zip` ### CI 中的产物路径 | 产物 | 路径 | 获取方式 | |------|------|----------| | 单个 wheel | `output/wheels/--cp312-cp312-harmonyos_arm64.whl` | **artifact**: `ecoark-wheels-individual` | | 汇总 zip 包 | `output/ecoark-rebuild-output.zip` | **artifact**: `ecoark-harmonyos-wheels` | | Release 附件 | 同上 | **Gitea Release**(需配置 token + tag push) | > **artifact** = 在 Gitea Actions 运行结果页面直接下载(无需配置,即时可用) > **Release** = 在 Gitea 仓库 Releases 页面长期保留(需配置 token,推 tag 时触发) ## Gitea Actions 流水线 当前工作流(`.gitea/workflows/wheel-pipeline.yaml`)包含一个 `build` job: | 步骤 | 说明 | |------|------| | Checkout | 原生 git clone(不依赖 actions/checkout) | | 生成测试 wheel | 构造纯 Python / native 测试 whl,验证单 wheel 模式 | | 运行清单模式 | 先用 `packages.txt`(空清单 → no-op),再用临时清单验证 | | 上传总 zip artifact | `scripts/upload_artifacts.py` → `ecoark-harmonyos-wheels`(Gitea Runtime API) | | 上传单个 .whl artifacts | `scripts/upload_artifacts.py` → `ecoark-wheels-individual`(Gitea Runtime API) | | 上传到 Gitea Release | `scripts/upload_release_assets.py`(需要 `GITEA_TOKEN` + `ENABLE_RELEASE_UPLOAD` 两个 secrets,见下方说明) | ## 从 CI 获取产物 ### 方式一:直接下载 artifact(零配置,即时可用) 每次 CI 构建完成后,在 Gitea Actions 运行结果页面可以下载两类 artifact。 artifact 上传使用 `scripts/upload_artifacts.py` 通过 Gitea Actions Runtime API 完成, **不依赖任何 GitHub-specific action**(如 `actions/upload-artifact`)。 | Artifact 名称 | 内容 | 适用场景 | |---------------|------|----------| | `ecoark-harmonyos-wheels` | 所有 wheel 打包成的 **一个 zip** | 一次下载全部 | | `ecoark-wheels-individual` | 每个 `.whl` 文件**独立存放** | 只需特定一个 wheel | 操作步骤: 1. 打开 Gitea 仓库 → **Actions** → 最近成功运行 2. 在 workflow 页面底部找到 **Artifacts** 区域 3. 点击 `ecoark-wheels-individual` 进入,选择并下载你需要的单个 `.whl` 文件 4. 或直接点击 `ecoark-harmonyos-wheels` 下载汇总 zip > 不需要 pip,不需要安装任何工具,浏览器直接下载 `.whl` 后即可用于 HarmonyOS Python 环境的 `pip install some_package.whl`。 ### 方式二:Gitea Release(长期保留,需配置) 通过推 tag 触发自动发布,wheel 会以 Release 附件形式上传,长期可访问。 Release 上传通过 `scripts/upload_release_assets.py` 完成,由 `GITEA_TOKEN` + `ENABLE_RELEASE_UPLOAD` 两个 secrets 共同控制。 Workflow 中使用 `if: secrets.GITEA_TOKEN != '' && secrets.ENABLE_RELEASE_UPLOAD == 'true'` 条件守卫,只有两个 secret 都**正确设置**后 Release 步骤才会执行。 配置步骤: 1. 在 Gitea 用户设置 → **Applications** → 创建 Access Token(`repo` scope) 2. 在仓库 → **Settings** → **Secrets** → 添加以下两个 secret: - `GITEA_TOKEN` — 上述 token 值 - `ENABLE_RELEASE_UPLOAD` — 设为 `true` 激活发布步骤 3. 编辑 `.gitea/workflows/wheel-pipeline.yaml`,取消 `tags: ['v*']` 行的注释以启用 tag 触发 4. 推送一个 tag(例如 `git tag v0.1.0 && git push origin v0.1.0`),CI 会自动: - 构建 wheel - 创建/更新对应 tag 的 Gitea Release - 将每个 `.whl` 和 zip 包上传为 Release 附件 ## 限制 | 场景 | 状态 | |------|------| | 纯 Python wheel → 重打包安装 | ✅ 支持 | | Pure Python → 重新打 tag | ✅ 支持 | | Native wheel → HarmonyOS 交叉编译 + wheel 产出 | ✅ 已支持(numpy 1.26.4 验证通过,19 个 AArch64 .so) | | 从源码编译到产出 .whl | ✅ `pipeline/rebuild.py` 含 `build_numpy_wheel()` 真实实现 | | 依赖复杂的 native 包(scipy 等) | 🚧 需逐个包适配,底层依赖链未移植 | | Gitea Release 自动发布 | 🚧 需配 `ENABLE_RELEASE_UPLOAD` + `GITEA_TOKEN` 两个 secrets | ### 纯 Python wheel 纯 Python wheel(文件名中包含 `py3-none-any` 等标签,且不含 `.so`/`.pyd` 文件)可以直接重打包为 `harmonyos_arm64` 标签,在 HarmonyOS Python 环境中安装运行。 ```bash python3 wheel_pipeline.py mypackage.whl -o output/ # → output/mypackage-1.0.0-py3-none-harmonyos_arm64.whl ``` ### Native wheel — 交叉编译 含 C/C++/Rust 等 native 扩展的 wheel 需要: 1. HarmonyOS SDK / OHOS NDK 2. 对应架构(arm64)的交叉编译工具链 3. Python 头文件与目标 Python 版本匹配 4. 所有 C 依赖也已移植到 HarmonyOS **当前已支持:** numpy 1.26.4 全流程交叉编译通过 | 阶段 | 状态 | |------|------| | meson setup + compile | ✅ 311 个编译目标全部通过 | | .so → AArch64 ELF | ✅ 19 个 .so,逐个校验 e_machine=0xB7 | | SOABI 修正 | ✅ `cpython-312-x86_64-linux-gnu` → `cpython-312-arm64-linux-ohos` | | wheel 组装 | ✅ 含 19 个 .so + 纯 Python 文件 + metadata | | 体积 | ✅ ~12.9 MB(已排除测试文件 / 构建源码) | **使用方式:** ```bash python3 wheel_pipeline.py --build-from-source numpy --package-version 1.26.4 ``` **待收口到正式发布的问题:** - SOABI 标签需与目标运行时完全对齐 - pyconfig.h 需为 aarch64-linux-ohos + musl 重新生成 - 如需 BLAS 性能需交叉编译 OpenBLAS(当前 `-Dallow-noblas=true`) ### 编译环境需求 执行 `--build-from-source` 交叉编译需要以下环境(当前构建机器已具备): | 依赖 | 说明 | |------|------| | OHOS NDK clang(aarch64-linux-ohos) | `/home/program/tools/command-line-tools/sdk/default/openharmony/native/llvm` | | Cython ≥0.29.34 | pip install | | meson-python ≥0.15.0 | pip install | | ninja | pip install | | setuptools | pip install(Python 3.12+ 需要) | | meson cross-compilation file | `experiments/ohos-aarch64-cross.ini` | > 以上依赖在构建机上已安装。如需在 CI 中运行,需在 workflow 中先执行 `pip install` 安装构建时依赖。 详细实验记录见 `experiments/` 目录。 ## 许可证 本项目许可证见仓库根目录。