diff options
-rw-r--r-- | eclass/python.eclass | 783 |
1 files changed, 476 insertions, 307 deletions
diff --git a/eclass/python.eclass b/eclass/python.eclass index a4315519f081..9e5afa762d79 100644 --- a/eclass/python.eclass +++ b/eclass/python.eclass @@ -1,6 +1,6 @@ # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-x86/eclass/python.eclass,v 1.96 2010/03/26 15:23:17 arfrever Exp $ +# $Header: /var/cvsroot/gentoo-x86/eclass/python.eclass,v 1.97 2010/05/17 18:01:59 arfrever Exp $ # @ECLASS: python.eclass # @MAINTAINER: @@ -184,20 +184,158 @@ fi # Set this to a name of a USE flag if you need to make either PYTHON_USE_WITH or # PYTHON_USE_WITH_OR atoms conditional under a USE flag. +if ! has "${EAPI:-0}" 0 1 && [[ -n ${PYTHON_USE_WITH} || -n ${PYTHON_USE_WITH_OR} ]]; then + _PYTHON_USE_WITH_ATOMS_ARRAY=() + if [[ -n "${PYTHON_USE_WITH}" ]]; then + for _PYTHON_ATOM in "${_PYTHON_ATOMS[@]}"; do + _PYTHON_USE_WITH_ATOMS_ARRAY+=("${_PYTHON_ATOM}[${PYTHON_USE_WITH// /,}]") + done + elif [[ -n "${PYTHON_USE_WITH_OR}" ]]; then + for _USE_flag in ${PYTHON_USE_WITH_OR}; do + for _PYTHON_ATOM in "${_PYTHON_ATOMS[@]}"; do + _PYTHON_USE_WITH_ATOMS_ARRAY+=("${_PYTHON_ATOM}[${_USE_flag}]") + done + done + unset _USE_flag + fi + if [[ "${#_PYTHON_USE_WITH_ATOMS_ARRAY[@]}" -gt 1 ]]; then + _PYTHON_USE_WITH_ATOMS="|| ( ${_PYTHON_USE_WITH_ATOMS_ARRAY[@]} )" + else + _PYTHON_USE_WITH_ATOMS="${_PYTHON_USE_WITH_ATOMS_ARRAY[@]}" + fi + if [[ -n "${PYTHON_USE_WITH_OPT}" ]]; then + _PYTHON_USE_WITH_ATOMS="${PYTHON_USE_WITH_OPT}? ( ${_PYTHON_USE_WITH_ATOMS} )" + fi + DEPEND+=" ${_PYTHON_USE_WITH_ATOMS}" + RDEPEND+=" ${_PYTHON_USE_WITH_ATOMS}" + unset _PYTHON_ATOM _PYTHON_USE_WITH_ATOMS _PYTHON_USE_WITH_ATOMS_ARRAY +fi + +unset _PYTHON_ATOMS + +# ================================================================================================ +# =================================== MISCELLANEOUS FUNCTIONS ==================================== +# ================================================================================================ + +_python_implementation() { + if [[ "${CATEGORY}/${PN}" == "dev-lang/python" ]]; then + return 0 + elif [[ "${CATEGORY}/${PN}" == "dev-java/jython" ]]; then + return 0 + else + return 1 + fi +} + +_python_package_supporting_installation_for_multiple_python_abis() { + if [[ "${EBUILD_PHASE}" == "depend" ]]; then + die "${FUNCNAME}() cannot be used in global scope" + fi + + if has "${EAPI:-0}" 0 1 2 3 4; then + if [[ -n "${SUPPORT_PYTHON_ABIS}" ]]; then + return 0 + else + return 1 + fi + else + die "${FUNCNAME}(): Support for EAPI=\"${EAPI}\" not implemented" + fi +} + +_python_initialize_prefix_variables() { + if has "${EAPI:-0}" 0 1 2; then + if [[ -n "${ROOT}" && -z "${EROOT}" ]]; then + EROOT="${ROOT%/}${EPREFIX}/" + fi + if [[ -n "${D}" && -z "${ED}" ]]; then + ED="${D%/}${EPREFIX}/" + fi + fi +} + +unset PYTHON_SANITY_CHECKS + +_python_initial_sanity_checks() { + if [[ "$(declare -p PYTHON_SANITY_CHECKS 2> /dev/null)" != "declare -- PYTHON_SANITY_CHECKS="* ]]; then + # Ensure that /usr/bin/python and /usr/bin/python-config are valid. + if [[ "$(readlink "${EPREFIX}/usr/bin/python")" != "python-wrapper" ]]; then + eerror "'${EPREFIX}/usr/bin/python' is not valid symlink." + eerror "Use \`eselect python set \${python_interpreter}\` to fix this problem." + die "'${EPREFIX}/usr/bin/python' is not valid symlink" + fi + if [[ "$(<"${EPREFIX}/usr/bin/python-config")" != *"Gentoo python-config wrapper script"* ]]; then + eerror "'${EPREFIX}/usr/bin/python-config' is not valid script" + eerror "Use \`eselect python set \${python_interpreter}\` to fix this problem." + die "'${EPREFIX}/usr/bin/python-config' is not valid script" + fi + fi +} + +_python_final_sanity_checks() { + if ! _python_implementation && [[ "$(declare -p PYTHON_SANITY_CHECKS 2> /dev/null)" != "declare -- PYTHON_SANITY_CHECKS="* ]]; then + local PYTHON_ABI="${PYTHON_ABI}" + for PYTHON_ABI in ${PYTHON_ABIS-${PYTHON_ABI}}; do + # Ensure that appropriate version of Python is installed. + if ! has_version "$(python_get_implementational_package)"; then + die "$(python_get_implementational_package) is not installed" + fi + + # Ensure that EPYTHON variable is respected. + if [[ "$(EPYTHON="$(PYTHON)" python -c "${_PYTHON_ABI_EXTRACTION_COMMAND}")" != "${PYTHON_ABI}" ]]; then + eerror "Path to 'python': '$(type -p python)'" + eerror "ABI: '${ABI}'" + eerror "DEFAULT_ABI: '${DEFAULT_ABI}'" + eerror "EPYTHON: '$(PYTHON)'" + eerror "PYTHON_ABI: '${PYTHON_ABI}'" + eerror "Locally active version of Python: '$(EPYTHON="$(PYTHON)" python -c "${_PYTHON_ABI_EXTRACTION_COMMAND}")'" + die "'python' does not respect EPYTHON variable" + fi + done + fi + PYTHON_SANITY_CHECKS="1" +} + +_python_set_color_variables() { + if [[ "${NOCOLOR:-false}" =~ ^(false|no)$ ]]; then + _BOLD=$'\e[1m' + _RED=$'\e[1;31m' + _GREEN=$'\e[1;32m' + _BLUE=$'\e[1;34m' + _CYAN=$'\e[1;36m' + _NORMAL=$'\e[0m' + else + _BOLD= + _RED= + _GREEN= + _BLUE= + _CYAN= + _NORMAL= + fi +} + +unset PYTHON_PKG_SETUP_EXECUTED + # @FUNCTION: python_pkg_setup # @DESCRIPTION: -# Makes sure PYTHON_USE_WITH or PYTHON_USE_WITH_OR listed use flags -# are respected. Only exported if one of those variables is set. -if ! has "${EAPI:-0}" 0 1 && [[ -n ${PYTHON_USE_WITH} || -n ${PYTHON_USE_WITH_OR} ]]; then - python_pkg_setup() { - # Check if phase is pkg_setup(). - [[ "${EBUILD_PHASE}" != "setup" ]] && die "${FUNCNAME}() can be used only in pkg_setup() phase" +# Perform sanity checks and initialize environment. +# +# This function is exported in EAPI 2 and 3 when PYTHON_USE_WITH or PYTHON_USE_WITH_OR variable +# is set and always in EAPI >=4. Calling of this function is mandatory in EAPI >=4. +# +# This function can be used only in pkg_setup() phase. +python_pkg_setup() { + # Check if phase is pkg_setup(). + [[ "${EBUILD_PHASE}" != "setup" ]] && die "${FUNCNAME}() can be used only in pkg_setup() phase" - python_pkg_setup_fail() { - eerror "${1}" - die "${1}" - } + if _python_package_supporting_installation_for_multiple_python_abis; then + validate_PYTHON_ABIS + export EPYTHON="$(PYTHON -f)" + else + PYTHON_ABI="$(PYTHON --ABI)" + fi + if ! has "${EAPI:-0}" 0 1 && [[ -n "${PYTHON_USE_WITH}" || -n "${PYTHON_USE_WITH_OR}" ]]; then if [[ "${PYTHON_USE_WITH_OPT}" ]]; then if [[ "${PYTHON_USE_WITH_OPT}" == !* ]]; then use ${PYTHON_USE_WITH_OPT#!} && return @@ -207,23 +345,25 @@ if ! has "${EAPI:-0}" 0 1 && [[ -n ${PYTHON_USE_WITH} || -n ${PYTHON_USE_WITH_OR fi python_pkg_setup_check_USE_flags() { - local pyatom use - pyatom="$(python_get_implementational_package)" + local python_atom USE_flag + python_atom="$(python_get_implementational_package)" - for use in ${PYTHON_USE_WITH}; do - if ! has_version "${pyatom}[${use}]"; then - python_pkg_setup_fail "Please rebuild ${pyatom} with the following USE flags enabled: ${PYTHON_USE_WITH}" + for USE_flag in ${PYTHON_USE_WITH}; do + if ! has_version "${python_atom}[${USE_flag}]"; then + eerror "Please rebuild ${python_atom} with the following USE flags enabled: ${PYTHON_USE_WITH}" + die "Please rebuild ${python_atom} with the following USE flags enabled: ${PYTHON_USE_WITH}" fi done - for use in ${PYTHON_USE_WITH_OR}; do - if has_version "${pyatom}[${use}]"; then + for USE_flag in ${PYTHON_USE_WITH_OR}; do + if has_version "${python_atom}[${USE_flag}]"; then return fi done if [[ ${PYTHON_USE_WITH_OR} ]]; then - python_pkg_setup_fail "Please rebuild ${pyatom} with at least one of the following USE flags enabled: ${PYTHON_USE_WITH_OR}" + eerror "Please rebuild ${python_atom} with at least one of the following USE flags enabled: ${PYTHON_USE_WITH_OR}" + die "Please rebuild ${python_atom} with at least one of the following USE flags enabled: ${PYTHON_USE_WITH_OR}" fi } @@ -233,38 +373,148 @@ if ! has "${EAPI:-0}" 0 1 && [[ -n ${PYTHON_USE_WITH} || -n ${PYTHON_USE_WITH_OR python_pkg_setup_check_USE_flags fi - unset -f python_pkg_setup_check_USE_flags python_pkg_setup_fail - } + unset -f python_pkg_setup_check_USE_flags + fi + + PYTHON_PKG_SETUP_EXECUTED="1" +} +if ! has "${EAPI:-0}" 0 1 2 3 || has "${EAPI:-0}" 2 3 && [[ -n "${PYTHON_USE_WITH}" || -n "${PYTHON_USE_WITH_OR}" ]]; then EXPORT_FUNCTIONS pkg_setup +fi - _PYTHON_USE_WITH_ATOMS_ARRAY=() - if [[ -n "${PYTHON_USE_WITH}" ]]; then - for _PYTHON_ATOM in "${_PYTHON_ATOMS[@]}"; do - _PYTHON_USE_WITH_ATOMS_ARRAY+=("${_PYTHON_ATOM}[${PYTHON_USE_WITH/ /,}]") - done - elif [[ -n "${PYTHON_USE_WITH_OR}" ]]; then - for _USE_flag in ${PYTHON_USE_WITH_OR}; do - for _PYTHON_ATOM in "${_PYTHON_ATOMS[@]}"; do - _PYTHON_USE_WITH_ATOMS_ARRAY+=("${_PYTHON_ATOM}[${_USE_flag}]") - done +# @FUNCTION: python_convert_shebangs +# @USAGE: [-q|--quiet] [-r|--recursive] [-x|--only-executables] [--] <Python_version> <file|directory> [files|directories] +# @DESCRIPTION: +# Convert shebangs in specified files. Directories can be specified only with --recursive option. +python_convert_shebangs() { + local argument file files=() only_executables="0" python_version quiet="0" recursive="0" + + while (($#)); do + case "$1" in + -r|--recursive) + recursive="1" + ;; + -q|--quiet) + quiet="1" + ;; + -x|--only-executables) + only_executables="1" + ;; + --) + shift + break + ;; + -*) + die "${FUNCNAME}(): Unrecognized option '$1'" + ;; + *) + break + ;; + esac + shift + done + + if [[ "$#" -eq 0 ]]; then + die "${FUNCNAME}(): Missing Python version and files or directories" + elif [[ "$#" -eq 1 ]]; then + die "${FUNCNAME}(): Missing files or directories" + fi + + python_version="$1" + shift + + for argument in "$@"; do + if [[ ! -e "${argument}" ]]; then + die "${FUNCNAME}(): '${argument}' does not exist" + elif [[ -f "${argument}" ]]; then + files+=("${argument}") + elif [[ -d "${argument}" ]]; then + if [[ "${recursive}" == "1" ]]; then + while read -d $'\0' -r file; do + files+=("${file}") + done < <(find "${argument}" $([[ "${only_executables}" == "1" ]] && echo -perm /111) -type f -print0) + else + die "${FUNCNAME}(): '${argument}' is not a regular file" + fi + else + die "${FUNCNAME}(): '${argument}' is not a regular file or a directory" + fi + done + + for file in "${files[@]}"; do + file="${file#./}" + [[ "${only_executables}" == "1" && ! -x "${file}" ]] && continue + + if [[ "$(head -n1 "${file}")" =~ ^'#!'.*python ]]; then + [[ "$(sed -ne "2p" "${file}")" =~ ^"# Gentoo '".*"' wrapper script generated by python_generate_wrapper_scripts()"$ ]] && continue + + if [[ "${quiet}" == "0" ]]; then + einfo "Converting shebang in '${file}'" + fi + + sed -e "1s/python\([[:digit:]]\+\(\.[[:digit:]]\+\)\?\)\?/python${python_version}/" -i "${file}" || die "Conversion of shebang in '${file}' failed" + + # Delete potential whitespace after "#!". + sed -e '1s/\(^#!\)[[:space:]]*/\1/' -i "${file}" || die "sed '${file}' failed" + fi + done +} + +# @FUNCTION: python_clean_installation_image +# @USAGE: [-q|--quiet] +# @DESCRIPTION: +# Delete needless files in installation image. +python_clean_installation_image() { + _python_initialize_prefix_variables + + local file files=() quiet="0" + + # Check if phase is src_install(). + [[ "${EBUILD_PHASE}" != "install" ]] && die "${FUNCNAME}() can be used only in src_install() phase" + + while (($#)); do + case "$1" in + -q|--quiet) + quiet="1" + ;; + -*) + die "${FUNCNAME}(): Unrecognized option '$1'" + ;; + *) + die "${FUNCNAME}(): Invalid usage" + ;; + esac + shift + done + + while read -d $'\0' -r file; do + files+=("${file}") + done < <(find "${ED}" "(" -name "*.py[co]" -o -name "*\$py.class" ")" -type f -print0) + + if [[ "${#files[@]}" -gt 0 ]]; then + if [[ "${quiet}" == "0" ]]; then + ewarn "Deleting byte-compiled Python modules needlessly generated by build system:" + fi + for file in "${files[@]}"; do + if [[ "${quiet}" == "0" ]]; then + ewarn " ${file}" + fi + rm -f "${file}" done - unset _USE_flag fi - if [[ "${#_PYTHON_USE_WITH_ATOMS_ARRAY[@]}" -gt 1 ]]; then - _PYTHON_USE_WITH_ATOMS="|| ( ${_PYTHON_USE_WITH_ATOMS_ARRAY[@]} )" + + python_clean_sitedirs() { + find "${ED}$(python_get_sitedir)" "(" -name "*.c" -o -name "*.h" -o -name "*.la" ")" -type f -print0 | xargs -0 rm -f + } + if _python_package_supporting_installation_for_multiple_python_abis; then + python_execute_function -q python_clean_sitedirs else - _PYTHON_USE_WITH_ATOMS="${_PYTHON_USE_WITH_ATOMS_ARRAY[@]}" + python_clean_sitedirs fi - if [[ -n "${PYTHON_USE_WITH_OPT}" ]]; then - _PYTHON_USE_WITH_ATOMS="${PYTHON_USE_WITH_OPT}? ( ${_PYTHON_USE_WITH_ATOMS} )" - fi - DEPEND+=" ${_PYTHON_USE_WITH_ATOMS}" - RDEPEND+=" ${_PYTHON_USE_WITH_ATOMS}" - unset _PYTHON_ATOM _PYTHON_USE_WITH_ATOMS _PYTHON_USE_WITH_ATOMS_ARRAY -fi -unset _PYTHON_ATOMS + unset -f python_clean_sitedirs +} # ================================================================================================ # =========== FUNCTIONS FOR PACKAGES SUPPORTING INSTALLATION FOR MULTIPLE PYTHON ABIS ============ @@ -294,7 +544,7 @@ if ! has "${EAPI:-0}" 0 1; then die \"\${FUNCNAME}() cannot be used in ebuilds of packages not supporting installation for multiple Python ABIs\" fi - python_execute_function -d -s \"\$@\" + python_execute_function -d -s -- \"\$@\" }" done unset python_default_function @@ -358,6 +608,10 @@ validate_PYTHON_ABIS() { else local python_version python2_version= python3_version= support_python_major_version + if ! has_version "dev-lang/python"; then + die "${FUNCNAME}(): 'dev-lang/python' is not installed" + fi + python_version="$("${EPREFIX}/usr/bin/python" -c 'from sys import version_info; print(".".join(str(x) for x in version_info[:2]))')" if has_version "=dev-lang/python-2*"; then @@ -675,7 +929,7 @@ python_copy_sources() { if [[ "$#" -eq 0 ]]; then if [[ "${WORKDIR}" == "${S}" ]]; then - die "${FUNCNAME}() cannot be used" + die "${FUNCNAME}() cannot be used with current value of S variable" fi dirs=("${S%/}") else @@ -909,6 +1163,10 @@ python_set_active_version() { die "${FUNCNAME}() cannot be used in ebuilds of packages supporting installation for multiple Python ABIs" fi + if [[ -n "${PYTHON_PKG_SETUP_EXECUTED}" ]]; then + die "${FUNCNAME}() should be called before python_pkg_setup()" + fi + if [[ "$#" -ne 1 ]]; then die "${FUNCNAME}() requires 1 argument" fi @@ -1045,19 +1303,22 @@ PYTHON() { elif [[ "${python2}" == "1" ]]; then PYTHON_ABI="$(eselect python show --python2 --ABI)" if [[ -z "${PYTHON_ABI}" ]]; then - die "${FUNCNAME}(): Active Python 2 interpreter not set" + die "${FUNCNAME}(): Active version of Python 2 not set" elif [[ "${PYTHON_ABI}" != "2."* ]]; then die "${FUNCNAME}(): Internal error in \`eselect python show --python2\`" fi elif [[ "${python3}" == "1" ]]; then PYTHON_ABI="$(eselect python show --python3 --ABI)" if [[ -z "${PYTHON_ABI}" ]]; then - die "${FUNCNAME}(): Active Python 3 interpreter not set" + die "${FUNCNAME}(): Active version of Python 3 not set" elif [[ "${PYTHON_ABI}" != "3."* ]]; then die "${FUNCNAME}(): Internal error in \`eselect python show --python3\`" fi elif ! _python_package_supporting_installation_for_multiple_python_abis; then PYTHON_ABI="$("${EPREFIX}/usr/bin/python" -c "${_PYTHON_ABI_EXTRACTION_COMMAND}")" + if [[ -z "${PYTHON_ABI}" ]]; then + die "${FUNCNAME}(): Main active version of Python not set" + fi elif [[ -z "${PYTHON_ABI}" ]]; then die "${FUNCNAME}(): Invalid usage: ${FUNCNAME}() should be used in ABI-specific local scope" fi @@ -1398,194 +1659,6 @@ python_get_version() { } # ================================================================================================ -# =================================== MISCELLANEOUS FUNCTIONS ==================================== -# ================================================================================================ - -_python_implementation() { - if [[ "${CATEGORY}/${PN}" == "dev-lang/python" ]]; then - return 0 - elif [[ "${CATEGORY}/${PN}" == "dev-java/jython" ]]; then - return 0 - else - return 1 - fi -} - -_python_package_supporting_installation_for_multiple_python_abis() { - if [[ "${EBUILD_PHASE}" == "depend" ]]; then - die "${FUNCNAME}() cannot be used in global scope" - fi - - if has "${EAPI:-0}" 0 1 2 3 4; then - if [[ -n "${SUPPORT_PYTHON_ABIS}" ]]; then - return 0 - else - return 1 - fi - else - die "${FUNCNAME}(): Support for EAPI=\"${EAPI}\" not implemented" - fi -} - -_python_initialize_prefix_variables() { - if has "${EAPI:-0}" 0 1 2; then - if [[ -n "${ROOT}" && -z "${EROOT}" ]]; then - EROOT="${ROOT%/}${EPREFIX}/" - fi - if [[ -n "${D}" && -z "${ED}" ]]; then - ED="${D%/}${EPREFIX}/" - fi - fi -} - -unset PYTHON_SANITY_CHECKS - -_python_initial_sanity_checks() { - if [[ "$(declare -p PYTHON_SANITY_CHECKS 2> /dev/null)" != "declare -- PYTHON_SANITY_CHECKS="* ]]; then - # Ensure that /usr/bin/python and /usr/bin/python-config are valid. - if [[ "$(readlink "${EPREFIX}/usr/bin/python")" != "python-wrapper" ]]; then - eerror "'${EPREFIX}/usr/bin/python' is not valid symlink." - eerror "Use \`eselect python set \${python_interpreter}\` to fix this problem." - die "'${EPREFIX}/usr/bin/python' is not valid symlink" - fi - if [[ "$(<"${EPREFIX}/usr/bin/python-config")" != *"Gentoo python-config wrapper script"* ]]; then - eerror "'${EPREFIX}/usr/bin/python-config' is not valid script" - eerror "Use \`eselect python set \${python_interpreter}\` to fix this problem." - die "'${EPREFIX}/usr/bin/python-config' is not valid script" - fi - fi -} - -_python_final_sanity_checks() { - if ! _python_implementation && [[ "$(declare -p PYTHON_SANITY_CHECKS 2> /dev/null)" != "declare -- PYTHON_SANITY_CHECKS="* ]]; then - local PYTHON_ABI="${PYTHON_ABI}" - for PYTHON_ABI in ${PYTHON_ABIS-${PYTHON_ABI}}; do - # Ensure that appropriate version of Python is installed. - if ! has_version "$(python_get_implementational_package)"; then - die "$(python_get_implementational_package) is not installed" - fi - - # Ensure that EPYTHON variable is respected. - if [[ "$(EPYTHON="$(PYTHON)" python -c "${_PYTHON_ABI_EXTRACTION_COMMAND}")" != "${PYTHON_ABI}" ]]; then - eerror "python: '$(type -p python)'" - eerror "ABI: '${ABI}'" - eerror "DEFAULT_ABI: '${DEFAULT_ABI}'" - eerror "EPYTHON: '$(PYTHON)'" - eerror "PYTHON_ABI: '${PYTHON_ABI}'" - eerror "Version of enabled Python: '$(EPYTHON="$(PYTHON)" python -c "${_PYTHON_ABI_EXTRACTION_COMMAND}")'" - die "'python' does not respect EPYTHON variable" - fi - done - fi - PYTHON_SANITY_CHECKS="1" -} - -_python_set_color_variables() { - if [[ "${NOCOLOR:-false}" =~ ^(false|no)$ ]]; then - _BOLD=$'\e[1m' - _RED=$'\e[1;31m' - _GREEN=$'\e[1;32m' - _BLUE=$'\e[1;34m' - _CYAN=$'\e[1;36m' - _NORMAL=$'\e[0m' - else - _BOLD= - _RED= - _GREEN= - _BLUE= - _CYAN= - _NORMAL= - fi -} - -# @FUNCTION: python_convert_shebangs -# @USAGE: [-q|--quiet] [-r|--recursive] [-x|--only-executables] [--] <Python_version> <file|directory> [files|directories] -# @DESCRIPTION: -# Convert shebangs in specified files. Directories can be specified only with --recursive option. -python_convert_shebangs() { - local argument file files=() only_executables="0" python_version quiet="0" recursive="0" - - while (($#)); do - case "$1" in - -r|--recursive) - recursive="1" - ;; - -q|--quiet) - quiet="1" - ;; - -x|--only-executables) - only_executables="1" - ;; - --) - shift - break - ;; - -*) - die "${FUNCNAME}(): Unrecognized option '$1'" - ;; - *) - break - ;; - esac - shift - done - - if [[ "$#" -eq 0 ]]; then - die "${FUNCNAME}(): Missing Python version and files or directories" - elif [[ "$#" -eq 1 ]]; then - die "${FUNCNAME}(): Missing files or directories" - fi - - python_version="$1" - shift - - for argument in "$@"; do - if [[ ! -e "${argument}" ]]; then - die "${FUNCNAME}(): '${argument}' does not exist" - elif [[ -f "${argument}" ]]; then - files+=("${argument}") - elif [[ -d "${argument}" ]]; then - if [[ "${recursive}" == "1" ]]; then - while read -d $'\0' -r file; do - files+=("${file}") - done < <(find "${argument}" $([[ "${only_executables}" == "1" ]] && echo -perm /111) -type f -print0) - else - die "${FUNCNAME}(): '${argument}' is not a regular file" - fi - else - die "${FUNCNAME}(): '${argument}' is not a regular file or a directory" - fi - done - - for file in "${files[@]}"; do - file="${file#./}" - [[ "${only_executables}" == "1" && ! -x "${file}" ]] && continue - - if [[ "$(head -n1 "${file}")" =~ ^'#!'.*python ]]; then - [[ "$(sed -ne "2p" "${file}")" =~ ^"# Gentoo '".*"' wrapper script generated by python_generate_wrapper_scripts()"$ ]] && continue - - if [[ "${quiet}" == "0" ]]; then - einfo "Converting shebang in '${file}'" - fi - - sed -e "1s/python\([[:digit:]]\+\(\.[[:digit:]]\+\)\?\)\?/python${python_version}/" -i "${file}" || die "Conversion of shebang in '${file}' failed" - - # Delete potential whitespace after "#!". - sed -e '1s/\(^#!\)[[:space:]]*/\1/' -i "${file}" || die "sed '${file}' failed" - fi - done -} - -# @FUNCTION: python_clean_sitedirs -# @DESCRIPTION: -# Delete needless files in site-packages directories in ${ED}. -python_clean_sitedirs() { - _python_initialize_prefix_variables - - find "${ED}"usr/$(get_libdir)/python*/site-packages "(" -name "*.c" -o -name "*.h" -o -name "*.la" ")" -type f -print0 | xargs -0 rm -f -} - -# ================================================================================================ # ================================ FUNCTIONS FOR RUNNING OF TESTS ================================ # ================================================================================================ @@ -1830,6 +1903,93 @@ python_disable_pyc() { export PYTHONDONTWRITEBYTECODE="1" } +_python_clean_compiled_modules() { + _python_initialize_prefix_variables + _python_set_color_variables + + [[ "${FUNCNAME[1]}" =~ ^(python_mod_optimize|python_mod_compile|python_mod_cleanup)$ ]] || die "${FUNCNAME}(): Invalid usage" + + local base_module_name compiled_file compiled_files=() dir path py_file root + + # Strip trailing slash from EROOT. + root="${EROOT%/}" + + for path in "$@"; do + compiled_files=() + if [[ -d "${path}" ]]; then + while read -d $'\0' -r compiled_file; do + compiled_files+=("${compiled_file}") + done < <(find "${path}" "(" -name "*.py[co]" -o -name "*\$py.class" ")" -print0) + + if [[ "${EBUILD_PHASE}" == "postrm" ]]; then + # Delete empty child directories. + find "${path}" -type d | sort -r | while read -r dir; do + rmdir "${dir}" 2> /dev/null && echo "${_CYAN}<<< ${dir}${_NORMAL}" + done + fi + elif [[ "${path}" == *.py ]]; then + base_module_name="${path##*/}" + base_module_name="${base_module_name%.py}" + if [[ -d "${path%/*}/__pycache__" ]]; then + while read -d $'\0' -r compiled_file; do + compiled_files+=("${compiled_file}") + done < <(find "${path%/*}/__pycache__" "(" -name "${base_module_name}.*.py[co]" -o -name "${base_module_name}\$py.class" ")" -print0) + fi + compiled_files+=("${path}c" "${path}o" "${path%.py}\$py.class") + fi + + for compiled_file in "${compiled_files[@]}"; do + [[ ! -f "${compiled_file}" ]] && continue + dir="${compiled_file%/*}" + dir="${dir##*/}" + if [[ "${compiled_file}" == *.py[co] ]]; then + if [[ "${dir}" == "__pycache__" ]]; then + base_module_name="${compiled_file##*/}" + base_module_name="${base_module_name%%.*py[co]}" + py_file="${compiled_file%__pycache__/*}${base_module_name}.py" + else + py_file="${compiled_file%[co]}" + fi + if [[ "${EBUILD_PHASE}" == "postinst" ]]; then + [[ -f "${py_file}" && "${compiled_file}" -nt "${py_file}" ]] && continue + else + [[ -f "${py_file}" ]] && continue + fi + echo "${_BLUE}<<< ${compiled_file%[co]}[co]${_NORMAL}" + rm -f "${compiled_file%[co]}"[co] + elif [[ "${compiled_file}" == *\$py.class ]]; then + if [[ "${dir}" == "__pycache__" ]]; then + base_module_name="${compiled_file##*/}" + base_module_name="${base_module_name%\$py.class}" + py_file="${compiled_file%__pycache__/*}${base_module_name}.py" + else + py_file="${compiled_file%\$py.class}" + fi + if [[ "${EBUILD_PHASE}" == "postinst" ]]; then + [[ -f "${py_file}" && "${compiled_file}" -nt "${py_file}" ]] && continue + else + [[ -f "${py_file}" ]] && continue + fi + echo "${_BLUE}<<< ${compiled_file}${_NORMAL}" + rm -f "${compiled_file}" + else + die "${FUNCNAME}(): Unrecognized file type: '${compiled_file}'" + fi + + # Delete empty parent directories. + dir="${compiled_file%/*}" + while [[ "${dir}" != "${root}" ]]; do + if rmdir "${compiled_file%/*}" 2> /dev/null; then + echo "${_CYAN}<<< ${compiled_file%/*}${_NORMAL}" + else + break + fi + dir="${dir%/*}" + done + done + done +} + # @FUNCTION: python_mod_optimize # @USAGE: [options] [directory|file] # @DESCRIPTION: @@ -1849,12 +2009,22 @@ python_mod_optimize() { [[ "${EBUILD_PHASE}" != "postinst" ]] && die "${FUNCNAME}() can be used only in pkg_postinst() phase" if ! has "${EAPI:-0}" 0 1 2 || _python_package_supporting_installation_for_multiple_python_abis; then - local dir file options=() other_dirs=() other_files=() previous_PYTHON_ABI="${PYTHON_ABI}" PYTHON_ABI="${PYTHON_ABI}" return_code root site_packages_absolute_dirs=() site_packages_dirs=() site_packages_absolute_files=() site_packages_files=() + # PYTHON_ABI variable cannot be local in packages not supporting installation for multiple Python ABIs. + local dir file iterated_PYTHON_ABIS options=() other_dirs=() other_files=() previous_PYTHON_ABI="${PYTHON_ABI}" return_code root site_packages_absolute_dirs=() site_packages_dirs=() site_packages_absolute_files=() site_packages_files=() + + if _python_package_supporting_installation_for_multiple_python_abis; then + if [[ -z "${PYTHON_ABIS}" ]]; then + die "${FUNCNAME}(): Environment not initialized" + fi + iterated_PYTHON_ABIS="${PYTHON_ABIS}" + else + iterated_PYTHON_ABIS="${PYTHON_ABI:=$(PYTHON --ABI)}" + fi - # Strip trailing slash from ROOT. + # Strip trailing slash from EROOT. root="${EROOT%/}" - # Respect ROOT and options passed to compileall.py. + # Respect EROOT and options passed to compileall.py. while (($#)); do case "$1" in -l|-f|-q) @@ -1868,20 +2038,23 @@ python_mod_optimize() { ewarn "${FUNCNAME}(): Ignoring option '$1'" ;; *) - if ! _python_implementation && [[ "$1" =~ ^"${EPREFIX}"/usr/lib(32|64)?/python[[:digit:]]+\.[[:digit:]]+ ]]; then - die "${FUNCNAME}() does not support absolute paths of directories/files in site-packages directories" + if ! _python_implementation && [[ "$1" =~ ^/usr/lib(32|64)?/python[[:digit:]]+\.[[:digit:]]+ ]]; then + die "${FUNCNAME}(): Paths of directories / files in site-packages directories must be relative to site-packages directories" elif [[ "$1" =~ ^/ ]]; then - if [[ -d "${root}/$1" ]]; then - other_dirs+=("${root}/$1") - elif [[ -f "${root}/$1" ]]; then - other_files+=("${root}/$1") - elif [[ -e "${root}/$1" ]]; then - ewarn "'${root}/$1' is not a file or a directory!" + if _python_package_supporting_installation_for_multiple_python_abis; then + die "${FUNCNAME}(): Absolute paths cannot be used in ebuilds of packages supporting installation for multiple Python ABIs" + fi + if [[ -d "${root}$1" ]]; then + other_dirs+=("${root}$1") + elif [[ -f "${root}$1" ]]; then + other_files+=("${root}$1") + elif [[ -e "${root}$1" ]]; then + ewarn "'${root}$1' is not a file or a directory!" else - ewarn "'${root}/$1' does not exist!" + ewarn "'${root}$1' does not exist!" fi else - for PYTHON_ABI in ${PYTHON_ABIS-${PYTHON_ABI:-$(PYTHON --ABI)}}; do + for PYTHON_ABI in ${iterated_PYTHON_ABIS}; do if [[ -d "${root}$(python_get_sitedir)/$1" ]]; then site_packages_dirs+=("$1") break @@ -1903,7 +2076,7 @@ python_mod_optimize() { # Set additional options. options+=("-q") - for PYTHON_ABI in ${PYTHON_ABIS-${PYTHON_ABI:-$(PYTHON --ABI)}}; do + for PYTHON_ABI in ${iterated_PYTHON_ABIS}; do if ((${#site_packages_dirs[@]})) || ((${#site_packages_files[@]})); then return_code="0" ebegin "Compilation and optimization of Python modules for $(python_get_implementation) $(python_get_version)" @@ -1915,6 +2088,7 @@ python_mod_optimize() { if [[ "$(_python_get_implementation "${PYTHON_ABI}")" != "Jython" ]]; then "$(PYTHON)" -O "${root}$(python_get_libdir)/compileall.py" "${options[@]}" "${site_packages_absolute_dirs[@]}" &> /dev/null || return_code="1" fi + _python_clean_compiled_modules "${site_packages_absolute_dirs[@]}" fi if ((${#site_packages_files[@]})); then for file in "${site_packages_files[@]}"; do @@ -1924,17 +2098,20 @@ python_mod_optimize() { if [[ "$(_python_get_implementation "${PYTHON_ABI}")" != "Jython" ]]; then "$(PYTHON)" -O "${root}$(python_get_libdir)/py_compile.py" "${site_packages_absolute_files[@]}" &> /dev/null || return_code="1" fi + _python_clean_compiled_modules "${site_packages_absolute_files[@]}" fi eend "${return_code}" fi unset site_packages_absolute_dirs site_packages_absolute_files done - # Restore previous value of PYTHON_ABI. - if [[ -n "${previous_PYTHON_ABI}" ]]; then - PYTHON_ABI="${previous_PYTHON_ABI}" - else - unset PYTHON_ABI + if _python_package_supporting_installation_for_multiple_python_abis; then + # Restore previous value of PYTHON_ABI. + if [[ -n "${previous_PYTHON_ABI}" ]]; then + PYTHON_ABI="${previous_PYTHON_ABI}" + else + unset PYTHON_ABI + fi fi if ((${#other_dirs[@]})) || ((${#other_files[@]})); then @@ -1945,12 +2122,14 @@ python_mod_optimize() { if [[ "$(_python_get_implementation "${PYTHON_ABI-$(PYTHON --ABI)}")" != "Jython" ]]; then "$(PYTHON ${PYTHON_ABI})" -O "${root}$(python_get_libdir)/compileall.py" "${options[@]}" "${other_dirs[@]}" &> /dev/null || return_code="1" fi + _python_clean_compiled_modules "${other_dirs[@]}" fi if ((${#other_files[@]})); then "$(PYTHON ${PYTHON_ABI})" "${root}$(python_get_libdir)/py_compile.py" "${other_files[@]}" || return_code="1" if [[ "$(_python_get_implementation "${PYTHON_ABI-$(PYTHON --ABI)}")" != "Jython" ]]; then "$(PYTHON ${PYTHON_ABI})" -O "${root}$(python_get_libdir)/py_compile.py" "${other_files[@]}" &> /dev/null || return_code="1" fi + _python_clean_compiled_modules "${other_dirs[@]}" fi eend "${return_code}" fi @@ -1960,7 +2139,7 @@ python_mod_optimize() { # strip trailing slash myroot="${EROOT%/}" - # respect ROOT and options passed to compileall.py + # respect EROOT and options passed to compileall.py while (($#)); do case "$1" in -l|-f|-q) @@ -1974,15 +2153,15 @@ python_mod_optimize() { ewarn "${FUNCNAME}(): Ignoring option '$1'" ;; *) - if [[ -d "${myroot}"/$1 ]]; then - mydirs+=("${myroot}/$1") - elif [[ -f "${myroot}"/$1 ]]; then - # Files are passed to python_mod_compile which is ROOT-aware + if [[ -d "${myroot}/${1#/}" ]]; then + mydirs+=("${myroot}/${1#/}") + elif [[ -f "${myroot}/${1#/}" ]]; then + # Files are passed to python_mod_compile which is EROOT-aware myfiles+=("$1") - elif [[ -e "${myroot}/$1" ]]; then - ewarn "${myroot}/$1 is not a file or directory!" + elif [[ -e "${myroot}/${1#/}" ]]; then + ewarn "${myroot}/${1#/} is not a file or directory!" else - ewarn "${myroot}/$1 does not exist!" + ewarn "${myroot}/${1#/} does not exist!" fi ;; esac @@ -1992,10 +2171,13 @@ python_mod_optimize() { # set additional opts myopts+=(-q) + PYTHON_ABI="$(PYTHON --ABI)" + ebegin "Compilation and optimization of Python modules for $(python_get_implementation) $(python_get_version)" if ((${#mydirs[@]})); then "$(PYTHON ${PYTHON_ABI})" "${myroot}$(python_get_libdir)/compileall.py" "${myopts[@]}" "${mydirs[@]}" || return_code="1" "$(PYTHON ${PYTHON_ABI})" -O "${myroot}$(python_get_libdir)/compileall.py" "${myopts[@]}" "${mydirs[@]}" &> /dev/null || return_code="1" + _python_clean_compiled_modules "${mydirs[@]}" fi if ((${#myfiles[@]})); then @@ -2019,83 +2201,63 @@ python_mod_optimize() { # This function can be used only in pkg_postrm() phase. python_mod_cleanup() { _python_initialize_prefix_variables - _python_set_color_variables - local path py_file PYTHON_ABI="${PYTHON_ABI}" SEARCH_PATH=() root + local dir iterated_PYTHON_ABIS PYTHON_ABI="${PYTHON_ABI}" root search_paths=() sitedir # Check if phase is pkg_postrm(). [[ "${EBUILD_PHASE}" != "postrm" ]] && die "${FUNCNAME}() can be used only in pkg_postrm() phase" - # Strip trailing slash from ROOT. + if _python_package_supporting_installation_for_multiple_python_abis; then + if [[ -z "${PYTHON_ABIS}" ]]; then + die "${FUNCNAME}(): Environment not initialized" + fi + iterated_PYTHON_ABIS="${PYTHON_ABIS}" + else + iterated_PYTHON_ABIS="${PYTHON_ABI:-$(PYTHON --ABI)}" + fi + + # Strip trailing slash from EROOT. root="${EROOT%/}" if (($#)); then if ! has "${EAPI:-0}" 0 1 2 || _python_package_supporting_installation_for_multiple_python_abis; then while (($#)); do - if ! _python_implementation && [[ "$1" =~ ^"${EPREFIX}"/usr/lib(32|64)?/python[[:digit:]]+\.[[:digit:]]+ ]]; then - die "${FUNCNAME}() does not support absolute paths of directories/files in site-packages directories" + if ! _python_implementation && [[ "$1" =~ ^/usr/lib(32|64)?/python[[:digit:]]+\.[[:digit:]]+ ]]; then + die "${FUNCNAME}(): Paths of directories / files in site-packages directories must be relative to site-packages directories" elif [[ "$1" =~ ^/ ]]; then - SEARCH_PATH+=("${root}/${1#/}") + if _python_package_supporting_installation_for_multiple_python_abis; then + die "${FUNCNAME}(): Absolute paths cannot be used in ebuilds of packages supporting installation for multiple Python ABIs" + fi + search_paths+=("${root}$1") else - for PYTHON_ABI in ${PYTHON_ABIS-${PYTHON_ABI:-$(PYTHON --ABI)}}; do - SEARCH_PATH+=("${root}$(python_get_sitedir)/$1") + for PYTHON_ABI in ${iterated_PYTHON_ABIS}; do + search_paths+=("${root}$(python_get_sitedir)/$1") done fi shift done else - SEARCH_PATH=("${@#/}") - SEARCH_PATH=("${SEARCH_PATH[@]/#/${root}/}") + search_paths=("${@#/}") + search_paths=("${search_paths[@]/#/${root}/}") fi else - local dir sitedir for dir in "${root}"/usr/lib*; do if [[ -d "${dir}" && ! -L "${dir}" ]]; then for sitedir in "${dir}"/python*/site-packages; do if [[ -d "${sitedir}" ]]; then - SEARCH_PATH+=("${sitedir}") + search_paths+=("${sitedir}") fi done fi done for sitedir in "${root}"/usr/share/jython-*/Lib/site-packages; do if [[ -d "${sitedir}" ]]; then - SEARCH_PATH+=("${sitedir}") + search_paths+=("${sitedir}") fi done fi - for path in "${SEARCH_PATH[@]}"; do - if [[ -d "${path}" ]]; then - find "${path}" "(" -name "*.py[co]" -o -name "*\$py.class" ")" -print0 | while read -rd ''; do - if [[ "${REPLY}" == *[co] ]]; then - py_file="${REPLY%[co]}" - [[ -f "${py_file}" || (! -f "${py_file}c" && ! -f "${py_file}o") ]] && continue - echo "${_BLUE}<<< ${py_file}[co]${_NORMAL}" - rm -f "${py_file}"[co] - elif [[ "${REPLY}" == *\$py.class ]]; then - py_file="${REPLY%\$py.class}.py" - [[ -f "${py_file}" || ! -f "${py_file%.py}\$py.class" ]] && continue - echo "${_BLUE}<<< ${py_file%.py}\$py.class${_NORMAL}" - rm -f "${py_file%.py}\$py.class" - fi - done - - # Attempt to delete directories, which may be empty. - find "${path}" -type d | sort -r | while read -r dir; do - rmdir "${dir}" 2>/dev/null && echo "${_CYAN}<<< ${dir}${_NORMAL}" - done - elif [[ "${path}" == *.py && ! -f "${path}" ]]; then - if [[ (-f "${path}c" || -f "${path}o") ]]; then - echo "${_BLUE}<<< ${path}[co]${_NORMAL}" - rm -f "${path}"[co] - fi - if [[ -f "${path%.py}\$py.class" ]]; then - echo "${_BLUE}<<< ${path%.py}\$py.class${_NORMAL}" - rm -f "${path%.py}\$py.class" - fi - fi - done + _python_clean_compiled_modules "${search_paths[@]}" } # ================================================================================================ @@ -2159,6 +2321,8 @@ python_mod_exists() { die "${FUNCNAME}() cannot be used in this EAPI" fi + _python_set_color_variables + echo echo " ${_RED}*${_NORMAL} ${_RED}Deprecation Warning: ${FUNCNAME}() is deprecated and will be banned on 2010-07-01.${_NORMAL}" echo " ${_RED}*${_NORMAL} ${_RED}Use USE dependencies and/or has_version() instead of ${FUNCNAME}().${_NORMAL}" @@ -2187,6 +2351,8 @@ python_tkinter_exists() { die "${FUNCNAME}() cannot be used in this EAPI" fi + _python_set_color_variables + if [[ "${FUNCNAME[1]}" != "distutils_python_tkinter" ]]; then echo echo " ${_RED}*${_NORMAL} ${_RED}Deprecation Warning: ${FUNCNAME}() is deprecated and will be banned on 2010-07-01.${_NORMAL}" @@ -2238,14 +2404,17 @@ python_mod_compile() { # strip trailing slash myroot="${EROOT%/}" - # respect ROOT + # respect EROOT for f in "$@"; do [[ -f "${myroot}/${f}" ]] && myfiles+=("${myroot}/${f}") done + PYTHON_ABI="$(PYTHON --ABI)" + if ((${#myfiles[@]})); then "$(PYTHON ${PYTHON_ABI})" "${myroot}$(python_get_libdir)/py_compile.py" "${myfiles[@]}" "$(PYTHON ${PYTHON_ABI})" -O "${myroot}$(python_get_libdir)/py_compile.py" "${myfiles[@]}" &> /dev/null + _python_clean_compiled_modules "${myfiles[@]}" else ewarn "No files to compile!" fi |