|
|
#!/usr/bin/env bash |
|
|
set -euo pipefail |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PYTHON_VERSION="${PYTHON_VERSION:-3.10}" |
|
|
PYTHON_PATCH="${PYTHON_PATCH:-18}" |
|
|
PY_STANDALONE_TAG="${PY_STANDALONE_TAG:-20250818}" |
|
|
|
|
|
PORTABLE_PYTHON="${PORTABLE_PYTHON:-false}" |
|
|
|
|
|
|
|
|
|
|
|
: "${PORTABLE_PY_FILENAME:=}" |
|
|
: "${PORTABLE_PY_SHA256:=}" |
|
|
|
|
|
|
|
|
|
|
|
if [ "x${USE_PIP:-}" == "x" ]; then |
|
|
USE_PIP=false |
|
|
fi |
|
|
|
|
|
|
|
|
function _is_musl() { |
|
|
|
|
|
if command -v ldd >/dev/null 2>&1; then |
|
|
ldd --version 2>&1 | grep -qi musl && return 0 |
|
|
fi |
|
|
|
|
|
if command -v getconf >/dev/null 2>&1; then |
|
|
getconf GNU_LIBC_VERSION >/dev/null 2>&1 || return 0 |
|
|
fi |
|
|
return 1 |
|
|
} |
|
|
|
|
|
function _triple() { |
|
|
local os="" arch="" libc="gnu" |
|
|
case "$(uname -s)" in |
|
|
Linux*) os="unknown-linux" ;; |
|
|
Darwin*) os="apple-darwin" ;; |
|
|
MINGW*|MSYS*|CYGWIN*) os="pc-windows-msvc" ;; |
|
|
*) echo "Unsupported OS $(uname -s)"; exit 1;; |
|
|
esac |
|
|
|
|
|
case "$(uname -m)" in |
|
|
x86_64) arch="x86_64" ;; |
|
|
aarch64|arm64) arch="aarch64" ;; |
|
|
armv7l) arch="armv7" ;; |
|
|
i686|i386) arch="i686" ;; |
|
|
ppc64le) arch="ppc64le" ;; |
|
|
s390x) arch="s390x" ;; |
|
|
riscv64) arch="riscv64" ;; |
|
|
*) echo "Unsupported arch $(uname -m)"; exit 1;; |
|
|
esac |
|
|
|
|
|
if [[ "$os" == "unknown-linux" ]]; then |
|
|
if _is_musl; then |
|
|
libc="musl" |
|
|
else |
|
|
libc="gnu" |
|
|
fi |
|
|
echo "${arch}-${os}-${libc}" |
|
|
else |
|
|
echo "${arch}-${os}" |
|
|
fi |
|
|
} |
|
|
|
|
|
function _portable_dir() { |
|
|
echo "${EDIR}/python" |
|
|
} |
|
|
|
|
|
function _portable_bin() { |
|
|
|
|
|
echo "$(_portable_dir)/bin" |
|
|
} |
|
|
|
|
|
function _portable_python() { |
|
|
if [ -x "$(_portable_bin)/python3" ]; then |
|
|
echo "$(_portable_bin)/python3" |
|
|
else |
|
|
echo "$(_portable_bin)/python" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_macosPortableEnv() { |
|
|
if [ "$(uname -s)" = "Darwin" ]; then |
|
|
export DYLD_LIBRARY_PATH="$(_portable_dir)/lib${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}}" |
|
|
export DYLD_FALLBACK_LIBRARY_PATH="$(_portable_dir)/lib${DYLD_FALLBACK_LIBRARY_PATH:+:${DYLD_FALLBACK_LIBRARY_PATH}}" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
_unquarantinePortablePython() { |
|
|
if [ "$(uname -s)" = "Darwin" ]; then |
|
|
command -v xattr >/dev/null 2>&1 && xattr -dr com.apple.quarantine "$(_portable_dir)" || true |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
function ensurePortablePython() { |
|
|
local pdir="$(_portable_dir)" |
|
|
local pbin="$(_portable_bin)" |
|
|
local pyexe |
|
|
|
|
|
if [ -x "${pbin}/python3" ] || [ -x "${pbin}/python" ]; then |
|
|
_macosPortableEnv |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
mkdir -p "${pdir}" |
|
|
local triple="$(_triple)" |
|
|
|
|
|
local full_ver="${PYTHON_VERSION}.${PYTHON_PATCH}" |
|
|
local fn="" |
|
|
if [ -n "${PORTABLE_PY_FILENAME}" ]; then |
|
|
fn="${PORTABLE_PY_FILENAME}" |
|
|
else |
|
|
|
|
|
fn="cpython-${full_ver}+${PY_STANDALONE_TAG}-${triple}-install_only.tar.gz" |
|
|
fi |
|
|
|
|
|
local url="https://github.com/astral-sh/python-build-standalone/releases/download/${PY_STANDALONE_TAG}/${fn}" |
|
|
local tmp="${pdir}/${fn}" |
|
|
echo "Downloading portable Python: ${fn}" |
|
|
|
|
|
if command -v curl >/dev/null 2>&1; then |
|
|
curl -L --fail --retry 3 --retry-delay 1 -o "${tmp}" "${url}" |
|
|
else |
|
|
wget -O "${tmp}" "${url}" |
|
|
fi |
|
|
|
|
|
if [ -n "${PORTABLE_PY_SHA256}" ]; then |
|
|
echo "${PORTABLE_PY_SHA256} ${tmp}" | sha256sum -c - |
|
|
fi |
|
|
|
|
|
echo "Extracting ${fn} -> ${pdir}" |
|
|
|
|
|
tar -xzf "${tmp}" -C "${pdir}" |
|
|
rm -f "${tmp}" |
|
|
|
|
|
|
|
|
|
|
|
local inner |
|
|
inner="$(find "${pdir}" -type f -path "*/bin/python*" -maxdepth 3 2>/dev/null | head -n1 || true)" |
|
|
if [ -n "${inner}" ]; then |
|
|
local inner_root |
|
|
inner_root="$(dirname "$(dirname "${inner}")")" |
|
|
if [ "${inner_root}" != "${pdir}" ]; then |
|
|
|
|
|
shopt -s dotglob |
|
|
mv "${inner_root}/"* "${pdir}/" |
|
|
rm -rf "${inner_root}" |
|
|
shopt -u dotglob |
|
|
fi |
|
|
fi |
|
|
|
|
|
_unquarantinePortablePython |
|
|
_macosPortableEnv |
|
|
|
|
|
pyexe="$(_portable_python)" |
|
|
"${pyexe}" -V |
|
|
} |
|
|
|
|
|
|
|
|
function init() { |
|
|
BACKEND_NAME=${PWD##*/} |
|
|
MY_DIR=$(realpath "$(dirname "$0")") |
|
|
BUILD_PROFILE=$(getBuildProfile) |
|
|
|
|
|
EDIR=${MY_DIR} |
|
|
if [ "x${ENV_DIR:-}" != "x" ]; then |
|
|
EDIR=${ENV_DIR} |
|
|
fi |
|
|
|
|
|
if [ ! -z "${LIMIT_TARGETS:-}" ]; then |
|
|
isValidTarget=$(checkTargets ${LIMIT_TARGETS}) |
|
|
if [ ${isValidTarget} != true ]; then |
|
|
echo "${BACKEND_NAME} can only be used on the following targets: ${LIMIT_TARGETS}" |
|
|
exit 0 |
|
|
fi |
|
|
fi |
|
|
|
|
|
echo "Initializing libbackend for ${BACKEND_NAME}" |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getBuildProfile() { |
|
|
if [ x"${BUILD_TYPE:-}" == "xcublas" ] || [ x"${BUILD_TYPE:-}" == "xl4t" ]; then |
|
|
if [ ! -z "${CUDA_MAJOR_VERSION:-}" ]; then |
|
|
echo ${BUILD_TYPE}${CUDA_MAJOR_VERSION} |
|
|
else |
|
|
echo ${BUILD_TYPE} |
|
|
fi |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
if [ -d "/opt/intel" ]; then |
|
|
echo "intel" |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
if [ -n "${BUILD_TYPE:-}" ]; then |
|
|
echo ${BUILD_TYPE} |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
echo "cpu" |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_makeVenvPortable() { |
|
|
local update_pyvenv_cfg=false |
|
|
if [ "${1:-}" = "--update-pyvenv-cfg" ]; then |
|
|
update_pyvenv_cfg=true |
|
|
fi |
|
|
|
|
|
local venv_dir="${EDIR}/venv" |
|
|
local vbin="${venv_dir}/bin" |
|
|
|
|
|
[ -d "${vbin}" ] || return 0 |
|
|
|
|
|
|
|
|
|
|
|
local rel_py='../../python/bin/python3' |
|
|
|
|
|
for name in python3 python; do |
|
|
if [ -e "${vbin}/${name}" ] || [ -L "${vbin}/${name}" ]; then |
|
|
rm -f "${vbin}/${name}" |
|
|
fi |
|
|
done |
|
|
ln -s "${rel_py}" "${vbin}/python3" |
|
|
ln -s "python3" "${vbin}/python" |
|
|
|
|
|
|
|
|
|
|
|
if [ "$update_pyvenv_cfg" = "true" ]; then |
|
|
local pyvenv_cfg="${venv_dir}/pyvenv.cfg" |
|
|
if [ -f "${pyvenv_cfg}" ]; then |
|
|
local portable_dir="$(_portable_dir)" |
|
|
|
|
|
|
|
|
if [ -d "${portable_dir}" ]; then |
|
|
portable_dir="$(cd "${portable_dir}" && pwd)" |
|
|
else |
|
|
|
|
|
portable_dir="../python" |
|
|
fi |
|
|
local sed_i=(sed -i) |
|
|
|
|
|
if sed --version >/dev/null 2>&1; then |
|
|
sed_i=(sed -i) |
|
|
else |
|
|
sed_i=(sed -i '') |
|
|
fi |
|
|
|
|
|
|
|
|
if grep -q "^home = " "${pyvenv_cfg}"; then |
|
|
"${sed_i[@]}" "s|^home = .*|home = ${portable_dir}|" "${pyvenv_cfg}" |
|
|
else |
|
|
|
|
|
echo "home = ${portable_dir}" >> "${pyvenv_cfg}" |
|
|
fi |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
local ve_abs="${vbin}/python" |
|
|
local sed_i=(sed -i) |
|
|
|
|
|
if sed --version >/dev/null 2>&1; then |
|
|
sed_i=(sed -i) |
|
|
else |
|
|
sed_i=(sed -i '') |
|
|
fi |
|
|
|
|
|
for f in "${vbin}"/*; do |
|
|
[ -f "$f" ] || continue |
|
|
|
|
|
head -c2 "$f" 2>/dev/null | grep -q '^#!' || continue |
|
|
|
|
|
if head -n1 "$f" | grep -Fq "${ve_abs}"; then |
|
|
"${sed_i[@]}" '1s|^#!.*$|#!/usr/bin/env python3|' "$f" |
|
|
chmod +x "$f" 2>/dev/null || true |
|
|
fi |
|
|
done |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function ensureVenv() { |
|
|
local interpreter="" |
|
|
|
|
|
if [ "x${PORTABLE_PYTHON}" == "xtrue" ] || [ -e "$(_portable_python)" ]; then |
|
|
echo "Using portable Python" |
|
|
ensurePortablePython |
|
|
interpreter="$(_portable_python)" |
|
|
else |
|
|
|
|
|
if command -v python${PYTHON_VERSION} >/dev/null 2>&1; then |
|
|
interpreter="python${PYTHON_VERSION}" |
|
|
elif command -v python3 >/dev/null 2>&1; then |
|
|
interpreter="python3" |
|
|
else |
|
|
echo "No suitable system Python found, bootstrapping portable build..." |
|
|
ensurePortablePython |
|
|
interpreter="$(_portable_python)" |
|
|
fi |
|
|
fi |
|
|
|
|
|
if [ ! -d "${EDIR}/venv" ]; then |
|
|
if [ "x${USE_PIP}" == "xtrue" ]; then |
|
|
"${interpreter}" -m venv --copies "${EDIR}/venv" |
|
|
source "${EDIR}/venv/bin/activate" |
|
|
"${interpreter}" -m pip install --upgrade pip |
|
|
else |
|
|
if [ "x${PORTABLE_PYTHON}" == "xtrue" ]; then |
|
|
uv venv --python "${interpreter}" "${EDIR}/venv" |
|
|
else |
|
|
uv venv --python "${PYTHON_VERSION}" "${EDIR}/venv" |
|
|
fi |
|
|
fi |
|
|
if [ "x${PORTABLE_PYTHON}" == "xtrue" ]; then |
|
|
|
|
|
_makeVenvPortable |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if [ -x "$(_portable_python)" ]; then |
|
|
_macosPortableEnv |
|
|
fi |
|
|
|
|
|
if [ "x${VIRTUAL_ENV:-}" != "x${EDIR}/venv" ]; then |
|
|
source "${EDIR}/venv/bin/activate" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
function runProtogen() { |
|
|
ensureVenv |
|
|
if [ "x${USE_PIP}" == "xtrue" ]; then |
|
|
pip install grpcio-tools |
|
|
else |
|
|
uv pip install grpcio-tools |
|
|
fi |
|
|
pushd "${EDIR}" >/dev/null |
|
|
|
|
|
python -m grpc_tools.protoc -I../../ -I./ --python_out=. --grpc_python_out=. backend.proto |
|
|
popd >/dev/null |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function installRequirements() { |
|
|
ensureVenv |
|
|
declare -a requirementFiles=( |
|
|
"${EDIR}/requirements-install.txt" |
|
|
"${EDIR}/requirements.txt" |
|
|
"${EDIR}/requirements-${BUILD_TYPE:-}.txt" |
|
|
) |
|
|
|
|
|
if [ "x${BUILD_TYPE:-}" != "x${BUILD_PROFILE}" ]; then |
|
|
requirementFiles+=("${EDIR}/requirements-${BUILD_PROFILE}.txt") |
|
|
fi |
|
|
if [ "x${BUILD_TYPE:-}" == "x" ]; then |
|
|
requirementFiles+=("${EDIR}/requirements-cpu.txt") |
|
|
fi |
|
|
requirementFiles+=("${EDIR}/requirements-after.txt") |
|
|
if [ "x${BUILD_TYPE:-}" != "x${BUILD_PROFILE}" ]; then |
|
|
requirementFiles+=("${EDIR}/requirements-${BUILD_PROFILE}-after.txt") |
|
|
fi |
|
|
|
|
|
|
|
|
if [ "x${PORTABLE_PYTHON}" == "xtrue" ]; then |
|
|
export C_INCLUDE_PATH="${C_INCLUDE_PATH:-}:$(_portable_dir)/include/python${PYTHON_VERSION}" |
|
|
fi |
|
|
|
|
|
for reqFile in ${requirementFiles[@]}; do |
|
|
if [ -f "${reqFile}" ]; then |
|
|
echo "starting requirements install for ${reqFile}" |
|
|
if [ "x${USE_PIP}" == "xtrue" ]; then |
|
|
pip install ${EXTRA_PIP_INSTALL_FLAGS:-} --requirement "${reqFile}" |
|
|
else |
|
|
uv pip install ${EXTRA_PIP_INSTALL_FLAGS:-} --requirement "${reqFile}" |
|
|
fi |
|
|
echo "finished requirements install for ${reqFile}" |
|
|
fi |
|
|
done |
|
|
|
|
|
runProtogen |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function startBackend() { |
|
|
ensureVenv |
|
|
|
|
|
|
|
|
if [ "x${PORTABLE_PYTHON}" == "xtrue" ] || [ -x "$(_portable_python)" ]; then |
|
|
_makeVenvPortable --update-pyvenv-cfg |
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if [ -d "${EDIR}/lib" ]; then |
|
|
export LD_LIBRARY_PATH="${EDIR}/lib:${LD_LIBRARY_PATH:-}" |
|
|
echo "Added ${EDIR}/lib to LD_LIBRARY_PATH for GPU libraries" |
|
|
fi |
|
|
|
|
|
if [ ! -z "${BACKEND_FILE:-}" ]; then |
|
|
exec "${EDIR}/venv/bin/python" "${BACKEND_FILE}" "$@" |
|
|
elif [ -e "${MY_DIR}/server.py" ]; then |
|
|
exec "${EDIR}/venv/bin/python" "${MY_DIR}/server.py" "$@" |
|
|
elif [ -e "${MY_DIR}/backend.py" ]; then |
|
|
exec "${EDIR}/venv/bin/python" "${MY_DIR}/backend.py" "$@" |
|
|
elif [ -e "${MY_DIR}/${BACKEND_NAME}.py" ]; then |
|
|
exec "${EDIR}/venv/bin/python" "${MY_DIR}/${BACKEND_NAME}.py" "$@" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function runUnittests() { |
|
|
ensureVenv |
|
|
if [ ! -z "${TEST_FILE:-}" ]; then |
|
|
testDir=$(dirname "$(realpath "${TEST_FILE}")") |
|
|
testFile=$(basename "${TEST_FILE}") |
|
|
pushd "${testDir}" >/dev/null |
|
|
python -m unittest "${testFile}" |
|
|
popd >/dev/null |
|
|
elif [ -f "${MY_DIR}/test.py" ]; then |
|
|
pushd "${MY_DIR}" >/dev/null |
|
|
python -m unittest test.py |
|
|
popd >/dev/null |
|
|
else |
|
|
echo "no tests defined for ${BACKEND_NAME}" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function checkTargets() { |
|
|
targets=$@ |
|
|
declare -a targets=($targets) |
|
|
for target in ${targets[@]}; do |
|
|
if [ "x${BUILD_TYPE:-}" == "x${target}" ]; then |
|
|
echo true; return 0 |
|
|
fi |
|
|
if [ "x${BUILD_PROFILE}" == "x${target}" ]; then |
|
|
echo true; return 0 |
|
|
fi |
|
|
done |
|
|
echo false |
|
|
} |
|
|
|
|
|
init |
|
|
|