298 lines
9.8 KiB
Bash
298 lines
9.8 KiB
Bash
#!/bin/bash
|
|
|
|
# Copyright 2019-2025 NVIDIA CORPORATION
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
SAVED_SETTINGS=`echo $-`
|
|
# Fail on undefined variables
|
|
set -u
|
|
# Don't exit on errors
|
|
set +e
|
|
|
|
if echo ${PM_VERBOSITY-} | grep -i "debug" > /dev/null ; then
|
|
set -x
|
|
PM_CURL_SILENT=""
|
|
PM_WGET_QUIET=""
|
|
else
|
|
PM_CURL_SILENT="-s -S"
|
|
PM_WGET_QUIET="--quiet"
|
|
fi
|
|
export PM_PACKMAN_VERSION=8.7.0
|
|
|
|
|
|
cleanup() {
|
|
# Remove temporary variable file if it exists
|
|
if [[ -n "${PM_VAR_PATH:-}" && -f "$PM_VAR_PATH" ]]; then
|
|
rm -f "$PM_VAR_PATH"
|
|
fi
|
|
if [[ -n "${PM_VAR_DIR:-}" && -d "$PM_VAR_DIR" ]]; then
|
|
rmdir "$PM_VAR_DIR"
|
|
fi
|
|
|
|
# Restore the settings we may have altered during our run
|
|
if [[ "$SAVED_SETTINGS" == *"e"* ]]; then
|
|
set -e
|
|
fi
|
|
|
|
if [[ "$SAVED_SETTINGS" != *"x"* ]]; then
|
|
set +x
|
|
fi
|
|
|
|
# the e setting is always set in source mode so we remove it here so it
|
|
# doesn't leak (in regular ./ execution doesn't matter)
|
|
set +u
|
|
}
|
|
|
|
main() {
|
|
# This is necessary for newer macOS
|
|
if [ `uname` == 'Darwin' ]; then
|
|
export LC_ALL=en_US.UTF-8
|
|
export LANG=en_US.UTF-8
|
|
fi
|
|
|
|
# We cannot rely on realpath, it isn't installed on macOS and some Linux distros
|
|
get_abs_filename() {
|
|
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
|
}
|
|
|
|
# Specify where packman command exists
|
|
export PM_INSTALL_PATH="$(get_abs_filename "$(dirname "${BASH_SOURCE}")")"
|
|
|
|
# The packages root may already be configured by the user
|
|
if [ -z "${PM_PACKAGES_ROOT:-}" ]; then
|
|
# Set variable temporarily in this process so that the following execution will work
|
|
if [ `uname` == 'Darwin' ]; then
|
|
export PM_PACKAGES_ROOT="${HOME}/Library/Application Support/packman-cache"
|
|
else
|
|
if [ -z "${XDG_CACHE_HOME:-}" ]; then
|
|
export PM_PACKAGES_ROOT="${HOME}/.cache/packman"
|
|
else
|
|
export PM_PACKAGES_ROOT="${XDG_CACHE_HOME}/packman"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Ensure the packages root path exists:
|
|
if [ ! -d "$PM_PACKAGES_ROOT" ]; then
|
|
echo "Creating packman packages cache at $PM_PACKAGES_ROOT"
|
|
mkdir -p "$PM_PACKAGES_ROOT" || return 1
|
|
fi
|
|
|
|
execute_with_retry()
|
|
{
|
|
local CMD="$1"
|
|
local MAX_TRIES=4
|
|
local DELAY=2
|
|
local TRIES=0
|
|
local exit_code
|
|
|
|
while [ $TRIES -lt $MAX_TRIES ]
|
|
do
|
|
((TRIES++))
|
|
eval $CMD
|
|
exit_code=$?
|
|
if [ $exit_code -eq 0 ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ $TRIES -lt $MAX_TRIES ]; then
|
|
echo "Attempt $TRIES failed. Retrying in $DELAY seconds ..."
|
|
sleep $DELAY
|
|
DELAY=$((DELAY * DELAY))
|
|
echo "Retrying ..."
|
|
fi
|
|
done
|
|
|
|
echo "Command failed after $MAX_TRIES attempts: $CMD"
|
|
return $exit_code
|
|
}
|
|
|
|
fetch_file_from_s3()
|
|
{
|
|
local SOURCE=$1
|
|
local SOURCE_URL=https://bootstrap.packman.nvidia.com/$SOURCE
|
|
local TARGET=$2
|
|
echo "Fetching $SOURCE from bootstrap.packman.nvidia.com ..."
|
|
local CMD="curl -o $TARGET $SOURCE_URL $PM_CURL_SILENT"
|
|
if command -v wget >/dev/null 2>&1; then
|
|
CMD="wget $PM_WGET_QUIET -O$TARGET $SOURCE_URL"
|
|
fi
|
|
execute_with_retry "$CMD"
|
|
return $?
|
|
}
|
|
|
|
generate_temp_file_name()
|
|
{
|
|
if [ `uname` == "Darwin" ]; then
|
|
local tmpfile=`mktemp -t packman`
|
|
else
|
|
local tmpfile=`mktemp -t packman.XXXXXXXX`
|
|
fi
|
|
echo "$tmpfile"
|
|
}
|
|
|
|
install_python()
|
|
{
|
|
PLATFORM=`uname`
|
|
PROCESSOR=`uname -m`
|
|
PYTHON_VERSION=3.12.15-nv1-pf
|
|
PYTHON_EXE=python
|
|
PYTHON_CONTAINER=tar.gz
|
|
|
|
if [ $PLATFORM == 'Darwin' ]; then
|
|
PYTHON_PACKAGE=3.12.15-macos-aarch64
|
|
elif [ $PLATFORM == 'Linux' ] && [ $PROCESSOR == 'x86_64' ]; then
|
|
PYTHON_PACKAGE=$PYTHON_VERSION-manylinux_2_35-x86_64
|
|
elif [ $PLATFORM == 'Linux' ] && [ $PROCESSOR == 'aarch64' ]; then
|
|
PYTHON_PACKAGE=$PYTHON_VERSION-manylinux_2_35-aarch64
|
|
elif [[ $PLATFORM =~ ^MINGW.* ]] && [ $PROCESSOR == 'x86_64' ]; then
|
|
PYTHON_PACKAGE=$PYTHON_VERSION-windows-x86_64
|
|
PYTHON_EXE=python.exe
|
|
PYTHON_CONTAINER=zip
|
|
else
|
|
echo "Operating system not supported"
|
|
return 1
|
|
fi
|
|
|
|
PYTHON_INSTALL_ROOT="$PM_PACKAGES_ROOT/python"
|
|
PYTHON_INSTALL_FOLDER="$PYTHON_INSTALL_ROOT/$PYTHON_PACKAGE"
|
|
export PM_PYTHON="$PYTHON_INSTALL_FOLDER/$PYTHON_EXE"
|
|
|
|
if [ ! -f "$PM_PYTHON" ]; then
|
|
mkdir -p "$PYTHON_INSTALL_ROOT" || return 1
|
|
|
|
PYTHON_PACKAGE_TMP=""
|
|
PYTHON_INSTALL_STAGING_PARENT=""
|
|
PYTHON_INSTALL_STATUS=0
|
|
cleanup_python_install()
|
|
{
|
|
local CLEANUP_STATUS=0
|
|
if [ -n "$PYTHON_PACKAGE_TMP" ]; then
|
|
rm -f "$PYTHON_PACKAGE_TMP" || CLEANUP_STATUS=1
|
|
fi
|
|
if [ -n "$PYTHON_INSTALL_STAGING_PARENT" ]; then
|
|
rm -rf "$PYTHON_INSTALL_STAGING_PARENT" || CLEANUP_STATUS=1
|
|
fi
|
|
return $CLEANUP_STATUS
|
|
}
|
|
|
|
PYTHON_PACKAGE_TMP=$(generate_temp_file_name)
|
|
fetch_file_from_s3 "python@$PYTHON_PACKAGE.$PYTHON_CONTAINER" "$PYTHON_PACKAGE_TMP" || PYTHON_INSTALL_STATUS=1
|
|
|
|
if [ $PYTHON_INSTALL_STATUS -eq 0 ]; then
|
|
PYTHON_INSTALL_STAGING_PARENT=$(mktemp -d "$PYTHON_INSTALL_ROOT/$PYTHON_PACKAGE.tmp.XXXXXXXX") || PYTHON_INSTALL_STATUS=1
|
|
fi
|
|
|
|
if [ $PYTHON_INSTALL_STATUS -eq 0 ]; then
|
|
PYTHON_INSTALL_TMP="$PYTHON_INSTALL_STAGING_PARENT/$PYTHON_PACKAGE"
|
|
mkdir "$PYTHON_INSTALL_TMP" || PYTHON_INSTALL_STATUS=1
|
|
fi
|
|
|
|
if [ $PYTHON_INSTALL_STATUS -eq 0 ]; then
|
|
echo "Unpacking python"
|
|
if [ "$PYTHON_CONTAINER" == "zip" ]; then
|
|
unzip -q "$PYTHON_PACKAGE_TMP" -d "$PYTHON_INSTALL_TMP" || PYTHON_INSTALL_STATUS=1
|
|
else
|
|
tar -xf "$PYTHON_PACKAGE_TMP" -C "$PYTHON_INSTALL_TMP" || PYTHON_INSTALL_STATUS=1
|
|
fi
|
|
fi
|
|
|
|
if [ $PYTHON_INSTALL_STATUS -eq 0 ]; then
|
|
# Move a version-named child into the install root instead of moving the
|
|
# unique temp folder directly to $PYTHON_INSTALL_FOLDER. If another
|
|
# process has already created $PYTHON_INSTALL_FOLDER, plain mv treats
|
|
# that existing folder as a parent and may nest the source inside it.
|
|
# Keeping the source basename equal to $PYTHON_PACKAGE makes that
|
|
# competing install fail cleanly instead.
|
|
mv "$PYTHON_INSTALL_TMP" "$PYTHON_INSTALL_ROOT" 2> /dev/null || {
|
|
if [ ! -f "$PM_PYTHON" ]; then
|
|
echo "Failed to install Python to '$PYTHON_INSTALL_FOLDER'. Please remove the directory and try again."
|
|
PYTHON_INSTALL_STATUS=1
|
|
fi
|
|
}
|
|
fi
|
|
|
|
cleanup_python_install || PYTHON_INSTALL_STATUS=1
|
|
return $PYTHON_INSTALL_STATUS
|
|
fi
|
|
}
|
|
|
|
# Ensure python is available:
|
|
if [ -z "${PM_PYTHON_EXT:-}" ]; then
|
|
install_python || return 1
|
|
else
|
|
PM_PYTHON="$PM_PYTHON_EXT"
|
|
fi
|
|
|
|
# The packman module may be externally configured
|
|
if [ -z "${PM_MODULE_DIR_EXT:-}" ]; then
|
|
PM_MODULE_DIR="$PM_PACKAGES_ROOT/packman-common/$PM_PACKMAN_VERSION"
|
|
else
|
|
PM_MODULE_DIR="$PM_MODULE_DIR_EXT"
|
|
fi
|
|
export PM_MODULE="$PM_MODULE_DIR/run.py"
|
|
|
|
# Ensure the packman package exists:
|
|
if [ ! -f "$PM_MODULE" ]; then
|
|
# Remove a previously corrupt packman-common if it's there
|
|
if [ -d "$PM_MODULE_DIR" ]; then
|
|
rm -rf "$PM_MODULE_DIR" || return 1
|
|
fi
|
|
PM_MODULE_PACKAGE="packman-common@$PM_PACKMAN_VERSION.zip"
|
|
TARGET=$(generate_temp_file_name)
|
|
# We always fetch packman from S3:
|
|
fetch_file_from_s3 "$PM_MODULE_PACKAGE" "$TARGET" || return 1
|
|
echo "Unpacking ..."
|
|
"$PM_PYTHON" -S -s -u -E "$PM_INSTALL_PATH/bootstrap/install_package.py" "$TARGET" "$PM_MODULE_DIR" || return 1
|
|
rm "$TARGET" || return 1
|
|
fi
|
|
|
|
# Use a private directory so the variable file cannot be replaced before Python opens it.
|
|
PM_VAR_DIR=$(mktemp -d -t tmp.$$.pmvars.XXXXXX) || return 1
|
|
PM_VAR_PATH="$PM_VAR_DIR/variables"
|
|
|
|
if [ $# -ne 0 ]; then
|
|
PM_VAR_PATH_ARG=--var-path="$PM_VAR_PATH"
|
|
else
|
|
PM_VAR_PATH_ARG=""
|
|
fi
|
|
|
|
"$PM_PYTHON" -S -s -u -E "$PM_MODULE" "$@" ${PM_VAR_PATH_ARG:-} || return 1
|
|
# Export the variables if the file was used:
|
|
if [ -f "$PM_VAR_PATH" ]; then
|
|
while read -r line
|
|
do
|
|
name=${line%%=*}
|
|
if [[ "$line" == *=* && "$name" == PM_* && "$name" != *[!A-Za-z0-9_]* ]]; then
|
|
export "$line"
|
|
fi
|
|
done < "$PM_VAR_PATH"
|
|
fi
|
|
|
|
# Return success
|
|
return 0
|
|
}
|
|
|
|
main "$@"
|
|
exit_code=$?
|
|
cleanup
|
|
|
|
# Determine execution context and return accordingly
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
# This is direct execution
|
|
exit $exit_code
|
|
else
|
|
# This is sourced execution
|
|
return $exit_code
|
|
fi
|