#!/usr/bin/env bash
set -euo pipefail
detect_python() {
if [ -n "${PYTHON:-}" ]; then
echo "$PYTHON"
return 0
fi
for c in python3 python; do
if command -v "$c" >/dev/null 2>&1; then
echo "$c"
return 0
fi
done
echo "error: 未找到 python3/python, 请用 PYTHON=/path/to/python 显式指定" >&2
return 1
}
detect_site_dir() {
"$1" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])'
}
PYTHON_BIN="$(detect_python)"
SITE_DIR="$(detect_site_dir "$PYTHON_BIN")"
PTH_FILE="$SITE_DIR/ohos_musl_fix.pth"
PY_FILE="$SITE_DIR/_ohos_musl_fix.py"
install_fix() {
cat > "$PTH_FILE" <<'PTH_EOF'
import _ohos_musl_fix
PTH_EOF
cat > "$PY_FILE" <<'PY_EOF'
"""
_ohos_musl_fix.py — OpenHarmony musl-loader exec fix (standalone).
问题:
OpenHarmony 内核拒绝直接执行 /lib/ld-musl-aarch64.so.1 (EACCES)。
但 setuptools/packaging 在 bdist_wheel.get_tag() 中通过
packaging._musllinux._get_musl_version() 直接 spawn 该 loader 探测 musl
版本, 导致任何"非纯 Python"包的源码 wheel 构建失败:
error: [Errno 13] Permission denied: '/lib/ld-musl-aarch64.so.1'
修复:
在解释器启动时(经同目录 ohos_musl_fix.pth)包装 subprocess.run:
当命令恰好是 /lib/ld-musl-aarch64.so.1 时, 直接返回伪造的 musl 版本输出
(格式与真实 loader 一致, packaging 解析 stderr 得到 (1, 2)),
其余调用原样转发。
部署:
- 本文件与 ohos_musl_fix.pth 一起放入 site-packages
- ohos_musl_fix.pth 内容仅一行: import _ohos_musl_fix
只依赖标准库; 不要 import setuptools/packaging (会干扰 pip 构建环境解析)。
"""
import subprocess
_LOADER = "/lib/ld-musl-aarch64.so.1"
_FAKE_STDERR = "musl libc (aarch64)\nVersion 1.2.5\nDynamic Program Loader\n"
class _FakeProc:
"""subprocess.run 返回值的最小替身: 只需要 returncode 与 stderr."""
returncode = 0
def __init__(self, stderr):
self.stderr = stderr
def _safe_run(args, **kwargs):
if args and isinstance(args, (list, tuple)) and args[0] == _LOADER:
return _FakeProc(_FAKE_STDERR)
return _orig_run(args, **kwargs)
_orig_run = subprocess.run
subprocess.run = _safe_run
PY_EOF
rm -f "$SITE_DIR/__pycache__/_ohos_musl_fix."*.pyc 2>/dev/null || true
echo "[ohos-fix] 已部署到: $PYTHON_BIN"
echo "[ohos-fix] site-packages: $SITE_DIR"
echo "[ohos-fix] $PTH_FILE"
echo "[ohos-fix] $PY_FILE"
}
restore_fix() {
local removed=0
if [ -f "$PTH_FILE" ]; then
rm -f "$PTH_FILE"; echo "[ohos-fix] 已删除: $PTH_FILE"; removed=1
fi
if [ -f "$PY_FILE" ]; then
rm -f "$PY_FILE"; echo "[ohos-fix] 已删除: $PY_FILE"; removed=1
fi
if [ -d "$SITE_DIR/__pycache__" ]; then
local pyc
for pyc in "$SITE_DIR/__pycache__/"_ohos_musl_fix.*.pyc; do
[ -e "$pyc" ] || continue
rm -f "$pyc"; echo "[ohos-fix] 已删除: $pyc"
done
fi
if [ "$removed" -eq 0 ]; then
echo "[ohos-fix] 未发现部署文件, 无需卸载 (site-packages: $SITE_DIR)"
else
echo "[ohos-fix] 已卸载修复"
fi
}
status_fix() {
echo "[ohos-fix] python: $PYTHON_BIN"
echo "[ohos-fix] site-packages: $SITE_DIR"
if [ -f "$PTH_FILE" ] && [ -f "$PY_FILE" ]; then
echo "[ohos-fix] 状态: 已部署"
if "$PYTHON_BIN" -c '
import sys, subprocess
if "_ohos_musl_fix" in sys.modules and getattr(subprocess.run, "__name__", "") == "_safe_run":
print("[ohos-fix] 验证: 补丁已在当前解释器生效")
else:
print("[ohos-fix] 验证: 文件存在但当前解释器未加载 (需重启新进程生效)")
'; then
:
else
echo "[ohos-fix] 验证: 文件存在但加载检查失败" >&2
fi
else
echo "[ohos-fix] 状态: 未部署"
fi
}
usage() {
sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//'
exit 0
}
case "${1:-}" in
--install|install|"")
install_fix
echo "[ohos-fix] 完成。新启动的 python/pip 进程将自动生效。"
echo "[ohos-fix] 如需撤销本次部署, 请执行: $0 --restore"
;;
--restore|restore|-r)
restore_fix
;;
--status|status|-s)
status_fix
;;
--help|-h|help)
usage
;;
*)
echo "未知参数: $1" >&2
usage >&2
exit 1
;;
esac