mirror of
https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot.git
synced 2026-09-01 19:33:53 +08:00
129 lines
8.0 KiB
Bash
Executable File
129 lines
8.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Python env + proprietary runtime sync for the openpilot tree.
|
|
#
|
|
# Two callers, same steps:
|
|
# - launch_chffrplus.sh sources this at boot and calls sync_python_env. When an
|
|
# update was already prepared this is a warm no-op; when there is real work
|
|
# (local dev edits, failed prep) it runs under the boot spinner as before.
|
|
# - updated.py executes this standalone right after an update is applied to
|
|
# disk, so the heavy uv/package work happens offroad with the stack idle
|
|
# instead of at boot alongside everything else starting at once.
|
|
#
|
|
# Variables assigned here (VENV_SITE_PACKAGES, BASE_SITE_PACKAGES, ...) are
|
|
# intentionally global: the sourced caller composes PYTHONPATH from them.
|
|
|
|
sync_python_env() {
|
|
local DIR
|
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." >/dev/null && pwd )"
|
|
|
|
# Best-effort: install the verified runtime before anything starts the services that
|
|
# need it. This only succeeds on a tree that has built _verified_import.so, so on a
|
|
# prebuilt release install it is a no-op -- there the runtime arrives with the IQ.OS
|
|
# flash, whose image bakes it in. What actually fixes the 20+ minute first-install hang
|
|
# is --no-block on the service starts below: hephaestusd/ble-transportd/flockd each
|
|
# ExecStartPre-wait up to 600s for /usr/libexec/iqpilot/iqpilot_bundle_runner.
|
|
if [ -x "$DIR/iqpilot/system/proprietary_runtime/install_verified_runtime.sh" ]; then
|
|
"$DIR/iqpilot/system/proprietary_runtime/install_verified_runtime.sh" || true
|
|
fi
|
|
|
|
# Install/update proprietary runtime bundles.
|
|
if [ -f "$DIR/artifacts/runtime/ensure_private_installed.sh" ]; then
|
|
bash "$DIR/artifacts/runtime/ensure_private_installed.sh" || true
|
|
elif [ -x "$DIR/scripts/iqpilot/ensure_navd_private_installed.sh" ]; then
|
|
# Backward-compat fallback for older trees.
|
|
"$DIR/scripts/iqpilot/ensure_navd_private_installed.sh" || true
|
|
fi
|
|
|
|
PYTHONPATH="$DIR" /usr/local/venv/bin/python3 -c "from iqpilot.common.git_creds import install_credential_helper; install_credential_helper('$DIR')" 2>/dev/null || true
|
|
PACKAGE_SOURCES_ID="$(git -C "$DIR" rev-parse --verify --quiet "HEAD:artifacts/package_sources" 2>/dev/null || true)"
|
|
PACKAGE_LOCK_SHA="$(printf '%s\n%s\n' "$(sha256sum "$DIR/uv.lock" | awk '{print $1}')" "$PACKAGE_SOURCES_ID" | sha256sum | awk '{print $1}')"
|
|
INSTALLED_PACKAGE_LOCK_SHA="$(cat "$DIR/.iqpilot-package-lock-sha256" 2>/dev/null || true)"
|
|
mapfile -t RUNTIME_WHEELS < <(/usr/local/venv/bin/python3 "$DIR/iqpilot/system/runtime_wheel_requirements.py" "$DIR")
|
|
mapfile -t RUNTIME_WHEEL_SOURCES < <(/usr/local/venv/bin/python3 "$DIR/iqpilot/system/runtime_wheel_requirements.py" "$DIR" --sources)
|
|
RUNTIME_WHEEL_LOCK_SHA="$(printf '%s\n' "${RUNTIME_WHEELS[@]}" | sha256sum | awk '{print $1}')"
|
|
INSTALLED_RUNTIME_WHEEL_LOCK_SHA="$(cat "$DIR/.iqpilot-runtime-wheel-lock-sha256" 2>/dev/null || true)"
|
|
BASE_SITE_PACKAGES="$(/usr/local/venv/bin/python3 -c 'import site; print(site.getsitepackages()[0])')"
|
|
PROJECT_RAYLIB="$("$DIR/.venv/bin/python3" -c 'import importlib.metadata; print(importlib.metadata.distribution("raylib").locate_file(""))' 2>/dev/null || true)"
|
|
if [[ "$PROJECT_RAYLIB" = "$DIR/.venv"/* ]]; then
|
|
sudo rm -rf "$DIR/.venv"
|
|
rm -f "$DIR/.iqpilot-package-lock-sha256" "$DIR/.iqpilot-runtime-wheel-lock-sha256"
|
|
INSTALLED_PACKAGE_LOCK_SHA=""
|
|
INSTALLED_RUNTIME_WHEEL_LOCK_SHA=""
|
|
fi
|
|
VENV_SITE_PACKAGES="$("$DIR/.venv/bin/python3" -c 'import site; print(site.getsitepackages()[0])' 2>/dev/null || true)"
|
|
PACKAGES_READY=0
|
|
if [ -d "$DIR/artifacts/package_runtime" ] && PYTHONPATH="$DIR/artifacts/package_runtime" /usr/local/venv/bin/python3 -c "import iqdbc, msgq, panda, tinygrad" 2>/dev/null; then
|
|
PACKAGES_READY=1
|
|
elif [ "$PACKAGE_LOCK_SHA" = "$INSTALLED_PACKAGE_LOCK_SHA" ] && "$DIR/.venv/bin/python3" -c "import iqdbc, msgq, panda, tinygrad" 2>/dev/null \
|
|
&& "$DIR/.venv/bin/python3" "$DIR/iqpilot/system/runtime_packages_verify.py"; then
|
|
# a top-level import passes on a partially extracted install (lazy backends),
|
|
# so readiness also requires every wheel RECORD file to exist on disk
|
|
PACKAGES_READY=1
|
|
fi
|
|
RUNTIME_WHEELS_READY=0
|
|
if [ -d "$DIR/artifacts/package_runtime" ] && PYTHONPATH="$DIR/artifacts/package_runtime" /usr/local/venv/bin/python3 -c "import libdatachannel" 2>/dev/null; then
|
|
RUNTIME_WHEELS_READY=1
|
|
elif [ "$RUNTIME_WHEEL_LOCK_SHA" = "$INSTALLED_RUNTIME_WHEEL_LOCK_SHA" ] && "$DIR/.venv/bin/python3" -c "import libdatachannel" 2>/dev/null \
|
|
&& PYTHONPATH="$DIR" "$DIR/.venv/bin/python3" "$DIR/iqpilot/system/runtime_wheels_verify.py"; then
|
|
RUNTIME_WHEELS_READY=1
|
|
fi
|
|
if [ "$PACKAGES_READY" != "1" ] || [ "$RUNTIME_WHEELS_READY" != "1" ]; then
|
|
UV_CACHE_DIR="$DIR/.uv-cache"
|
|
if [ ! -x "$DIR/.venv/bin/python3" ]; then
|
|
UV_CACHE_DIR="$UV_CACHE_DIR" uv venv --python /usr/local/venv/bin/python3 "$DIR/.venv" || return 1
|
|
VENV_SITE_PACKAGES="$("$DIR/.venv/bin/python3" -c 'import site; print(site.getsitepackages()[0])')"
|
|
fi
|
|
sudo chown -R "$(id -u):$(id -g)" "$DIR/.venv" "$UV_CACHE_DIR" "$DIR/artifacts/package_sources" 2>/dev/null || true
|
|
if [ "${#RUNTIME_WHEELS[@]}" = "0" ] || [ "${#RUNTIME_WHEEL_SOURCES[@]}" = "0" ]; then
|
|
return 1
|
|
fi
|
|
if [ "$PACKAGES_READY" != "1" ]; then
|
|
IQDBC_PACKAGE_SOURCE=""
|
|
PACKAGE_SOURCES=()
|
|
while IFS=$'\t' read -r package_name package_source; do
|
|
PACKAGE_SOURCES+=("$package_source")
|
|
if [ "$package_name" = "iqdbc" ]; then
|
|
IQDBC_PACKAGE_SOURCE="$package_source"
|
|
fi
|
|
done < <(/usr/local/venv/bin/python3 "$DIR/iqpilot/system/runtime_package_sources.py" "$DIR")
|
|
if [ "${#PACKAGE_SOURCES[@]}" = "0" ] || [ -z "$IQDBC_PACKAGE_SOURCE" ]; then
|
|
return 1
|
|
fi
|
|
PACKAGE_BUILD_PYTHONPATH="$BASE_SITE_PACKAGES:$VENV_SITE_PACKAGES"
|
|
# The base AGNOS venv ships Eigen as a Python package instead of under
|
|
# /usr/include, so source-package builds need its install directory on the compiler include path.
|
|
# Resolve it through Python rather than pinning the Python minor version.
|
|
EIGEN_INCLUDE_ROOT="$(/usr/local/venv/bin/python3 -c \
|
|
'from pathlib import Path; import eigen; root = Path(eigen.__file__).resolve().parent / "install"; assert (root / "eigen3/Eigen/Dense").is_file(); print(root)' \
|
|
2>/dev/null || true)"
|
|
PACKAGE_BUILD_CPATH="${CPATH:-}"
|
|
if [ -n "$EIGEN_INCLUDE_ROOT" ]; then
|
|
PACKAGE_BUILD_CPATH="$EIGEN_INCLUDE_ROOT${PACKAGE_BUILD_CPATH:+:$PACKAGE_BUILD_CPATH}"
|
|
fi
|
|
UV_CACHE_DIR="$UV_CACHE_DIR" PYTHONPATH="$PACKAGE_BUILD_PYTHONPATH" CPATH="$PACKAGE_BUILD_CPATH" PATH="/usr/local/venv/bin:/usr/bin:$PATH" \
|
|
uv pip install --python "$DIR/.venv/bin/python" --no-build-isolation --no-deps --reinstall "$IQDBC_PACKAGE_SOURCE" || return 1
|
|
UV_CACHE_DIR="$UV_CACHE_DIR" PYTHONPATH="$PACKAGE_BUILD_PYTHONPATH" CPATH="$PACKAGE_BUILD_CPATH" PATH="/usr/local/venv/bin:/usr/bin:$PATH" \
|
|
uv pip install --python "$DIR/.venv/bin/python" --no-build-isolation --no-deps --reinstall "${PACKAGE_SOURCES[@]}" || return 1
|
|
printf '%s\n' "$PACKAGE_LOCK_SHA" > "$DIR/.iqpilot-package-lock-sha256"
|
|
fi
|
|
if [ "$RUNTIME_WHEELS_READY" != "1" ]; then
|
|
UV_CACHE_DIR="$UV_CACHE_DIR" PATH="/usr/local/venv/bin:/usr/bin:$PATH" \
|
|
uv pip install --python "$DIR/.venv/bin/python" --no-deps --reinstall "${RUNTIME_WHEEL_SOURCES[@]}" || return 1
|
|
"$DIR/.venv/bin/python3" -c "import libdatachannel" || return 1
|
|
PYTHONPATH="$DIR" "$DIR/.venv/bin/python3" "$DIR/iqpilot/system/runtime_wheels_verify.py" || return 1
|
|
printf '%s\n' "$RUNTIME_WHEEL_LOCK_SHA" > "$DIR/.iqpilot-runtime-wheel-lock-sha256"
|
|
fi
|
|
fi
|
|
if [ -n "$VENV_SITE_PACKAGES" ]; then
|
|
printf 'import site; site.addsitedir("%s")\n' "$BASE_SITE_PACKAGES" | sudo tee "$VENV_SITE_PACKAGES/iqpilot-system-venv.pth" >/dev/null
|
|
fi
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
sync_python_env
|
|
exit $?
|
|
fi
|
|
|
|
# NOTE: keep this file in lockstep with the launch-time expectations above --
|
|
# updated.py invokes it standalone and launch_chffrplus.sh sources it.
|