#!/bin/bash # @author: # @date: 2017-06-06 set -o nounset set -o errexit set -o pipefail readonly PROGNAME="$(basename "${0}")"; readonly PROGDIR="$(readlink -f "$(dirname "${0}")")"; readonly ARGS="${@}"; readonly PYTHON_VERSION_PATH="$(mktemp)"; readonly PIP_INSTALL_SCRIPT="$(mktemp)"; cleanup() { local rc=$?; # Keep original return code rm -f "${PYTHON_VERSION_PATH}" rm -f "${PIP_INSTALL_SCRIPT}" exit ${rc} } get_python_version() { local python="${1}"; shift; local version_path="${1}"; shift; # Redirecting stderr to stdout is necessary because older versions # of Python printed to stderr "${python}" --version 2>&1 \ | cut -d\ -f2 \ > "${version_path}" } venv_install_method() { local python="${1}"; shift; local env_dir="${1}"; shift; "${python}" -m venv "${env_dir}" set +o nounset source "${env_dir}/bin/activate" set -o nounset pip install --upgrade pip set +o nounset deactivate set -o nounset } pip_install_method() { local python="${1}"; shift; local env_dir="${1}"; shift; local version="${1:-}"; shift; local opts=''; [ -n "${version}" ] && version="/${version}" wget "https://bootstrap.pypa.io/pip${version}/get-pip.py" \ -O "${PIP_INSTALL_SCRIPT}" "${python}" "${PIP_INSTALL_SCRIPT}" --isolated \ --prefix="${env_dir}" \ --ignore-installed \ --no-cache # WARNING: for some reason it is not possible to force # the script to build from source (with --no-binary setuptools,pip) # due to an unsupported --no-user option in the install procedure. # So it is better to check that the installed binary is compatible # with the local system (notably the SSL library which may cause # issues when downloading packages from HTTPS). } install_environment() { get_python_version "${python}" "${PYTHON_VERSION_PATH}" local major="$(cat "${PYTHON_VERSION_PATH}" | cut -d. -f1)"; local minor="$(cat "${PYTHON_VERSION_PATH}" | cut -d. -f2)"; local num=$((${minor} + 10 * ${major})); if [ ${num} -gt 33 ]; then set +o errexit "${python}" -m ensurepip --help 2>&1 >/dev/null local rc=$? set -o errexit if [ 0 -eq ${rc} ]; then venv_install_method "${python}" "${env_dir}" else pip_install_method "${python}" "${env_dir}" "${major}.${minor}" install_virtualenv "${python}" "${env_dir}" fi elif [ ${num} -eq 27 ]; then pip_install_method "${python}" "${env_dir}" "${major}.${minor}" install_virtualenv "${python}" "${env_dir}" else >&2 echo "Unsupported Python version" exit 1 fi mkdir -p "${env_dir}/cache" cat > "${env_dir}/pip.conf" << EOF [global] cache-dir=${env_dir}/cache EOF } install_virtualenv() { local python="${1}"; shift; local env_dir="${1}"; shift; local _pkg_dir="$(find "${env_dir}/lib" -mindepth 2 -maxdepth 2 \ -name "site-packages")"; local pkg_dir="$(readlink -f "${_pkg_dir}")"; echo "Install virtual env" # Install virtualenv PYTHONPATH="${pkg_dir}" \ "${env_dir}/bin/pip" install --prefix="${env_dir}" \ --ignore-installed \ --no-cache \ --no-binary virtualenv \ virtualenv PIP_CACHE_DIR="${env_dir}/cache" \ PIP_IGNORE_INSTALLED=true \ PYTHONPATH="${pkg_dir}" \ "${python}" -m virtualenv "${env_dir}" } main() { local python="${1}"; shift; local _install_dir="${1}"; shift; local install_dir="$(readlink -f "${_install_dir}")"; local env_dir="${install_dir}/env"; trap 'cleanup' INT TERM EXIT install_environment "${python}" "${env_dir}" trap - INT TERM EXIT echo 'Environment ready' } main ${ARGS} exit 0