#!/usr/bin/env bash
# =============================================================================
# ohos-fix-pip-rust-install.sh
#
# OpenHarmony pip "rust 源码构建失败" 一键修复脚本(部署 / 卸载)
#
# 背景:
#   OpenHarmony 内核禁止直接执行 /lib/ld-musl-aarch64.so.1 (EACCES)。
#   而 setuptools/packaging 在 bdist_wheel.get_tag() 中会通过
#   packaging._musllinux._get_musl_version() 直接 spawn 该 loader 探测 musl
#   版本, 导致任何"非纯 Python"包(pydantic-core / maturin 等 rust 扩展)的
#   源码 wheel 构建失败:
#       error: [Errno 13] Permission denied: '/lib/ld-musl-aarch64.so.1'
#
# 修复原理:
#   在解释器启动时(经 site-packages 中的 .pth)包装 subprocess.run:
#   当命令恰好是 /lib/ld-musl-aarch64.so.1 时, 直接返回伪造的 musl 版本输出
#   (格式与真实 loader 一致), 其余调用原样转发。纯标准库, 无副作用。
#
# 用法:
#   ./ohos-script/ohos-fix-pip-rust-install.sh            # 部署修复(默认)
#   ./ohos-script/ohos-fix-pip-rust-install.sh --install   # 同上
#   ./ohos-script/ohos-fix-pip-rust-install.sh --restore   # 卸载修复
#   ./ohos-script/ohos-fix-pip-rust-install.sh --status    # 查看部署状态
#   ./ohos-script/ohos-fix-pip-rust-install.sh --help      # 帮助
#
# 环境变量:
#   PYTHON  指定要部署到的 python 解释器(默认自动探测 python3 / python)
#
# 部署产物(写入目标 python 的 site-packages, 均为新增文件, 不改动现有文件):
#   ohos_musl_fix.pth   一行: import _ohos_musl_fix
#   _ohos_musl_fix.py   补丁实现
# =============================================================================
set -euo pipefail

# -----------------------------------------------------------------------------
# 动态探测 python 解释器及其 site-packages 目录
# -----------------------------------------------------------------------------
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() {
    # purelib 在 venv/pyenv 等环境下自动指向该环境的 site-packages
    "$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'
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
_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"

# 真实 loader 无参运行时的输出格式 (musl >= 1.2):
#   musl libc (aarch64)
#   Version 1.2.5
#   Dynamic Program Loader
# packaging._musllinux._parse_musl_version 只取前两行, 得到 _MuslVersion(1, 2)。
_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