#!/bin/sh # bootstrap/python # # Python-finding script for all `sh` environments, including Linux, MSYS2, # Git for Windows, and GitHub Desktop. Invokable from CLI or automation. # # If a python.exe installed by `python_.ps1` is present, it will be used. # Otherwise, this script requires a system `python3` and `pip` to be provided, # and will create a standard virtualenv in which to install `requirements.txt`. set -e # Convenience variables Bootstrap="$(dirname "$0")" Sdk="$(dirname "$Bootstrap")" Cache="$Bootstrap/.cache" if [ "$TG_BOOTSTRAP_CACHE" ]; then Cache="$TG_BOOTSTRAP_CACHE" fi OldPWD="$PWD" cd "$Bootstrap/../.." . ./dependencies.sh # sets PYTHON_VERSION cd "$OldPWD" PythonVersion="$PYTHON_VERSION" PythonDir="$Cache/python-$PythonVersion" PythonExe="$PythonDir/python.exe" Log="$Cache/last-command.log" # If a portable Python for Windows is not present, search on $PATH. if [ "$(uname)" = "Linux" ] || [ ! -f "$PythonExe" ]; then # Strip the "App Execution Aliases" from $PATH. Even if the user installed # Python using the Windows Store on purpose, these aliases always generate # "Permission denied" errors when sh.exe tries to invoke them. PATH=$(echo "$PATH" | tr ":" "\n" | grep -v "AppData/Local/Microsoft/WindowsApps" | tr "\n" ":") # Try to find a Python executable. if command -v python3 >/dev/null 2>&1; then PythonExe=python3 elif command -v python >/dev/null 2>&1; then PythonExe=python elif command -v py >/dev/null 2>&1; then PythonExe="py -3" else echo if command -v apt-get >/dev/null 2>&1; then echo "Please install Python using your system's package manager:" echo " sudo apt-get install python3 python3-pip" elif [ "$(uname -o)" = "Msys" ]; then echo "Please run tools/bootstrap/python.bat instead of tools/bootstrap/python once to" echo "install Python automatically, or install it from https://www.python.org/downloads/" # TODO: give MSYS pacman advice? elif command -v pacman >/dev/null 2>&1; then echo "Please install Python using your system's package manager:" echo " sudo pacman -S python python-pip" else echo "Please install Python from https://www.python.org/downloads/ or using your system's package manager." fi echo exit 1 fi # Create a venv and activate it PythonDir="$Cache/venv" if [ ! -d "$PythonDir" ]; then echo "Creating virtualenv..." "$PythonExe" -m venv "$PythonDir" fi if [ -f "$PythonDir/bin/python" ]; then PythonExe="$PythonDir/bin/python" elif [ -f "$PythonDir/scripts/python3.exe" ]; then PythonExe="$PythonDir/scripts/python3.exe"; else echo "bootstrap/python failed to find the python executable inside its virtualenv" exit 1 fi fi # Use pip to install our requirements if [ ! -f "$PythonDir/requirements.txt" ] || [ "$(b2sum < "$Sdk/requirements.txt")" != "$(b2sum < "$PythonDir/requirements.txt")" ]; then echo "Updating dependencies..." "$PythonExe" -m pip install -U wheel "$PythonExe" -m pip install -U pip -r "$Sdk/requirements.txt" cp "$Sdk/requirements.txt" "$PythonDir/requirements.txt" echo "---" fi # Verify version and deduce the path separator PythonMajor=${PythonVersion%%.*} PythonMinor=${PythonVersion#*.} PythonMinor=${PythonMinor%.*} PATHSEP=$("$PythonExe" - "$PythonMajor" "$PythonMinor" <<'EOF' import sys, os if sys.version_info.major != int(sys.argv[1]) or sys.version_info.minor < int(sys.argv[2]): print("Error: Python ", sys.argv[1], ".", sys.argv[2], " or later is required, but you have:\n", sys.version, sep="", file=sys.stderr) exit(1) print(os.pathsep) EOF ) # Cheap shell function if tee.exe is not available if ! command -v tee >/dev/null 2>&1; then tee() { # Fudge: assume $1 is always "-a" while read -r line; do echo "$line" >> "$2" echo "$line" done } fi # Invoke python with all command-line arguments export PYTHONPATH="$Sdk$PATHSEP${PYTHONPATH:-}" mkdir -p "$Cache" printf '%s\n' "$PythonExe" "$@" > "$Log" printf -- '---\n' >> "$Log" exec 4>&1 exitstatus=$({ { set +e; "$PythonExe" -u "$@" 2>&1 3>&-; printf %s $? >&3; } 4>&- | tee -a "$Log" 1>&4; } 3>&1) exec 4>&- exit "$exitstatus"