# Copyright 1999-2022 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 # @ECLASS: python-single-r1.eclass # @MAINTAINER: # Python team # @AUTHOR: # Author: Michał Górny # Based on work of: Krzysztof Pawlik # @SUPPORTED_EAPIS: 6 7 8 # @PROVIDES: python-utils-r1 # @BLURB: An eclass for Python packages not installed for multiple implementations. # @DESCRIPTION: # An extension of the python-r1 eclass suite for packages which # don't support being installed for multiple Python implementations. # This mostly includes tools embedding Python and packages using foreign # build systems. # # This eclass sets correct IUSE. It also provides PYTHON_DEPS # and PYTHON_REQUIRED_USE that need to be added to appropriate ebuild # metadata variables. # # The eclass exports PYTHON_SINGLE_USEDEP that is suitable for depending # on other packages using the eclass. Dependencies on packages using # python-r1 should be created via python_gen_cond_dep() function, # using PYTHON_USEDEP placeholder. # # Please note that packages support multiple Python implementations # (using python-r1 eclass) can not depend on packages not supporting # them (using this eclass). # # Please note that python-single-r1 will always inherit python-utils-r1 # as well. Thus, all the functions defined there can be used # in the packages using python-single-r1, and there is no need ever # to inherit both. # # For more information, please see the Python Guide: # https://projects.gentoo.org/python/guide/ case "${EAPI:-0}" in [0-5]) die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}" ;; [6-8]) ;; *) die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;; esac if [[ ! ${_PYTHON_SINGLE_R1} ]]; then if [[ ${_PYTHON_R1} ]]; then die 'python-single-r1.eclass can not be used with python-r1.eclass.' elif [[ ${_PYTHON_ANY_R1} ]]; then die 'python-single-r1.eclass can not be used with python-any-r1.eclass.' fi inherit python-utils-r1 fi EXPORT_FUNCTIONS pkg_setup # @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_7 python3_3 python3_4 ) # @CODE # # Please note that you can also use bash brace expansion if you like: # @CODE # PYTHON_COMPAT=( python2_7 python3_{3,4} ) # @CODE # @ECLASS-VARIABLE: PYTHON_COMPAT_OVERRIDE # @USER_VARIABLE # @DEFAULT_UNSET # @DESCRIPTION: # This variable can be used when working with ebuilds to override # the in-ebuild PYTHON_COMPAT. It is a string naming the implementation # which package will be built for. It needs to be specified # in the calling environment, and not in ebuilds. # # It should be noted that in order to preserve metadata immutability, # PYTHON_COMPAT_OVERRIDE does not affect IUSE nor dependencies. # The state of PYTHON_SINGLE_TARGET is ignored, and the implementation # in PYTHON_COMPAT_OVERRIDE is built instead. Dependencies need to be # satisfied manually. # # Example: # @CODE # PYTHON_COMPAT_OVERRIDE='pypy' emerge -1v dev-python/bar # @CODE # @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. # # This should be set before calling `inherit'. # # Example: # @CODE # PYTHON_REQ_USE="gdbm,ncurses(-)?" # @CODE # # It will cause the Python dependencies to look like: # @CODE # python_single_target_pythonX_Y? ( dev-lang/python:X.Y[gdbm,ncurses(-)?] ) # @CODE # @ECLASS-VARIABLE: PYTHON_DEPS # @OUTPUT_VARIABLE # @DESCRIPTION: # This is an eclass-generated Python dependency string for all # implementations listed in PYTHON_COMPAT. # # The dependency string is conditional on PYTHON_SINGLE_TARGET. # # Example use: # @CODE # RDEPEND="${PYTHON_DEPS} # dev-foo/mydep" # BDEPEND="${PYTHON_DEPS}" # @CODE # # Example value: # @CODE # python_single_target_python2_7? ( dev-lang/python:2.7[gdbm] ) # python_single_target_pypy? ( dev-python/pypy[gdbm] ) # @CODE # @ECLASS-VARIABLE: PYTHON_SINGLE_USEDEP # @OUTPUT_VARIABLE # @DESCRIPTION: # This is an eclass-generated USE-dependency string which can be used to # depend on another python-single-r1 package being built for the same # Python implementations. # # If you need to depend on a multi-impl (python-r1) package, use # python_gen_cond_dep with PYTHON_USEDEP placeholder instead. # # Example use: # @CODE # RDEPEND="dev-python/foo[${PYTHON_SINGLE_USEDEP}]" # @CODE # # Example value: # @CODE # python_single_target_python3_4(-)? # @CODE # @ECLASS-VARIABLE: PYTHON_USEDEP # @OUTPUT_VARIABLE # @DESCRIPTION: # This is a placeholder variable supported by python_gen_cond_dep, # in order to depend on python-r1 packages built for the same Python # implementations. # # Example use: # @CODE # RDEPEND="$(python_gen_cond_dep ' # dev-python/foo[${PYTHON_USEDEP}] # ')" # @CODE # # Example value: # @CODE # python_targets_python3_4(-) # @CODE # @ECLASS-VARIABLE: PYTHON_REQUIRED_USE # @OUTPUT_VARIABLE # @DESCRIPTION: # This is an eclass-generated required-use expression which ensures # that exactly one PYTHON_SINGLE_TARGET value has been enabled. # # This expression should be utilized in an ebuild by including it in # REQUIRED_USE, optionally behind a use flag. # # Example use: # @CODE # REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )" # @CODE # # Example value: # @CODE # ^^ ( python_single_target_python2_7 python_single_target_python3_3 ) # @CODE _python_single_set_globals() { _python_set_impls local flags=( "${_PYTHON_SUPPORTED_IMPLS[@]/#/python_single_target_}" ) if [[ ${#_PYTHON_SUPPORTED_IMPLS[@]} -eq 1 ]]; then # if only one implementation is supported, use IUSE defaults # to avoid requesting the user to enable it IUSE="+${flags[0]}" else IUSE="${flags[*]}" fi local requse="^^ ( ${flags[*]} )" local single_flags="${flags[@]/%/(-)?}" local single_usedep=${single_flags// /,} local deps= i PYTHON_PKG_DEP for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do _python_export "${i}" PYTHON_PKG_DEP deps+="python_single_target_${i}? ( ${PYTHON_PKG_DEP} ) " done if [[ ${PYTHON_DEPS+1} ]]; then if [[ ${PYTHON_DEPS} != "${deps}" ]]; then eerror "PYTHON_DEPS have changed between inherits (PYTHON_REQ_USE?)!" eerror "Before: ${PYTHON_DEPS}" eerror "Now : ${deps}" die "PYTHON_DEPS integrity check failed" fi # these two are formality -- they depend on PYTHON_COMPAT only if [[ ${PYTHON_REQUIRED_USE} != ${requse} ]]; then eerror "PYTHON_REQUIRED_USE have changed between inherits!" eerror "Before: ${PYTHON_REQUIRED_USE}" eerror "Now : ${requse}" die "PYTHON_REQUIRED_USE integrity check failed" fi if [[ ${PYTHON_SINGLE_USEDEP} != "${single_usedep}" ]]; then eerror "PYTHON_SINGLE_USEDEP have changed between inherits!" eerror "Before: ${PYTHON_SINGLE_USEDEP}" eerror "Now : ${single_usedep}" die "PYTHON_SINGLE_USEDEP integrity check failed" fi else PYTHON_DEPS=${deps} PYTHON_REQUIRED_USE=${requse} PYTHON_USEDEP='%PYTHON_USEDEP-NEEDS-TO-BE-USED-IN-PYTHON_GEN_COND_DEP%' PYTHON_SINGLE_USEDEP=${single_usedep} readonly PYTHON_DEPS PYTHON_REQUIRED_USE PYTHON_SINGLE_USEDEP \ PYTHON_USEDEP fi } _python_single_set_globals unset -f _python_single_set_globals if [[ ! ${_PYTHON_SINGLE_R1} ]]; then # @FUNCTION: _python_gen_usedep # @USAGE: [...] # @INTERNAL # @DESCRIPTION: # Output a USE dependency string for Python implementations which # are both in PYTHON_COMPAT and match any of the patterns passed # as parameters to the function. # # The patterns are fnmatch-style patterns (matched via bash # == operator against PYTHON_COMPAT values). Remember to escape # or quote the fnmatch patterns to prevent accidental shell filename # expansion. # # This is an internal function used to implement python_gen_cond_dep. _python_gen_usedep() { debug-print-function ${FUNCNAME} "${@}" local impl matches=() _python_verify_patterns "${@}" for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do if _python_impl_matches "${impl}" "${@}"; then matches+=( "python_single_target_${impl}(-)?" ) fi done [[ ${matches[@]} ]] || die "No supported implementations match python_gen_usedep patterns: ${@}" local out=${matches[@]} echo "${out// /,}" } # @FUNCTION: python_gen_useflags # @USAGE: [...] # @DESCRIPTION: # Output a list of USE flags for Python implementations which # are both in PYTHON_COMPAT and match any of the patterns passed # as parameters to the function. # # The patterns are fnmatch-style patterns (matched via bash # == operator against PYTHON_COMPAT values). Remember to escape # or quote the fnmatch patterns to prevent accidental shell filename # expansion. # # Example: # @CODE # PYTHON_COMPAT=( python{2_7,3_4} ) # REQUIRED_USE="doc? ( ^^ ( $(python_gen_useflags 'python2*') ) )" # @CODE # # It will cause the variable to look like: # @CODE # REQUIRED_USE="doc? ( ^^ ( python_single_target_python2_7 ) )" # @CODE python_gen_useflags() { debug-print-function ${FUNCNAME} "${@}" local impl matches=() _python_verify_patterns "${@}" for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do if _python_impl_matches "${impl}" "${@}"; then matches+=( "python_single_target_${impl}" ) fi done echo "${matches[@]}" } # @FUNCTION: python_gen_cond_dep # @USAGE: [...] # @DESCRIPTION: # Output a list of -ies made conditional to USE flags # of Python implementations which are both in PYTHON_COMPAT and match # any of the patterns passed as the remaining parameters. # # The patterns are fnmatch-style patterns (matched via bash # == operator against PYTHON_COMPAT values). Remember to escape # or quote the fnmatch patterns to prevent accidental shell filename # expansion. # # In order to enforce USE constraints on the packages, verbatim # '${PYTHON_SINGLE_USEDEP}' and '${PYTHON_USEDEP}' (quoted!) may # be placed in the dependency specification. It will get expanded within # the function into a proper USE dependency string. # # Example: # @CODE # PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy ) # RDEPEND="$(python_gen_cond_dep \ # 'dev-python/unittest2[${PYTHON_USEDEP}]' python2_7 pypy )" # @CODE # # It will cause the variable to look like: # @CODE # RDEPEND="python_single_target_python2_7? ( # dev-python/unittest2[python_targets_python2_7(-)?,...] ) # python_single_target_pypy? ( # dev-python/unittest2[python_targets_pypy(-)?,...] )" # @CODE python_gen_cond_dep() { debug-print-function ${FUNCNAME} "${@}" local impl matches=() local dep=${1} shift _python_verify_patterns "${@}" for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do if _python_impl_matches "${impl}" "${@}"; then # substitute ${PYTHON_SINGLE_USEDEP} if used # (since python_gen_usedep() will not return # ${PYTHON_SINGLE_USEDEP}, the code is run at most once) if [[ ${dep} == *'${PYTHON_SINGLE_USEDEP}'* ]]; then local usedep=$(_python_gen_usedep "${@}") dep=${dep//\$\{PYTHON_SINGLE_USEDEP\}/${usedep}} fi local multi_usedep="python_targets_${impl}(-)" if [[ ${EAPI} != [67] ]]; then if [[ ${dep} == *\$\{PYTHON_MULTI_USEDEP\}* ]]; then die "Replace PYTHON_MULTI_USEDEP with PYTHON_USEDEP in EAPI ${EAPI}" fi fi local subdep=${dep//\$\{PYTHON_MULTI_USEDEP\}/${multi_usedep}} matches+=( "python_single_target_${impl}? ( ${subdep//\$\{PYTHON_USEDEP\}/${multi_usedep}} )" ) fi done echo "${matches[@]}" } # @FUNCTION: python_gen_impl_dep # @USAGE: [ [...]] # @DESCRIPTION: # Output a dependency on Python implementations with the specified USE # dependency string appended, or no USE dependency string if called # without the argument (or with empty argument). If any implementation # patterns are passed, the output dependencies will be generated only # for the implementations matching them. # # The patterns are fnmatch-style patterns (matched via bash # == operator against PYTHON_COMPAT values). Remember to escape # or quote the fnmatch patterns to prevent accidental shell filename # expansion. # # Use this function when you need to request different USE flags # on the Python interpreter depending on package's USE flags. If you # only need a single set of interpreter USE flags, just set # PYTHON_REQ_USE and use ${PYTHON_DEPS} globally. # # Example: # @CODE # PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy ) # RDEPEND="foo? ( $(python_gen_impl_dep 'xml(+)') )" # @CODE # # It will cause the variable to look like: # @CODE # RDEPEND="foo? ( # python_single_target_python2_7? ( # dev-lang/python:2.7[xml(+)] ) # python_single_target_pypy? ( # dev-python/pypy[xml(+)] ) )" # @CODE python_gen_impl_dep() { debug-print-function ${FUNCNAME} "${@}" local impl local matches=() local PYTHON_REQ_USE=${1} shift _python_verify_patterns "${@}" for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do if _python_impl_matches "${impl}" "${@}"; then local PYTHON_PKG_DEP _python_export "${impl}" PYTHON_PKG_DEP matches+=( "python_single_target_${impl}? ( ${PYTHON_PKG_DEP} )" ) fi done echo "${matches[@]}" } # @FUNCTION: python_setup # @DESCRIPTION: # Determine what the selected Python implementation is and set # the Python build environment up for it. python_setup() { debug-print-function ${FUNCNAME} "${@}" unset EPYTHON # support developer override if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then local impls=( ${PYTHON_COMPAT_OVERRIDE} ) [[ ${#impls[@]} -eq 1 ]] || die "PYTHON_COMPAT_OVERRIDE must name exactly one implementation for python-single-r1" ewarn "WARNING: PYTHON_COMPAT_OVERRIDE in effect. The following Python" ewarn "implementation will be used:" ewarn ewarn " ${PYTHON_COMPAT_OVERRIDE}" ewarn ewarn "Dependencies won't be satisfied, and PYTHON_SINGLE_TARGET flags will be ignored." _python_export "${impls[0]}" EPYTHON PYTHON _python_wrapper_setup einfo "Using ${EPYTHON} to build" return fi local impl for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do if use "python_single_target_${impl}"; then if [[ ${EPYTHON} ]]; then eerror "Your PYTHON_SINGLE_TARGET setting lists more than a single Python" eerror "implementation. Please set it to just one value. If you need" eerror "to override the value for a single package, please use package.env" eerror "or an equivalent solution (man 5 portage)." echo die "More than one implementation in PYTHON_SINGLE_TARGET." fi _python_export "${impl}" EPYTHON PYTHON _python_wrapper_setup einfo "Using ${EPYTHON} to build" fi done if [[ ! ${EPYTHON} ]]; then eerror "No Python implementation selected for the build. Please set" eerror "the PYTHON_SINGLE_TARGET variable in your make.conf to one" eerror "of the following values:" eerror eerror "${_PYTHON_SUPPORTED_IMPLS[@]}" echo die "No supported Python implementation in PYTHON_SINGLE_TARGET." fi } # @FUNCTION: python-single-r1_pkg_setup # @DESCRIPTION: # Runs python_setup. python-single-r1_pkg_setup() { debug-print-function ${FUNCNAME} "${@}" [[ ${MERGE_TYPE} != binary ]] && python_setup } _PYTHON_SINGLE_R1=1 fi