| # Copyright 1999-2012 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| # $Header: /var/cvsroot/gentoo-x86/eclass/python-r1.eclass,v 1.24 2012/11/30 11:40:15 mgorny Exp $ |
| |
| # @ECLASS: python-r1 |
| # @MAINTAINER: |
| # Michał Górny <mgorny@gentoo.org> |
| # Python herd <python@gentoo.org> |
| # @AUTHOR: |
| # Author: Michał Górny <mgorny@gentoo.org> |
| # Based on work of: Krzysztof Pawlik <nelchael@gentoo.org> |
| # @BLURB: A common, simple eclass for Python packages. |
| # @DESCRIPTION: |
| # A common eclass providing helper functions to build and install |
| # packages supporting being installed for multiple Python |
| # implementations. |
| # |
| # This eclass sets correct IUSE and REQUIRED_USE. It exports PYTHON_DEPS |
| # and PYTHON_USEDEP so you can create correct dependencies for your |
| # package easily. It also provides methods to easily run a command for |
| # each enabled Python implementation and duplicate the sources for them. |
| # |
| # Please note that python-r1 will always inherit python-utils-r1 as |
| # well. Thus, all the functions defined there can be used |
| # in the packages using python-r1, and there is no need ever to inherit |
| # both. |
| # |
| # 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) |
| die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}" |
| ;; |
| 4|5) |
| # EAPI=4 needed for REQUIRED_USE |
| ;; |
| *) |
| die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" |
| ;; |
| esac |
| |
| if [[ ! ${_PYTHON_R1} ]]; then |
| |
| if [[ ${_PYTHON_SINGLE_R1} ]]; then |
| die 'python-r1.eclass can not be used with python-single-r1.eclass.' |
| fi |
| |
| inherit python-utils-r1 |
| |
| # @ECLASS-VARIABLE: PYTHON_COMPAT |
| # @REQUIRED |
| # @DESCRIPTION: |
| # This variable contains a list of Python implementations the package |
| # supports. It must be set before the `inherit' call. It has to be |
| # an array. |
| # |
| # Example: |
| # @CODE |
| # PYTHON_COMPAT=( python2_5 python2_6 python2_7 ) |
| # @CODE |
| # |
| # Please note that you can also use bash brace expansion if you like: |
| # @CODE |
| # PYTHON_COMPAT=( python{2_5,2_6,2_7} ) |
| # @CODE |
| if ! declare -p PYTHON_COMPAT &>/dev/null; then |
| if [[ ${CATEGORY}/${PN} == dev-python/python-exec ]]; then |
| PYTHON_COMPAT=( "${_PYTHON_ALL_IMPLS[@]}" ) |
| else |
| die 'PYTHON_COMPAT not declared.' |
| fi |
| fi |
| |
| # @ECLASS-VARIABLE: PYTHON_REQ_USE |
| # @DEFAULT_UNSET |
| # @DESCRIPTION: |
| # The list of USEflags required to be enabled on the chosen Python |
| # implementations, formed as a USE-dependency string. It should be valid |
| # for all implementations in PYTHON_COMPAT, so it may be necessary to |
| # use USE defaults. |
| # |
| # Example: |
| # @CODE |
| # PYTHON_REQ_USE="gdbm,ncurses(-)?" |
| # @CODE |
| # |
| # It will cause the Python dependencies to look like: |
| # @CODE |
| # python_targets_pythonX_Y? ( dev-lang/python:X.Y[gdbm,ncurses(-)?] ) |
| # @CODE |
| |
| # @ECLASS-VARIABLE: PYTHON_DEPS |
| # @DESCRIPTION: |
| # This is an eclass-generated Python dependency string for all |
| # implementations listed in PYTHON_COMPAT. |
| # |
| # Example use: |
| # @CODE |
| # RDEPEND="${PYTHON_DEPS} |
| # dev-foo/mydep" |
| # DEPEND="${RDEPEND}" |
| # @CODE |
| # |
| # Example value: |
| # @CODE |
| # dev-python/python-exec |
| # python_targets_python2_6? ( dev-lang/python:2.6[gdbm] ) |
| # python_targets_python2_7? ( dev-lang/python:2.7[gdbm] ) |
| # @CODE |
| |
| # @ECLASS-VARIABLE: PYTHON_USEDEP |
| # @DESCRIPTION: |
| # This is an eclass-generated USE-dependency string which can be used to |
| # depend on another Python package being built for the same Python |
| # implementations. |
| # |
| # The generate USE-flag list is compatible with packages using python-r1 |
| # and python-distutils-ng eclasses. It must not be used on packages |
| # using python.eclass. |
| # |
| # Example use: |
| # @CODE |
| # RDEPEND="dev-python/foo[${PYTHON_USEDEP}]" |
| # @CODE |
| # |
| # Example value: |
| # @CODE |
| # python_targets_python2_6?,python_targets_python2_7? |
| # @CODE |
| |
| _python_set_globals() { |
| local flags=( "${PYTHON_COMPAT[@]/#/python_targets_}" ) |
| local optflags=${flags[@]/%/?} |
| |
| IUSE=${flags[*]} |
| REQUIRED_USE="|| ( ${flags[*]} )" |
| PYTHON_USEDEP=${optflags// /,} |
| |
| local usestr |
| [[ ${PYTHON_REQ_USE} ]] && usestr="[${PYTHON_REQ_USE}]" |
| |
| # 1) well, python-exec would suffice as an RDEP |
| # but no point in making this overcomplex, BDEP doesn't hurt anyone |
| # 2) python-exec should be built with all targets forced anyway |
| # but if new targets were added, we may need to force a rebuild |
| PYTHON_DEPS="dev-python/python-exec[${PYTHON_USEDEP}]" |
| local i |
| for i in "${PYTHON_COMPAT[@]}"; do |
| local d |
| case ${i} in |
| python*) |
| d='dev-lang/python';; |
| jython*) |
| d='dev-java/jython';; |
| pypy*) |
| d='dev-python/pypy';; |
| *) |
| die "Invalid implementation: ${i}" |
| esac |
| |
| local v=${i##*[a-z]} |
| PYTHON_DEPS+=" python_targets_${i}? ( ${d}:${v/_/.}${usestr} )" |
| done |
| } |
| _python_set_globals |
| |
| # @ECLASS-VARIABLE: BUILD_DIR |
| # @DESCRIPTION: |
| # The current build directory. In global scope, it is supposed to |
| # contain an initial build directory; if unset, it defaults to ${S}. |
| # |
| # In functions run by python_foreach_impl(), the BUILD_DIR is locally |
| # set to an implementation-specific build directory. That path is |
| # created through appending a hyphen and the implementation name |
| # to the final component of the initial BUILD_DIR. |
| # |
| # Example value: |
| # @CODE |
| # ${WORKDIR}/foo-1.3-python2_6 |
| # @CODE |
| |
| # @FUNCTION: python_copy_sources |
| # @DESCRIPTION: |
| # Create a single copy of the package sources (${S}) for each enabled |
| # Python implementation. |
| # |
| # The sources are always copied from S to implementation-specific build |
| # directories respecting BUILD_DIR. |
| python_copy_sources() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| local impl |
| local bdir=${BUILD_DIR:-${S}} |
| |
| debug-print "${FUNCNAME}: bdir = ${bdir}" |
| einfo "Will copy sources from ${S}" |
| # the order is irrelevant here |
| for impl in "${PYTHON_COMPAT[@]}"; do |
| if use "python_targets_${impl}" |
| then |
| local BUILD_DIR=${bdir%%/}-${impl} |
| |
| einfo "${impl}: copying to ${BUILD_DIR}" |
| debug-print "${FUNCNAME}: [${impl}] cp ${S} => ${BUILD_DIR}" |
| cp -pr "${S}" "${BUILD_DIR}" || die |
| fi |
| done |
| } |
| |
| # @FUNCTION: _python_check_USE_PYTHON |
| # @INTERNAL |
| # @DESCRIPTION: |
| # Check whether USE_PYTHON and PYTHON_TARGETS are in sync. Output |
| # warnings if they are not. |
| _python_check_USE_PYTHON() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| if [[ ! ${_PYTHON_USE_PYTHON_CHECKED} ]]; then |
| _PYTHON_USE_PYTHON_CHECKED=1 |
| |
| # python-exec has profile-forced flags. |
| if [[ ${CATEGORY}/${PN} == dev-python/python-exec ]]; then |
| return |
| fi |
| |
| _try_eselect() { |
| # The eselect solution will work only with one py2 & py3. |
| |
| local impl py2 py3 dis_py2 dis_py3 |
| for impl in "${PYTHON_COMPAT[@]}"; do |
| if use "python_targets_${impl}"; then |
| case "${impl}" in |
| python2_*) |
| if [[ ${py2+1} ]]; then |
| debug-print "${FUNCNAME}: -> more than one py2: ${py2} ${impl}" |
| return 1 |
| fi |
| py2=${impl/_/.} |
| ;; |
| python3_*) |
| if [[ ${py3+1} ]]; then |
| debug-print "${FUNCNAME}: -> more than one py3: ${py3} ${impl}" |
| return 1 |
| fi |
| py3=${impl/_/.} |
| ;; |
| *) |
| return 1 |
| ;; |
| esac |
| else |
| case "${impl}" in |
| python2_*) |
| dis_py2=1 |
| ;; |
| python3_*) |
| dis_py3=1 |
| ;; |
| esac |
| fi |
| done |
| |
| # The eselect solution won't work if the disabled Python version |
| # is installed. |
| if [[ ! ${py2+1} && ${dis_py2} ]]; then |
| debug-print "${FUNCNAME}: -> all py2 versions disabled" |
| if has_version '=dev-lang/python-2*'; then |
| debug-print "${FUNCNAME}: ---> but =python-2* installed!" |
| return 1 |
| fi |
| fi |
| if [[ ! ${py3+1} && ${dis_py3} ]]; then |
| debug-print "${FUNCNAME}: -> all py3 versions disabled" |
| if has_version '=dev-lang/python-3*'; then |
| debug-print "${FUNCNAME}: ---> but =python-3* installed!" |
| return 1 |
| fi |
| fi |
| |
| local warned |
| |
| # Now check whether the correct implementations are active. |
| if [[ ${py2+1} ]]; then |
| local sel_py2=$(eselect python show --python2) |
| |
| debug-print "${FUNCNAME}: -> py2 built: ${py2}, active: ${sel_py2}" |
| if [[ ${py2} != ${sel_py2} ]]; then |
| ewarn "Building package for ${py2} only while ${sel_py2} is active." |
| ewarn "Please consider switching the active Python 2 interpreter:" |
| ewarn |
| ewarn " eselect python set --python2 ${py2}" |
| warned=1 |
| fi |
| fi |
| |
| if [[ ${py3+1} ]]; then |
| local sel_py3=$(eselect python show --python3) |
| |
| debug-print "${FUNCNAME}: -> py3 built: ${py3}, active: ${sel_py3}" |
| if [[ ${py3} != ${sel_py3} ]]; then |
| [[ ${warned} ]] && ewarn |
| ewarn "Building package for ${py3} only while ${sel_py3} is active." |
| ewarn "Please consider switching the active Python 3 interpreter:" |
| ewarn |
| ewarn " eselect python set --python3 ${py3}" |
| warned=1 |
| fi |
| fi |
| |
| if [[ ${warned} ]]; then |
| ewarn |
| ewarn "Please note that after switching the active Python interpreter," |
| ewarn "you may need to run 'python-updater' to rebuild affected packages." |
| ewarn |
| ewarn "For more information on python.eclass compatibility, please see" |
| ewarn "the appropriate python-r1 User's Guide chapter [1]." |
| ewarn |
| ewarn "[1] http://www.gentoo.org/proj/en/Python/python-r1/user-guide.xml#doc_chap2" |
| fi |
| } |
| |
| # If user has no USE_PYTHON, try to avoid it. |
| if [[ ! ${USE_PYTHON} ]]; then |
| debug-print "${FUNCNAME}: trying eselect solution ..." |
| _try_eselect && return |
| fi |
| |
| debug-print "${FUNCNAME}: trying USE_PYTHON solution ..." |
| debug-print "${FUNCNAME}: -> USE_PYTHON=${USE_PYTHON}" |
| |
| local impl old=${USE_PYTHON} new=() removed=() |
| |
| for impl in "${PYTHON_COMPAT[@]}"; do |
| local abi |
| case "${impl}" in |
| python*) |
| abi=${impl#python} |
| ;; |
| jython*) |
| abi=${impl#jython}-jython |
| ;; |
| pypy*) |
| abi=2.7-pypy-${impl#pypy} |
| ;; |
| *) |
| die "Unexpected Python implementation: ${impl}" |
| ;; |
| esac |
| abi=${abi/_/.} |
| |
| has "${abi}" ${USE_PYTHON} |
| local has_abi=${?} |
| use "python_targets_${impl}" |
| local has_impl=${?} |
| |
| # 0 = has, 1 = does not have |
| if [[ ${has_abi} == 0 && ${has_impl} == 1 ]]; then |
| debug-print "${FUNCNAME}: ---> remove ${abi}" |
| # remove from USE_PYTHON |
| old=${old/${abi}/} |
| removed+=( ${abi} ) |
| elif [[ ${has_abi} == 1 && ${has_impl} == 0 ]]; then |
| debug-print "${FUNCNAME}: ---> add ${abi}" |
| # add to USE_PYTHON |
| new+=( ${abi} ) |
| fi |
| done |
| |
| if [[ ${removed[@]} || ${new[@]} ]]; then |
| old=( ${old} ) |
| |
| debug-print "${FUNCNAME}: -> old: ${old[@]}" |
| debug-print "${FUNCNAME}: -> new: ${new[@]}" |
| debug-print "${FUNCNAME}: -> removed: ${removed[@]}" |
| |
| if [[ ${USE_PYTHON} ]]; then |
| ewarn "It seems that your USE_PYTHON setting lists different Python" |
| ewarn "implementations than your PYTHON_TARGETS variable. Please consider" |
| ewarn "using the following value instead:" |
| ewarn |
| ewarn " USE_PYTHON='\033[35m${old[@]}${new[@]+ \033[1m${new[@]}}\033[0m'" |
| |
| if [[ ${removed[@]} ]]; then |
| ewarn |
| ewarn "(removed \033[31m${removed[@]}\033[0m)" |
| fi |
| else |
| ewarn "It seems that you need to set USE_PYTHON to make sure that legacy" |
| ewarn "packages will be built with respect to PYTHON_TARGETS correctly:" |
| ewarn |
| ewarn " USE_PYTHON='\033[35;1m${new[@]}\033[0m'" |
| fi |
| |
| ewarn |
| ewarn "Please note that after changing the USE_PYTHON variable, you may need" |
| ewarn "to run 'python-updater' to rebuild affected packages." |
| ewarn |
| ewarn "For more information on python.eclass compatibility, please see" |
| ewarn "the appropriate python-r1 User's Guide chapter [1]." |
| ewarn |
| ewarn "[1] http://www.gentoo.org/proj/en/Python/python-r1/user-guide.xml#doc_chap2" |
| fi |
| fi |
| } |
| |
| # @FUNCTION: python_foreach_impl |
| # @USAGE: <command> [<args>...] |
| # @DESCRIPTION: |
| # Run the given command for each of the enabled Python implementations. |
| # If additional parameters are passed, they will be passed through |
| # to the command. If the command fails, python_foreach_impl dies. |
| # If necessary, use ':' to force a successful return. |
| # |
| # For each command being run, EPYTHON, PYTHON and BUILD_DIR are set |
| # locally, and the former two are exported to the command environment. |
| python_foreach_impl() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| _python_check_USE_PYTHON |
| |
| local impl |
| local bdir=${BUILD_DIR:-${S}} |
| |
| debug-print "${FUNCNAME}: bdir = ${bdir}" |
| for impl in "${_PYTHON_ALL_IMPLS[@]}"; do |
| if has "${impl}" "${PYTHON_COMPAT[@]}" && use "python_targets_${impl}" |
| then |
| local EPYTHON PYTHON |
| python_export "${impl}" EPYTHON PYTHON |
| local BUILD_DIR=${bdir%%/}-${impl} |
| export EPYTHON PYTHON |
| |
| einfo "${EPYTHON}: running ${@}" |
| "${@}" || die "${EPYTHON}: ${1} failed" |
| fi |
| done |
| } |
| |
| # @FUNCTION: python_export_best |
| # @USAGE: [<variable>...] |
| # @DESCRIPTION: |
| # Find the best (most preferred) Python implementation enabled |
| # and export given variables for it. If no variables are provided, |
| # EPYTHON & PYTHON will be exported. |
| python_export_best() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| [[ ${#} -gt 0 ]] || set -- EPYTHON PYTHON |
| |
| local impl best |
| for impl in "${_PYTHON_ALL_IMPLS[@]}"; do |
| if has "${impl}" "${PYTHON_COMPAT[@]}" && use "python_targets_${impl}" |
| then |
| best=${impl} |
| fi |
| done |
| |
| [[ ${best+1} ]] || die "python_export_best(): no implementation found!" |
| |
| debug-print "${FUNCNAME}: Best implementation is: ${impl}" |
| python_export "${impl}" "${@}" |
| } |
| |
| # @FUNCTION: python_replicate_script |
| # @USAGE: <path>... |
| # @DESCRIPTION: |
| # Copy the given script to variants for all enabled Python |
| # implementations, then replace it with a symlink to the wrapper. |
| # |
| # All specified files must start with a 'python' shebang. A file not |
| # having a matching shebang will be refused. |
| python_replicate_script() { |
| debug-print-function ${FUNCNAME} "${@}" |
| |
| if [[ ${_PYTHON_SINGLE_R1} ]]; then |
| die "${FUNCNAME} must not be used with python-single-r1 eclass." |
| fi |
| |
| local suffixes=() |
| |
| _add_suffix() { |
| suffixes+=( "${EPYTHON}" ) |
| } |
| python_foreach_impl _add_suffix |
| debug-print "${FUNCNAME}: suffixes = ( ${suffixes[@]} )" |
| |
| local f suffix |
| for suffix in "${suffixes[@]}"; do |
| for f; do |
| local newf=${f}-${suffix} |
| |
| debug-print "${FUNCNAME}: ${f} -> ${newf}" |
| cp "${f}" "${newf}" || die |
| done |
| |
| _python_rewrite_shebang "${suffix}" "${@/%/-${suffix}}" |
| done |
| |
| for f; do |
| _python_ln_rel "${ED}"/usr/bin/python-exec "${f}" || die |
| done |
| } |
| |
| _PYTHON_R1=1 |
| fi |