# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: /var/cvsroot/gentoo-x86/eclass/python-utils-r1.eclass,v 1.66 2014/11/23 16:05:42 floppym Exp $ # @ECLASS: python-utils-r1 # @MAINTAINER: # Python team # @AUTHOR: # Author: Michał Górny # Based on work of: Krzysztof Pawlik # @BLURB: Utility functions for packages with Python parts. # @DESCRIPTION: # A utility eclass providing functions to query Python implementations, # install Python modules and scripts. # # This eclass does not set any metadata variables nor export any phase # functions. It can be inherited safely. # # For more information, please see the python-r1 Developer's Guide: # http://www.gentoo.org/proj/en/Python/python-r1/dev-guide.xml case "${EAPI:-0}" in 0|1|2|3|4|5) ;; *) die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;; esac if [[ ${_PYTHON_ECLASS_INHERITED} ]]; then die 'python-r1 suite eclasses can not be used with python.eclass.' fi if [[ ! ${_PYTHON_UTILS_R1} ]]; then inherit eutils multilib toolchain-funcs # @ECLASS-VARIABLE: _PYTHON_ALL_IMPLS # @INTERNAL # @DESCRIPTION: # All supported Python implementations, most preferred last. _PYTHON_ALL_IMPLS=( jython2_5 jython2_7 pypy pypy3 python3_3 python3_4 python2_7 ) # @FUNCTION: _python_impl_supported # @USAGE: # @INTERNAL # @DESCRIPTION: # Check whether the implementation (PYTHON_COMPAT-form) # is still supported. # # Returns 0 if the implementation is valid and supported. If it is # unsupported, returns 1 -- and the caller should ignore the entry. # If it is invalid, dies with an appopriate error messages. _python_impl_supported() { debug-print-function ${FUNCNAME} "${@}" [[ ${#} -eq 1 ]] || die "${FUNCNAME}: takes exactly 1 argument (impl)." local impl=${1} # keep in sync with _PYTHON_ALL_IMPLS! # (not using that list because inline patterns shall be faster) case "${impl}" in python2_7|python3_[34]|jython2_[57]) return 0 ;; pypy1_[89]|pypy2_0|python2_[56]|python3_[12]) return 1 ;; pypy|pypy3) if [[ ${EAPI:-0} == [01234] ]]; then die "PyPy is supported in EAPI 5 and newer only." fi ;; *) die "Invalid implementation in PYTHON_COMPAT: ${impl}" esac } # @ECLASS-VARIABLE: PYTHON # @DEFAULT_UNSET # @DESCRIPTION: # The absolute path to the current Python interpreter. # # This variable is set automatically in the following contexts: # # python-r1: Set in functions called by python_foreach_impl() or after # calling python_export_best(). # # python-single-r1: Set after calling python-single-r1_pkg_setup(). # # distutils-r1: Set within any of the python sub-phase functions. # # Example value: # @CODE # /usr/bin/python2.7 # @CODE # @ECLASS-VARIABLE: EPYTHON # @DEFAULT_UNSET # @DESCRIPTION: # The executable name of the current Python interpreter. # # This variable is set automatically in the following contexts: # # python-r1: Set in functions called by python_foreach_impl() or after # calling python_export_best(). # # python-single-r1: Set after calling python-single-r1_pkg_setup(). # # distutils-r1: Set within any of the python sub-phase functions. # # Example value: # @CODE # python2.7 # @CODE # @ECLASS-VARIABLE: PYTHON_SITEDIR # @DEFAULT_UNSET # @DESCRIPTION: # The path to Python site-packages directory. # # Set and exported on request using python_export(). # # Example value: # @CODE # /usr/lib64/python2.7/site-packages # @CODE # @ECLASS-VARIABLE: PYTHON_INCLUDEDIR # @DEFAULT_UNSET # @DESCRIPTION: # The path to Python include directory. # # Set and exported on request using python_export(). # # Example value: # @CODE # /usr/include/python2.7 # @CODE # @ECLASS-VARIABLE: PYTHON_LIBPATH # @DEFAULT_UNSET # @DESCRIPTION: # The path to Python library. # # Set and exported on request using python_export(). # Valid only for CPython. # # Example value: # @CODE # /usr/lib64/libpython2.7.so # @CODE # @ECLASS-VARIABLE: PYTHON_CFLAGS # @DEFAULT_UNSET # @DESCRIPTION: # Proper C compiler flags for building against Python. Obtained from # pkg-config or python-config. # # Set and exported on request using python_export(). # Valid only for CPython. Requires a proper build-time dependency # on the Python implementation and on pkg-config. # # Example value: # @CODE # -I/usr/include/python2.7 # @CODE # @ECLASS-VARIABLE: PYTHON_LIBS # @DEFAULT_UNSET # @DESCRIPTION: # Proper C compiler flags for linking against Python. Obtained from # pkg-config or python-config. # # Set and exported on request using python_export(). # Valid only for CPython. Requires a proper build-time dependency # on the Python implementation and on pkg-config. # # Example value: # @CODE # -lpython2.7 # @CODE # @ECLASS-VARIABLE: PYTHON_PKG_DEP # @DEFAULT_UNSET # @DESCRIPTION: # The complete dependency on a particular Python package as a string. # # Set and exported on request using python_export(). # # Example value: # @CODE # dev-lang/python:2.7[xml] # @CODE # @ECLASS-VARIABLE: PYTHON_SCRIPTDIR # @DEFAULT_UNSET # @DESCRIPTION: # The location where Python scripts must be installed for current impl. # # Set and exported on request using python_export(). # # Example value: # @CODE # /usr/lib/python-exec/python2.7 # @CODE # @FUNCTION: python_export # @USAGE: [] ... # @DESCRIPTION: # Set and export the Python implementation-relevant variables passed # as parameters. # # The optional first parameter may specify the requested Python # implementation (either as PYTHON_TARGETS value, e.g. python2_7, # or an EPYTHON one, e.g. python2.7). If no implementation passed, # the current one will be obtained from ${EPYTHON}. # # The variables which can be exported are: PYTHON, EPYTHON, # PYTHON_SITEDIR. They are described more completely in the eclass # variable documentation. python_export() { debug-print-function ${FUNCNAME} "${@}" local impl var case "${1}" in python*|jython*) impl=${1/_/.} shift ;; pypy|pypy3) impl=${1} shift ;; *) impl=${EPYTHON} if [[ -z ${impl} ]]; then die "python_export called without a python implementation and EPYTHON is unset" fi ;; esac debug-print "${FUNCNAME}: implementation: ${impl}" for var; do case "${var}" in EPYTHON) export EPYTHON=${impl} debug-print "${FUNCNAME}: EPYTHON = ${EPYTHON}" ;; PYTHON) export PYTHON=${EPREFIX}/usr/bin/${impl} debug-print "${FUNCNAME}: PYTHON = ${PYTHON}" ;; PYTHON_SITEDIR) local dir case "${impl}" in python*|pypy|pypy3) dir=/usr/$(get_libdir)/${impl} ;; jython*) dir=/usr/share/${impl/n/n-}/Lib ;; esac export PYTHON_SITEDIR=${EPREFIX}${dir}/site-packages debug-print "${FUNCNAME}: PYTHON_SITEDIR = ${PYTHON_SITEDIR}" ;; PYTHON_INCLUDEDIR) local dir case "${impl}" in python*) dir=/usr/include/${impl} ;; pypy|pypy3) dir=/usr/$(get_libdir)/${impl}/include ;; *) die "${impl} lacks header files" ;; esac export PYTHON_INCLUDEDIR=${EPREFIX}${dir} debug-print "${FUNCNAME}: PYTHON_INCLUDEDIR = ${PYTHON_INCLUDEDIR}" ;; PYTHON_LIBPATH) local libname case "${impl}" in python*) libname=lib${impl} ;; *) die "${impl} lacks a dynamic library" ;; esac local path=${EPREFIX}/usr/$(get_libdir) export PYTHON_LIBPATH=${path}/${libname}$(get_libname) debug-print "${FUNCNAME}: PYTHON_LIBPATH = ${PYTHON_LIBPATH}" ;; PYTHON_CFLAGS) local val case "${impl}" in python*) # python-2.7, python-3.2, etc. val=$($(tc-getPKG_CONFIG) --cflags ${impl/n/n-}) ;; *) die "${impl}: obtaining ${var} not supported" ;; esac export PYTHON_CFLAGS=${val} debug-print "${FUNCNAME}: PYTHON_CFLAGS = ${PYTHON_CFLAGS}" ;; PYTHON_LIBS) local val case "${impl}" in python*) # python-2.7, python-3.2, etc. val=$($(tc-getPKG_CONFIG) --libs ${impl/n/n-}) ;; *) die "${impl}: obtaining ${var} not supported" ;; esac export PYTHON_LIBS=${val} debug-print "${FUNCNAME}: PYTHON_LIBS = ${PYTHON_LIBS}" ;; PYTHON_PKG_DEP) local d case ${impl} in python2.7) PYTHON_PKG_DEP='>=dev-lang/python-2.7.5-r2:2.7';; python3.2) PYTHON_PKG_DEP='>=dev-lang/python-3.2.5-r2:3.2';; python3.3) PYTHON_PKG_DEP='>=dev-lang/python-3.3.2-r2:3.3';; python*) PYTHON_PKG_DEP="dev-lang/python:${impl#python}";; pypy) PYTHON_PKG_DEP='virtual/pypy:0=';; pypy3) PYTHON_PKG_DEP='virtual/pypy3:0=';; jython2.5) PYTHON_PKG_DEP='>=dev-java/jython-2.5.3-r2:2.5';; jython2.7) PYTHON_PKG_DEP='dev-java/jython:2.7';; *) die "Invalid implementation: ${impl}" esac # use-dep if [[ ${PYTHON_REQ_USE} ]]; then PYTHON_PKG_DEP+=[${PYTHON_REQ_USE}] fi export PYTHON_PKG_DEP debug-print "${FUNCNAME}: PYTHON_PKG_DEP = ${PYTHON_PKG_DEP}" ;; PYTHON_SCRIPTDIR) local dir export PYTHON_SCRIPTDIR=${EPREFIX}/usr/lib/python-exec/${impl} debug-print "${FUNCNAME}: PYTHON_SCRIPTDIR = ${PYTHON_SCRIPTDIR}" ;; *) die "python_export: unknown variable ${var}" esac done } # @FUNCTION: python_get_sitedir # @USAGE: [] # @DESCRIPTION: # Obtain and print the 'site-packages' path for the given # implementation. If no implementation is provided, ${EPYTHON} will # be used. # # If you just need to have PYTHON_SITEDIR set (and exported), then it is # better to use python_export() directly instead. python_get_sitedir() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_SITEDIR echo "${PYTHON_SITEDIR}" } # @FUNCTION: python_get_includedir # @USAGE: [] # @DESCRIPTION: # Obtain and print the include path for the given implementation. If no # implementation is provided, ${EPYTHON} will be used. # # If you just need to have PYTHON_INCLUDEDIR set (and exported), then it # is better to use python_export() directly instead. python_get_includedir() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_INCLUDEDIR echo "${PYTHON_INCLUDEDIR}" } # @FUNCTION: python_get_library_path # @USAGE: [] # @DESCRIPTION: # Obtain and print the Python library path for the given implementation. # If no implementation is provided, ${EPYTHON} will be used. # # Please note that this function can be used with CPython only. Use # in another implementation will result in a fatal failure. python_get_library_path() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_LIBPATH echo "${PYTHON_LIBPATH}" } # @FUNCTION: python_get_CFLAGS # @USAGE: [] # @DESCRIPTION: # Obtain and print the compiler flags for building against Python, # for the given implementation. If no implementation is provided, # ${EPYTHON} will be used. # # Please note that this function can be used with CPython only. # It requires Python and pkg-config installed, and therefore proper # build-time dependencies need be added to the ebuild. python_get_CFLAGS() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_CFLAGS echo "${PYTHON_CFLAGS}" } # @FUNCTION: python_get_LIBS # @USAGE: [] # @DESCRIPTION: # Obtain and print the compiler flags for linking against Python, # for the given implementation. If no implementation is provided, # ${EPYTHON} will be used. # # Please note that this function can be used with CPython only. # It requires Python and pkg-config installed, and therefore proper # build-time dependencies need be added to the ebuild. python_get_LIBS() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_LIBS echo "${PYTHON_LIBS}" } # @FUNCTION: python_get_scriptdir # @USAGE: [] # @DESCRIPTION: # Obtain and print the script install path for the given # implementation. If no implementation is provided, ${EPYTHON} will # be used. python_get_scriptdir() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON_SCRIPTDIR echo "${PYTHON_SCRIPTDIR}" } # @FUNCTION: _python_ln_rel # @USAGE: # @INTERNAL # @DESCRIPTION: # Create a relative symlink. _python_ln_rel() { debug-print-function ${FUNCNAME} "${@}" local target=${1} local symname=${2} local tgpath=${target%/*}/ local sympath=${symname%/*}/ local rel_target= while [[ ${sympath} ]]; do local tgseg= symseg= while [[ ! ${tgseg} && ${tgpath} ]]; do tgseg=${tgpath%%/*} tgpath=${tgpath#${tgseg}/} done while [[ ! ${symseg} && ${sympath} ]]; do symseg=${sympath%%/*} sympath=${sympath#${symseg}/} done if [[ ${tgseg} != ${symseg} ]]; then rel_target=../${rel_target}${tgseg:+${tgseg}/} fi done rel_target+=${tgpath}${target##*/} debug-print "${FUNCNAME}: ${symname} -> ${target}" debug-print "${FUNCNAME}: rel_target = ${rel_target}" ln -fs "${rel_target}" "${symname}" } # @FUNCTION: python_optimize # @USAGE: [...] # @DESCRIPTION: # Compile and optimize Python modules in specified directories (absolute # paths). If no directories are provided, the default system paths # are used (prepended with ${D}). python_optimize() { debug-print-function ${FUNCNAME} "${@}" if [[ ${EBUILD_PHASE} == pre* || ${EBUILD_PHASE} == post* ]]; then eerror "The new Python eclasses expect the compiled Python files to" eerror "be controlled by the Package Manager. For this reason," eerror "the python_optimize function can be used only during src_* phases" eerror "(src_install most commonly) and not during pkg_* phases." echo die "python_optimize is not to be used in pre/post* phases" fi [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).' local PYTHON=${PYTHON} [[ ${PYTHON} ]] || python_export PYTHON # Note: python2.6 can't handle passing files to compileall... # TODO: we do not support 2.6 any longer # default to sys.path if [[ ${#} -eq 0 ]]; then local f while IFS= read -r -d '' f; do # 1) accept only absolute paths # (i.e. skip '', '.' or anything like that) # 2) skip paths which do not exist # (python2.6 complains about them verbosely) if [[ ${f} == /* && -d ${D}${f} ]]; then set -- "${D}${f}" "${@}" fi done < <("${PYTHON}" -c 'import sys; print("\0".join(sys.path))') debug-print "${FUNCNAME}: using sys.path: ${*/%/;}" fi local d for d; do # make sure to get a nice path without // local instpath=${d#${D}} instpath=/${instpath##/} case "${EPYTHON}" in python*) "${PYTHON}" -m compileall -q -f -d "${instpath}" "${d}" "${PYTHON}" -OO -m compileall -q -f -d "${instpath}" "${d}" ;; *) "${PYTHON}" -m compileall -q -f -d "${instpath}" "${d}" ;; esac done } # @ECLASS-VARIABLE: python_scriptroot # @DEFAULT_UNSET # @DESCRIPTION: # The current script destination for python_doscript(). The path # is relative to the installation root (${ED}). # # When unset, ${DESTTREE}/bin (/usr/bin by default) will be used. # # Can be set indirectly through the python_scriptinto() function. # # Example: # @CODE # src_install() { # local python_scriptroot=${GAMES_BINDIR} # python_foreach_impl python_doscript foo # } # @CODE # @FUNCTION: python_scriptinto # @USAGE: # @DESCRIPTION: # Set the current scriptroot. The new value will be stored # in the 'python_scriptroot' environment variable. The new value need # be relative to the installation root (${ED}). # # Alternatively, you can set the variable directly. python_scriptinto() { debug-print-function ${FUNCNAME} "${@}" python_scriptroot=${1} } # @FUNCTION: python_doexe # @USAGE: ... # @DESCRIPTION: # Install the given executables into current python_scriptroot, # for the current Python implementation (${EPYTHON}). # # The executable will be wrapped properly for the Python implementation, # though no shebang mangling will be performed. python_doexe() { debug-print-function ${FUNCNAME} "${@}" local f for f; do python_newexe "${f}" "${f##*/}" done } # @FUNCTION: python_newexe # @USAGE: # @DESCRIPTION: # Install the given executable into current python_scriptroot, # for the current Python implementation (${EPYTHON}). # # The executable will be wrapped properly for the Python implementation, # though no shebang mangling will be performed. It will be renamed # to . python_newexe() { debug-print-function ${FUNCNAME} "${@}" [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).' [[ ${#} -eq 2 ]] || die "Usage: ${FUNCNAME} " local d=${python_scriptroot:-${DESTTREE}/bin} local wrapd=${d} local f=${1} local barefn=${2} local newfn if _python_want_python_exec2; then local PYTHON_SCRIPTDIR python_export PYTHON_SCRIPTDIR d=${PYTHON_SCRIPTDIR#${EPREFIX}} newfn=${barefn} else newfn=${barefn}-${EPYTHON} fi ( dodir "${wrapd}" exeinto "${d}" newexe "${f}" "${newfn}" || die ) # install the wrapper _python_ln_rel "${ED%/}"$(_python_get_wrapper_path) \ "${ED%/}/${wrapd}/${barefn}" || die # don't use this at home, just call python_doscript() instead if [[ ${_PYTHON_REWRITE_SHEBANG} ]]; then python_fix_shebang -q "${ED%/}/${d}/${newfn}" fi } # @FUNCTION: python_doscript # @USAGE: ... # @DESCRIPTION: # Install the given scripts into current python_scriptroot, # for the current Python implementation (${EPYTHON}). # # All specified files must start with a 'python' shebang. The shebang # will be converted, and the files will be wrapped properly # for the Python implementation. # # Example: # @CODE # src_install() { # python_foreach_impl python_doscript ${PN} # } # @CODE python_doscript() { debug-print-function ${FUNCNAME} "${@}" local _PYTHON_REWRITE_SHEBANG=1 python_doexe "${@}" } # @FUNCTION: python_newscript # @USAGE: # @DESCRIPTION: # Install the given script into current python_scriptroot # for the current Python implementation (${EPYTHON}), and name it # . # # The file must start with a 'python' shebang. The shebang will be # converted, and the file will be wrapped properly for the Python # implementation. It will be renamed to . # # Example: # @CODE # src_install() { # python_foreach_impl python_newscript foo.py foo # } # @CODE python_newscript() { debug-print-function ${FUNCNAME} "${@}" local _PYTHON_REWRITE_SHEBANG=1 python_newexe "${@}" } # @ECLASS-VARIABLE: python_moduleroot # @DEFAULT_UNSET # @DESCRIPTION: # The current module root for python_domodule(). The path can be either # an absolute system path (it must start with a slash, and ${ED} will be # prepended to it) or relative to the implementation's site-packages directory # (then it must start with a non-slash character). # # When unset, the modules will be installed in the site-packages root. # # Can be set indirectly through the python_moduleinto() function. # # Example: # @CODE # src_install() { # local python_moduleroot=bar # # installs ${PYTHON_SITEDIR}/bar/baz.py # python_foreach_impl python_domodule baz.py # } # @CODE # @FUNCTION: python_moduleinto # @USAGE: # @DESCRIPTION: # Set the current module root. The new value will be stored # in the 'python_moduleroot' environment variable. The new value need # be relative to the site-packages root. # # Alternatively, you can set the variable directly. python_moduleinto() { debug-print-function ${FUNCNAME} "${@}" python_moduleroot=${1} } # @FUNCTION: python_domodule # @USAGE: ... # @DESCRIPTION: # Install the given modules (or packages) into the current # python_moduleroot. The list can mention both modules (files) # and packages (directories). All listed files will be installed # for all enabled implementations, and compiled afterwards. # # Example: # @CODE # src_install() { # # (${PN} being a directory) # python_foreach_impl python_domodule ${PN} # } # @CODE python_domodule() { debug-print-function ${FUNCNAME} "${@}" [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).' local d if [[ ${python_moduleroot} == /* ]]; then # absolute path d=${python_moduleroot} else # relative to site-packages local PYTHON_SITEDIR=${PYTHON_SITEDIR} [[ ${PYTHON_SITEDIR} ]] || python_export PYTHON_SITEDIR d=${PYTHON_SITEDIR#${EPREFIX}}/${python_moduleroot} fi local INSDESTTREE insinto "${d}" doins -r "${@}" || die python_optimize "${ED}/${d}" } # @FUNCTION: python_doheader # @USAGE: ... # @DESCRIPTION: # Install the given headers into the implementation-specific include # directory. This function is unconditionally recursive, i.e. you can # pass directories instead of files. # # Example: # @CODE # src_install() { # python_foreach_impl python_doheader foo.h bar.h # } # @CODE python_doheader() { debug-print-function ${FUNCNAME} "${@}" [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).' local d PYTHON_INCLUDEDIR=${PYTHON_INCLUDEDIR} [[ ${PYTHON_INCLUDEDIR} ]] || python_export PYTHON_INCLUDEDIR d=${PYTHON_INCLUDEDIR#${EPREFIX}} local INSDESTTREE insinto "${d}" doins -r "${@}" || die } # @FUNCTION: python_wrapper_setup # @USAGE: [ []] # @DESCRIPTION: # Create proper 'python' executable and pkg-config wrappers # (if available) in the directory named by . Set up PATH # and PKG_CONFIG_PATH appropriately. defaults to ${T}/${EPYTHON}. # # The wrappers will be created for implementation named by , # or for one named by ${EPYTHON} if no passed. # # If the named directory contains a python symlink already, it will # be assumed to contain proper wrappers already and only environment # setup will be done. If wrapper update is requested, the directory # shall be removed first. python_wrapper_setup() { debug-print-function ${FUNCNAME} "${@}" local workdir=${1:-${T}/${EPYTHON}} local impl=${2:-${EPYTHON}} [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified." [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EPYTHON specified." if [[ ! -x ${workdir}/bin/python ]]; then mkdir -p "${workdir}"/{bin,pkgconfig} || die # Clean up, in case we were supposed to do a cheap update. rm -f "${workdir}"/bin/python{,2,3,-config} rm -f "${workdir}"/bin/2to3 rm -f "${workdir}"/pkgconfig/python{,2,3}.pc local EPYTHON PYTHON python_export "${impl}" EPYTHON PYTHON local pyver if python_is_python3; then pyver=3 else pyver=2 fi # Python interpreter ln -s "${PYTHON}" "${workdir}"/bin/python || die ln -s python "${workdir}"/bin/python${pyver} || die local nonsupp=() # CPython-specific if [[ ${EPYTHON} == python* ]]; then ln -s "${PYTHON}-config" "${workdir}"/bin/python-config || die # Python 2.6+. ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die # Python 2.7+. ln -s "${EPREFIX}"/usr/$(get_libdir)/pkgconfig/${EPYTHON/n/n-}.pc \ "${workdir}"/pkgconfig/python.pc || die ln -s python.pc "${workdir}"/pkgconfig/python${pyver}.pc || die else nonsupp+=( 2to3 python-config ) fi local x for x in "${nonsupp[@]}"; do cat >"${workdir}"/bin/${x} <<__EOF__ #!/bin/sh echo "${x} is not supported by ${EPYTHON}" >&2 exit 1 __EOF__ chmod +x "${workdir}"/bin/${x} || die done # Now, set the environment. # But note that ${workdir} may be shared with something else, # and thus already on top of PATH. if [[ ${PATH##:*} != ${workdir}/bin ]]; then PATH=${workdir}/bin${PATH:+:${PATH}} fi if [[ ${PKG_CONFIG_PATH##:*} != ${workdir}/pkgconfig ]]; then PKG_CONFIG_PATH=${workdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} fi export PATH PKG_CONFIG_PATH fi } # @FUNCTION: python_is_python3 # @USAGE: [] # @DESCRIPTION: # Check whether (or ${EPYTHON}) is a Python3k variant # (i.e. uses syntax and stdlib of Python 3.*). # # Returns 0 (true) if it is, 1 (false) otherwise. python_is_python3() { local impl=${1:-${EPYTHON}} [[ ${impl} ]] || die "python_is_python3: no impl nor EPYTHON" [[ ${impl} == python3* || ${impl} == pypy3 ]] } # @FUNCTION: python_is_installed # @USAGE: [] # @DESCRIPTION: # Check whether the interpreter for (or ${EPYTHON}) is installed. # Uses has_version with a proper dependency string. # # Returns 0 (true) if it is, 1 (false) otherwise. python_is_installed() { local impl=${1:-${EPYTHON}} [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EPYTHON" # for has_version local -x ROOT=/ case "${impl}" in pypy|pypy3) local append= if [[ ${PYTHON_REQ_USE} ]]; then append=[${PYTHON_REQ_USE}] fi # be happy with just the interpeter, no need for the virtual has_version "dev-python/${impl}${append}" \ || has_version "dev-python/${impl}-bin${append}" ;; *) local PYTHON_PKG_DEP python_export "${impl}" PYTHON_PKG_DEP has_version "${PYTHON_PKG_DEP}" ;; esac } # @FUNCTION: python_fix_shebang # @USAGE: [-f|--force] [-q|--quiet] ... # @DESCRIPTION: # Replace the shebang in Python scripts with the current Python # implementation (EPYTHON). If a directory is passed, works recursively # on all Python scripts. # # Only files having a 'python*' shebang will be modified. Files with # other shebang will either be skipped when working recursively # on a directory or treated as error when specified explicitly. # # Shebangs matching explicitly current Python version will be left # unmodified. Shebangs requesting another Python version will be treated # as fatal error, unless --force is given. # # --force causes the function to replace even shebangs that require # incompatible Python version. --quiet causes the function not to list # modified files verbosely. python_fix_shebang() { debug-print-function ${FUNCNAME} "${@}" [[ ${EPYTHON} ]] || die "${FUNCNAME}: EPYTHON unset (pkg_setup not called?)" local force quiet while [[ ${@} ]]; do case "${1}" in -f|--force) force=1; shift;; -q|--quiet) quiet=1; shift;; --) shift; break;; *) break;; esac done [[ ${1} ]] || die "${FUNCNAME}: no paths given" local path f for path; do local any_correct any_fixed is_recursive [[ -d ${path} ]] && is_recursive=1 while IFS= read -r -d '' f; do local shebang i local error= from= IFS= read -r shebang <${f} # First, check if it's shebang at all... if [[ ${shebang} == '#!'* ]]; then local split_shebang=() read -r -a split_shebang <<<${shebang} # Match left-to-right in a loop, to avoid matching random # repetitions like 'python2.7 python2'. for i in "${split_shebang[@]}"; do case "${i}" in *"${EPYTHON}") debug-print "${FUNCNAME}: in file ${f#${D}}" debug-print "${FUNCNAME}: shebang matches EPYTHON: ${shebang}" # Nothing to do, move along. any_correct=1 from=${EPYTHON} break ;; *python|*python[23]) debug-print "${FUNCNAME}: in file ${f#${D}}" debug-print "${FUNCNAME}: rewriting shebang: ${shebang}" if [[ ${i} == *python2 ]]; then from=python2 if [[ ! ${force} ]]; then python_is_python3 "${EPYTHON}" && error=1 fi elif [[ ${i} == *python3 ]]; then from=python3 if [[ ! ${force} ]]; then python_is_python3 "${EPYTHON}" || error=1 fi else from=python fi break ;; *python[23].[0123456789]|*pypy|*pypy3|*jython[23].[0123456789]) # Explicit mismatch. if [[ ! ${force} ]]; then error=1 else case "${i}" in *python[23].[0123456789]) from="python[23].[0123456789]";; *pypy) from="pypy";; *pypy3) from="pypy3";; *jython[23].[0123456789]) from="jython[23].[0123456789]";; *) die "${FUNCNAME}: internal error in 2nd pattern match";; esac fi break ;; esac done fi if [[ ! ${error} && ! ${from} ]]; then # Non-Python shebang. Allowed in recursive mode, # disallowed when specifying file explicitly. [[ ${is_recursive} ]] && continue error=1 fi if [[ ! ${quiet} ]]; then einfo "Fixing shebang in ${f#${D}}." fi if [[ ! ${error} ]]; then # We either want to match ${from} followed by space # or at end-of-string. if [[ ${shebang} == *${from}" "* ]]; then sed -i -e "1s:${from} :${EPYTHON} :" "${f}" || die else sed -i -e "1s:${from}$:${EPYTHON}:" "${f}" || die fi any_fixed=1 else eerror "The file has incompatible shebang:" eerror " file: ${f#${D}}" eerror " current shebang: ${shebang}" eerror " requested impl: ${EPYTHON}" die "${FUNCNAME}: conversion of incompatible shebang requested" fi done < <(find "${path}" -type f -print0) if [[ ! ${any_fixed} ]]; then eqawarn "QA warning: ${FUNCNAME}, ${path#${D}} did not match any fixable files." if [[ ${any_correct} ]]; then eqawarn "All files have ${EPYTHON} shebang already." else eqawarn "There are no Python files in specified directory." fi fi done } # @FUNCTION: _python_want_python_exec2 # @INTERNAL # @DESCRIPTION: # Check whether we should be using python-exec:2. _python_want_python_exec2() { debug-print-function ${FUNCNAME} "${@}" # EAPI 4 lacks slot operators, so just fix it on python-exec:2. [[ ${EAPI} == 4 ]] && return 0 # Check if we cached the result, or someone put an override. if [[ ! ${_PYTHON_WANT_PYTHON_EXEC2+1} ]]; then has_version 'dev-lang/python-exec:2' _PYTHON_WANT_PYTHON_EXEC2=$(( ! ${?} )) fi # Non-zero means 'yes', zero means 'no'. [[ ${_PYTHON_WANT_PYTHON_EXEC2} != 0 ]] } # @FUNCTION: _python_get_wrapper_path # @INTERNAL # @DESCRIPTION: # Output path to proper python-exec slot. _python_get_wrapper_path() { debug-print-function ${FUNCNAME} "${@}" if _python_want_python_exec2; then echo /usr/lib/python-exec/python-exec2 else echo /usr/bin/python-exec fi } # @FUNCTION: python_export_utf8_locale # @RETURN: 0 on success, 1 on failure. # @DESCRIPTION: # Attempts to export a usable UTF-8 locale in the LC_CTYPE variable. Does # nothing if LC_ALL is defined, or if the current locale uses a UTF-8 charmap. # This may be used to work around the quirky open() behavior of python3. python_export_utf8_locale() { debug-print-function ${FUNCNAME} "${@}" if [[ $(locale charmap) != UTF-8 ]]; then if [[ -n ${LC_ALL} ]]; then ewarn "LC_ALL is set to a locale with a charmap other than UTF-8." ewarn "This may trigger build failures in some python packages." return 1 fi # Try English first, then everything else. local lang locales="en_US.UTF-8 $(locale -a)" for lang in ${locales}; do if [[ $(LC_CTYPE=${lang} locale charmap 2>/dev/null) == UTF-8 ]]; then export LC_CTYPE=${lang} return 0 fi done ewarn "Could not find a UTF-8 locale. This may trigger build failures in" ewarn "some python packages. Please ensure that a UTF-8 locale is listed in" ewarn "/etc/locale.gen and run locale-gen." return 1 fi return 0 } _PYTHON_UTILS_R1=1 fi