- #!/usr/bin/env bash
- #
- # Summary: Install a Python version using python-build
- #
- # Usage: pyenv install [-f] [-kvp] <version>
- # pyenv install [-f] [-kvp] <definition-file>
- # pyenv install -l|--list
- #
- # -l/--list List all available versions
- # -f/--force Install even if the version appears to be installed already
- # -s/--skip-existing Skip if the version appears to be installed already
- #
- # python-build options:
- #
- # -k/--keep Keep source tree in $PYENV_BUILD_ROOT after installation
- # (defaults to $PYENV_ROOT/sources)
- # -v/--verbose Verbose mode: print compilation status to stdout
- # -p/--patch Apply a patch from stdin before building
- # -g/--debug Build a debug version
- #
- # For detailed information on installing Python versions with
- # python-build, including a list of environment variables for adjusting
- # compilation, see: https://github.com/yyuu/pyenv#readme
- #
- set -e
- [ -n "$PYENV_DEBUG" ] && set -x
-
- if [ -z "$PYENV_ROOT" ]; then
- PYENV_ROOT="${HOME}/.pyenv"
- fi
-
- # Add `share/python-build/` directory from each pyenv plugin to the list of
- # paths where build definitions are looked up.
- shopt -s nullglob
- for plugin_path in "$PYENV_ROOT"/plugins/*/share/python-build; do
- PYTHON_BUILD_DEFINITIONS="${PYTHON_BUILD_DEFINITIONS}:${plugin_path}"
- done
- export PYTHON_BUILD_DEFINITIONS
- shopt -u nullglob
-
- # Provide pyenv completions
- if [ "$1" = "--complete" ]; then
- exec python-build --definitions
- fi
-
- # Load shared library functions
- eval "$(python-build --lib)"
-
- usage() {
- pyenv-help install 2>/dev/null
- [ -z "$1" ] || exit "$1"
- }
-
- definitions() {
- local query="$1"
- python-build --definitions | $(type -p ggrep grep | head -1) -F "$query" || true
- }
-
- indent() {
- sed 's/^/ /'
- }
-
- unset FORCE
- unset SKIP_EXISTING
- unset KEEP
- unset VERBOSE
- unset HAS_PATCH
- unset DEBUG
-
- parse_options "$@"
- for option in "${OPTIONS[@]}"; do
- case "$option" in
- "h" | "help" )
- usage 0
- ;;
- "l" | "list" )
- echo "Available versions:"
- definitions | indent
- exit
- ;;
- "f" | "force" )
- FORCE=true
- ;;
- "s" | "skip-existing" )
- SKIP_EXISTING=true
- ;;
- "k" | "keep" )
- [ -n "${PYENV_BUILD_ROOT}" ] || PYENV_BUILD_ROOT="${PYENV_ROOT}/sources"
- ;;
- "v" | "verbose" )
- VERBOSE="-v"
- ;;
- "p" | "patch" )
- HAS_PATCH="-p"
- ;;
- "g" | "debug" )
- DEBUG="-g"
- ;;
- "version" )
- exec python-build --version
- ;;
- * )
- usage 1 >&2
- ;;
- esac
- done
-
- [ "${#ARGUMENTS[@]}" -le 1 ] || usage 1 >&2
-
- unset VERSION_NAME
-
- # The first argument contains the definition to install. If the
- # argument is missing, try to install whatever local app-specific
- # version is specified by pyenv. Show usage instructions if a local
- # version is not specified.
- DEFINITION="${ARGUMENTS[0]}"
- [ -n "$DEFINITION" ] || DEFINITION="$(pyenv-local 2>/dev/null || true)"
- [ -n "$DEFINITION" ] || usage 1 >&2
-
- # Define `before_install` and `after_install` functions that allow
- # plugin hooks to register a string of code for execution before or
- # after the installation process.
- declare -a before_hooks after_hooks
-
- before_install() {
- local hook="$1"
- before_hooks["${#before_hooks[@]}"]="$hook"
- }
-
- after_install() {
- local hook="$1"
- after_hooks["${#after_hooks[@]}"]="$hook"
- }
-
- OLDIFS="$IFS"
- IFS=$'\n' scripts=(`pyenv-hooks install`)
- IFS="$OLDIFS"
- for script in "${scripts[@]}"; do source "$script"; done
-
-
- # Set VERSION_NAME from $DEFINITION, if it is not already set. Then
- # compute the installation prefix.
- [ -n "$VERSION_NAME" ] || VERSION_NAME="${DEFINITION##*/}"
- [ -n "$DEBUG" ] && VERSION_NAME="${VERSION_NAME}-debug"
- PREFIX="${PYENV_ROOT}/versions/${VERSION_NAME}"
-
- [ -d "${PREFIX}" ] && PREFIX_EXISTS=1
-
- # If the installation prefix exists, prompt for confirmation unless
- # the --force option was specified.
- if [ -d "${PREFIX}/bin" ]; then
- if [ -z "$FORCE" ] && [ -z "$SKIP_EXISTING" ]; then
- echo "pyenv: $PREFIX already exists" >&2
- read -p "continue with installation? (y/N) "
-
- case "$REPLY" in
- y* | Y* ) ;;
- * ) exit 1 ;;
- esac
- elif [ -n "$SKIP_EXISTING" ]; then
- # Since we know the python version is already installed, and are opting to
- # not force installation of existing versions, we just `exit 0` here to
- # leave things happy
- exit 0
- fi
- fi
-
- # If PYENV_BUILD_ROOT is set, always pass keep options to python-build.
- if [ -n "${PYENV_BUILD_ROOT}" ]; then
- export PYTHON_BUILD_BUILD_PATH="${PYENV_BUILD_ROOT}/${VERSION_NAME}"
- KEEP="-k"
- fi
-
- # Set PYTHON_BUILD_CACHE_PATH to $PYENV_ROOT/cache, if the directory
- # exists and the variable is not already set.
- if [ -z "${PYTHON_BUILD_CACHE_PATH}" ] && [ -d "${PYENV_ROOT}/cache" ]; then
- export PYTHON_BUILD_CACHE_PATH="${PYENV_ROOT}/cache"
- fi
-
- # Default PYENV_VERSION to the friendly Python version. (The
- # CPython installer requires an existing Python installation to run. An
- # unsatisfied local .python-version file can cause the installer to
- # fail.)
- if [[ "${VERSION_NAME}" == [23]"."* ]]; then
- for version in "${VERSION_NAME%-dev}" "${VERSION_NAME%.*}" "${VERSION_NAME%%.*}"; do
- PYENV_VERSION="$(pyenv-whence "python${version}" 2>/dev/null | tail -n 1 || true)"
- if [ -n "${PYENV_VERSION}" ]; then
- export PYENV_VERSION
- break
- fi
- done
- fi
-
- # PyPy/PyPy3 requires existing Python 2.7 to build
- if [[ "${VERSION_NAME}" == "pypy-"*"-src" ]] || [[ "${VERSION_NAME}" == "pypy3-"*"-src" ]]; then
- if [ -z "$PYENV_RPYTHON_VERSION" ]; then
- for version in $(pyenv-versions --bare | sort -r); do
- if [[ "$version" == "2.7"* ]]; then
- PYENV_RPYTHON_VERSION="$version"
- fi
- done
- fi
- if [ -n "$PYENV_RPYTHON_VERSION" ]; then
- if PYENV_VERSION="$PYENV_RPYTHON_VERSION" pyenv-exec python -c 'import curses' 1>/dev/null 2>&1; then
- export PYENV_VERSION="$PYENV_RPYTHON_VERSION"
- else
- echo "pyenv-install: $VERSION_NAME: PyPy requires \`curses' in $PYENV_RPYTHON_VERSION to build from source." >&2
- exit 1
- fi
- else
- echo "pyenv-install: $VERSION_NAME: PyPy requires Python 2.5, 2.6 or 2.7 to build from source." >&2
- exit 1
- fi
- fi
-
- # Execute `before_install` hooks.
- for hook in "${before_hooks[@]}"; do eval "$hook"; done
-
- # Plan cleanup on unsuccessful installation.
- cleanup() {
- [ -z "${PREFIX_EXISTS}" ] && rm -rf "$PREFIX"
- }
-
- trap cleanup SIGINT
-
- # Invoke `python-build` and record the exit status in $STATUS.
- STATUS=0
- python-build $KEEP $VERBOSE $HAS_PATCH $DEBUG "$DEFINITION" "$PREFIX" || STATUS="$?"
-
- # Display a more helpful message if the definition wasn't found.
- if [ "$STATUS" == "2" ]; then
- { candidates="$(definitions "$DEFINITION")"
- here="$(dirname "${0%/*}")/../.."
- if [ -n "$candidates" ]; then
- echo
- echo "The following versions contain \`$DEFINITION' in the name:"
- echo "$candidates" | indent
- fi
- echo
- echo "See all available versions with \`pyenv install --list'."
- echo
- echo -n "If the version you need is missing, try upgrading pyenv"
- if [ "$here" != "${here#$(brew --prefix 2>/dev/null)}" ]; then
- printf ":\n\n"
- echo " brew update && brew upgrade pyenv"
- elif [ -d "${here}/.git" ]; then
- printf ":\n\n"
- echo " cd ${here} && git pull && cd -"
- else
- printf ".\n"
- fi
- } >&2
- fi
-
- # Execute `after_install` hooks.
- for hook in "${after_hooks[@]}"; do eval "$hook"; done
-
- # Run `pyenv-rehash` after a successful installation.
- if [ "$STATUS" == "0" ]; then
- pyenv-rehash
- else
- cleanup
- fi
-
- exit "$STATUS"
|