# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ # @ECLASS: python-multilib-utils-r1 # @MAINTAINER: # Python team # @AUTHOR: # Greg Turner # Based on work of: Michał Górny # Based on work of: Krzysztof Pawlik # @BLURB: Utility functions for packages with Python parts. # @DESCRIPTION: # Utility eclass providing functions to query Python implementations, # and to install Python modules and scripts for multilib ebuilds. # # This eclass does not automaticaly set any metadata variables # nor export any phase functions; therefore, it can be inherited # safely, or, at least, without immediate, nonfatal detriment to its # sub-eclasses and ebuild-consumers. # # This is the foundational eclass for the experimental python-multilib-r1 # framework. That framework is based as closely as sensibly possible on # the python-r1 framework. For information about the python-r1 framework, # please see the python-r1 Developer's Guide: # http://www.gentoo.org/proj/en/Python/python-r1/dev-guide.xml # # Like the pythron-r1 framework, the python-multilib-r1 framework # has different kinds of python "impl," which can be taken to be short # for "implementation specifier" or "implementation selector". It # retains the two kinds used in the python-r1 framework, which I # will explain below, and provides two additional kinds of specifiers, # which respectively shadow the python-r1 specifier types. # # Specifically, where the python-r1 framework refers to # "PYTHON_COMPAT"-format specifiers, the python-multilib-r1 framework # additionally refers to PYTHON_COMPAT_MULTILIB specifiers. These # specifiers differ in two major ways from their PYTHON_COMPAT # counterparts. First, PYTHON_COMPAT, the bash variable, is provided # to both the python-r1 framework and the python-multilib-r1 framework # from without, typically by the ebuild author. By contrast, the # PYTHON_COMPAT_MULTILIB variable is generated by the # python-multilib-r1 framework, in the front-end eclasses such as # python-multilib-r1, distutils-multilib-r1, and # python-multilib-single-r1. Second, PYTHON_COMPAT_MULTILIB # specifiers look different from PYTHON_COMPAT specifiers in that # they have a multilib ABI specifier appended to them. For example, # the PYTHON_COMPAT specifier for pypy 2.0 is pypy2_0, regardless of # the ABI it pertains to. However, pypy 2.0 PYTHON_COMPAT_MULTILIB # specifiers look like "pypy2_0_x86_x86" or "pypy2_0_mips_n64". # They are formed by stripping "abi" from the front of the "abi_*" # use-flag managed by multilib-build.eclass (see: _MULTILIB_FLAGS # in that eclass), and appending the remaining portion to the # corresponding PYTHON_COMPAT impl specifier. # # In python-r1, there is a second kind of implementation specifier, # usually referred to as "EPYTHON". EPYTHON specifiers are, so to # speak, "closer to the metal" than PYTHON_COMPAT specifiers. Whereas # PYTHON_COMPAT specifiers are by design suitable for use in ebuild # metadata that may, after some slicing and dicing, be included in # package manager caches, EPYTHON-format specifiers are by design # ebuild-runtime-only constructs. There is no such thing as an # EPYTHON_MULTILIB variable; instead, either no-multilib or multilib # EPYTHON specifiers may be used in the same places. To construct a # no-multilib EPYTHON specifier from a multilib EPYTHON specifier, # one needs only append the associated value of the ABI variable. # For example, in a no-multilib x86 profile, the no-multilib # EPYTHON specifier "python2.7" may be used interchangably with the # multilib EPYTHON specifier "python2.7-x86", because the value # of the DEFAULT_ABI variable is "x86". In general this value tends # to appear in several different variables -- ABI, or DEFAULT_ABI, # or perhaps MULTILIB_BUILD_ABI, depending on your intent, are the # correct ones from which to derive this suffix. The expectation, # in general, is that the EPYTHON variable will be the name of a # python executable. # # To translate any PYTHON_COMPAT or PYTHON_COMPAT_MULTILIB impl into # an EPYTHON format, one can simply call $(epythonize ${impl}). # epythonize will also sanity check an epython impl and, in # some cases, return a canonicalized equivalent (see epythonize # below). case "${EAPI:-0}" in 5) ;; *) die "Unsupported EAPI=${EAPI} for ${ECLASS}" ;; esac [[ ${_PYTHON_ECLASS_INHERITED} ]] && \ die 'python-multilib-r1 suite eclasses cannot be used with python.eclass.' [[ ${_PYTHON_UTILS_R1} ]] && \ die 'python-multilib-r1 suite eclasses cannot be used with python-r1 suite eclasses.' if [[ ! ${_PYTHON_MULTILIB_UTILS_R1} ]]; then inherit my-god-its-full-of-quotation-marks multilib-build # @ECLASS-VARIABLE: _PYTHON_NOMULTILIB_IMPLS_MATRIX # @INTERNAL # @DESCRIPTION: # no-multilib python impls mapped to their correspoinding # EPYTHON values _PYTHON_NOMULTILIB_IMPLS_MATRIX=( jython2_5:jython2.5 jython2_7:jython2.7 pypy2_0:pypy-c2.0 python3_2:python3.2 python3_3:python3.3 python2_6:python2.6 python2_7:python2.7 ) # @ECLASS-VARIABLE: _PYTHON_NOMULTILIB_HISTORICAL_IMPLS # @INTERNAL # @DESCRIPTION: # This variable contains previously supported python impls # in the same format as _PYTHON_NOMULTILIB_ALL_IMPLS. It is # fine to put "future" impls here as well, if, for example, # they could have been pulled in via an overlay and then # removed or eclass-overridden. _PYTHON_NOMULTILIB_HISTORICAL_IMPLS=( pypy1_8 pypy1_9 python2_5 python3_1 ) # @ECLASS-VARIABLE: _PYTHON_NOMULTILIB_ALL_IMPLS # @INTERNAL # @DESCRIPTION: # no-multilib python impls. This is to be kept identical # to _PYTHON_ALL_IMPLS from python-utils-r1.eclass. # @ECLASS-VARIABLE: _PYTHON_NOMULTILIB_ALL_EPYTHONS # @INTERNAL # @DESCRIPTION: # all valid no-multilib values of EPYTHON # @ECLASS-VARIABLE: _CACHED_MULTILIB_FLAGS # @INTERNAL # @DESCRIPTION: # A list of the supported mutilib-build USE flags, # with their "abi_" prefix stripped off. Under regular # circumstances, this will be some permutation of the output of: # # @CODE@ # for f in $(portageq portdir)/profiles/desc/abi_*.desc ; do x=${f##*/abi_}; x=${x%.desc}; \ # cat $f | egrep -v '^#|^[[:space:]]*$' | sed "s|^|${x}_|;s| .*$||" ; done # @CODE@ # # However, don't rely on this hack: it presumes that the abi_foo.desc files are all # kept in sync with what's in multlib-build. # # As of this writing, it was (in no particular order): # # @CODE@ # (mips_n32 mips_n64 mips_o32 x86_32 x86_64 x86_x32) # @CODE@ # # During ebuild phases other than depend, the native abi is placed # last in this list; this may be exploited to create native-abi-last # lists where convenient. During the depend phase, the ordering # is arbitrary, so this must not be exploited for cached metadata # generation. # @ECLASS-VARIABLE: _CACHED_PROFILE_FLAG_ABI_MAPPINGS # @INTERNAL # @DESCRIPTION: # A list of mappings, based on the active multilib profie, # from values like those in _CACHED_MULTILIB_FLAGS to the # corresponding active ABI values in portage. Unlike other # _CACHED_* values in this ECLASS, this one is dynamically # generated according to active portage profile settings, and # therefore not suitable for generating metadata subject to # caching by the package manager. # @ECLASS-VARIABLE: _PYTHON_MULTILIB_ALL_IMPLS # @INTERNAL # @DESCRIPTION: # This constant value is generated at runtime to include multilib # python implentation specifiers for all multilib-build-supported # multilib ABI's, including ABIs which are not active in portage's # configuration. It does not include values corresponding to ABIs # with no multilib-build support. For example, it will include # python2_7_amd64, but not python2_7_ppc64 because ppc64 is not a # multilib-build-managed abi. # # _PYTHON_MULTILIB_ALL_IMPLS is generated automatically from # _PYTHON_NOMULTILIB_ALL_IMPLS and _CACHED_MULTILIB_FLAGS. # The result is, rougly speaking, the Cartesian product of the two # arrays, expressed as an arbitrarily ordered array of # _ tuples. This value, although # generated, is invariant, and suitable for generating cachable # metadata. # @FUNCTION: _generate-python-multilib-utils-r1_globals # @INTERNAL # @DESCRIPTION: # Generates _PYTHON_MULTILIB_ALL_IMPLS and _CACHED_MULTILIB_FLAGS. # Called automatically upon inheriting python-multilib-utils-r1. # # Also ensures that no ambiguous combinations exist. For example, # if we had no-multilib python impls 'foo' and 'foo_bar', and # multilib abi useflags 'food' and 'bar_food', the multilib impl # foo_bar_food would be ambiguous because one could never tell # whether it pertained to the 'foo' impl combined with the # 'bar_food' multilib flag, or the 'foo_bar' impl combined with # the 'food' multilib flag. # # If any such ambiguity exists, this function dies: therefore, # the python-multilib-utils-r1 eclass will die immediately upon # being inherited (neither passing "go," nor collecting $200). _generate-python-multilib-utils-r1_globals() { debug-print-function ${FUNCNAME} "$@" _PYTHON_NOMULTILIB_ALL_IMPLS=( "${_PYTHON_NOMULTILIB_IMPLS_MATRIX[@]%%:*}" ) _PYTHON_NOMULTILIB_ALL_EPYTHONS=( "${_PYTHON_NOMULTILIB_IMPLS_MATRIX[@]#*:}" ) _CACHED_MULTILIB_FLAGS=( "${_MULTILIB_FLAGS[@]%:*}" ) if [[ ${EBUILD_PHASE} != depend ]] ; then # put the most preferred _CACHED_MULTILIB_FLAG last in the list # if the native abi isn't represented there, we don't care, as # we are not affected by it. _CACHED_MULTILIB_FLAGS=( $( found= # we dont have the mappings cached yet -- so use multilib_flag_abi # note that we havent yet stripped off the abi_ part. bestabiflag="$(multilib_abi_flag "$(multilib_get_native_abi)")" for flag in "${_CACHED_MULTILIB_FLAGS[@]}" ; do if [[ ${flag} == ${bestabiflag} ]]; then found=1 else echo "${flag}" fi done [[ ${found} ]] && echo "${bestabiflag}" ) ) fi _CACHED_MULTILIB_FLAGS=( "${_CACHED_MULTILIB_FLAGS[@]##abi_}" ) _CACHED_PROFILE_FLAG_ABI_MAPPINGS=() if [[ ${EBUILD_PHASE} != depend ]] ; then local flag flagabi for flag in "${_CACHED_MULTILIB_FLAGS[@]}" ; do flagabi=$(multilib_flag_abi "abi_${flag}") [[ ${flagabi} ]] && \ _CACHED_PROFILE_FLAG_ABI_MAPPINGS+=("${flag} ${flagabi}") done fi _PYTHON_MULTILIB_ALL_IMPLS=() local impl flag multi_impl for impl in "${_PYTHON_NOMULTILIB_ALL_IMPLS[@]}" ; do for flag in "${_CACHED_MULTILIB_FLAGS[@]}" ; do multi_impl="${impl}_${flag}" if has "${multi_impl}" "${_PYTHON_MULTILIB_ALL_IMPLS[@]}" \ "${_PYTHON_NOMULTILIB_ALL_IMPLS[@]}" "${_PYTHON_NOMULTILIB_HISTORICAL_IMPLS[@]}" ; then eerror eerror "${FUNCNAME}: Bad news. A python-abi_multilib-abi concatenation," eerror "\"${multi_impl},\" is ambiguous. The list of known multilib python abis is:" eerror eerror " ( ${_PYTHON_NOMULTILIB_ALL_IMPLS[*]} ${_PYTHON_NOMULTILIB_HISTORICAL_IMPLS[@]} )," eerror eerror "and the list of multilib abis is:" eerror eerror " ( ${_CACHED_MULTILIB_FLAGS[*]} )." eerror eerror "There is no way for the framework to proceed, sorry!" eerror die "Ambiguous python-multilib-abi \"${multi_impl}\" is multiply constructed" fi _PYTHON_MULTILIB_ALL_IMPLS+=("${multi_impl}") done done debug-print "${FUNCNAME}: no-multilib impls: $(mg-qm "${_PYTHON_NOMULTILIB_ALL_IMPLS[@]}")" debug-print "${FUNCNAME}: no-multilib epythons: $(mg-qm "${_PYTHON_NOMULTILIB_ALL_EPYTHONS[@]}")" debug-print "${FUNCNAME}: cached multilib flags: $(mg-qm "${_CACHED_MULTILIB_FLAGS[@]}")" debug-print "${FUNCNAME}: cached flag:abi mappings: $(mg-qm "${_CACHED_PROFILE_FLAG_ABI_MAPPINGS[@]}")" debug-print "${FUNCNAME}: generated multilib impls: $(mg-qm "${_PYTHON_MULTILIB_ALL_IMPLS[@]}")" } _generate-python-multilib-utils-r1_globals # @FUNCTION: python_get_multlib_flag_abi # @USAGE: # @DESCRIPTION: # Exactly equivalent to multilib_flag_abi in multilib-build, but # hopefully, much faster, due to caching. # # The only difference is that, whereas multilib_build might map # "abi_foo_bar" to, let's say, the "foobar" ABI, this API omits # the "abi_" prefix, i.e., mapping just "foo_bar" to "foobar". # # It either returns 0 and outputs an enabled ABI corresponding to the # provided use-flag, or returns 1 and outputs nothing, when no match # can be identified amongst the active multilib profile ABI's. # # During the depend phase the output is always empty and the # return code always 1. python_get_multilib_flag_abi() { if [[ ${EBUILD_PHASE} != depend ]] ; then debug-print-function ${FUNCNAME} "${@}" local map for map in "${_CACHED_PROFILE_FLAG_ABI_MAPPINGS[@]}"; do if [[ ${map% *} == ${1} ]] ; then echo "${map#* }" return 0 fi done debug-print "${FUNCNAME}: no match found for ${1}" fi return 1 } # @FUNCTION: _python_strip_multilib_impl_abi # @USAGE: [] # @INTERNAL # @DESCRIPTION: # Given a valid python impl, and an optional list of no-multilib impls to treat # as valid (otherwise _PYTHON_MULTILIB_ALL_IMPLS will be used), strip off the # multilib-abi suffix, if any, of the multilib_impl and return only the no-multilib # portion, as would be found in _PYTHON_NOMULTILIB_ALL_IMPLS. Results go to standard # output. For example: # # @CODE@ # x=$(_python_strip_multilib_impl_abi "pypy2_0_mips_n64") # @CODE@ # # would set x to "pypy2_0" assuming this is a valid no-multilib python implementation. # If a no-multilib impl is provided to this function it is returned unmangled. # # This is a purely functional API acting on deterministically generated data; # in other words, it is suitable for generating cachable package-manager metadata. _python_strip_multilib_impl_abi() { debug-print-function ${FUNCNAME} "$@" local impl="$1" shift local no_multilib_impls rslt flag if [[ ${#} -gt 0 ]] ; then no_multilib_impls=" $* " else no_multilib_impls=" ${_PYTHON_NOMULTILIB_ALL_IMPLS[*]} " fi # if the abi is a bare no-multilib impl then return it unmangled if [[ "${no_multilib_impls}" == *" ${impl} "* ]] ; then echo "${impl}" return 0 fi for flag in "${_CACHED_MULTILIB_FLAGS[@]}" ; do if [[ ${impl} == *_${flag} ]] ; then rslt=${impl%_${flag}} # false alarm? [[ "${no_multilib_impls}" == *" ${rslt} "* ]] || continue echo "${rslt}" debug-print "${FUNCNAME}: result (stripped _${flag}): \"${rslt}\")" return 0 fi done die "Could not find python-abi or python-abi_multilib-abi combination matching \"${impl}\"" } # @FUNCTION: _python_strip_multilib_epython_abi # @USAGE: # @INTERNAL # @DESCRIPTION: # Given a valid python EPYTHON value, strip off the multilib-abi suffix, if any, # and return only the no-multilib portion. Only multilib-build abis, or the # default ABI, will be stripped. Since this is profile-dependant, results are # not suitable for package-manager cached-metadata generation. _python_strip_multilib_epython_abi() { debug-print-function ${FUNCNAME} "$@" [[ $# -eq 1 ]] || die "${FUNCNAME}: wrong argument count: $#" local epimpl="$1" abi rslt # if epimpl is a bare no-multilib EPYTHON value then return it unmangled if [[ " ${_PYTHON_NOMULTILIB_ALL_EPYTHONS[*]} " == *" ${epimpl} "* ]] ; then echo "${epimpl}" debug-print "${FUNCNAME}: result (already stripped): \"${epimpl}\"" return 0 fi for abi in $(get_multilib_build_abis $( [[ ${EBUILD_PHASE} == depend ]] && echo --dependmode ) ) ; do if [[ ${abi} && ${epimpl} == *-${abi} ]] ; then rslt=${epimpl%-${abi}} [[ " ${_PYTHON_NOMULTILIB_ALL_EPYTHONS[*]} " == *" ${rslt} "* ]] || continue echo "${rslt}" debug-print "${FUNCNAME}: result (stripped -${abi}): \"${rslt}\"" return 0 fi done die "Could not find EPYTHON-format python-abi-multilib-abi combination matching \"${epimpl}\"" } # @FUNCTION: _python_multilib_impl_supported # @USAGE: # @INTERNAL # @DESCRIPTION: # Check whether the implementation (PYTHON_COMPAT_MULTILIB form) # is supported. # # Returns 0 if the implementation is a valid and supported multilib python # impl in PYTHON_COMPAT_MULTILIB format (see the lengthy description of the # various types of impls at the top of this document). If it is a no-longer-supported # multilib impl (corresponding to a value from _PYTHON_NOMULTILIB_HISTORICAL_IMPLS), # returns 1. Otherwise, dies. Notably, will die if the provided impl is a # PYTHON_COMPAT-format nomultilib impl such as "python3_2". # # Note that, unlike many functions in this eclass, here "supported" means # "supported by the python-multilib-r1 framework in general", and not, # "supported by the particular portage environment we are in now". To answer # the latter question it is perhaps best to use epythonize. _python_multilib_impl_supported() { debug-print-function ${FUNCNAME} "${@}" [[ ${#} -eq 1 ]] || die "${FUNCNAME}: takes exactly 1 argument (impl)." # we need to provide the historical impls as well as the valid ones or else # _python_strip_multilib_impl_abi will reject its input and die local impl=$(_python_strip_multilib_impl_abi "${1}" "${_PYTHON_NOMULTILIB_ALL_IMPLS[@]}" \ "${_PYTHON_NOMULTILIB_HISTORICAL_IMPLS[@]}") [[ ${impl} == ${1} ]] && \ die "impl \"${1}\" does not end in an abi prefix. use _python_nomultilib_impl_supported for such queries." # _python_strip_multilib_impl_abi died if an invalid value was provided, so if its not valid, it's historical [[ " ${_PYTHON_NOMULTILIB_ALL_IMPLS[*]} " == *" ${impl} "* ]] } # @FUNCTION: _python_nomultilib_impl_supported # @USAGE: # @INTERNAL # @DESCRIPTION: # Check whether the implementation (PYTHON_COMPAT form) # is still supported. # # Returns 0 if the implementation is valid and supported PYTHON_COMPAT-format # nomultilib impl (see the lengthy description of the various types of impls # at the top of this document). If it is a no-longer-supported impl (precisely, # those appearing in _PYTHON_NOMULTILIB_HISTORICAL_IMPLS), returns 1. Otherwise, # dies. Notably, will die if the provided impl is a PYTHON_COMPAT_MULTILIB-format # multilib impl such as "python3_2_x86_x32". # # Note that, unlike many functions in this eclass, here "supported" means # "supported by the python-multilib-r1 framework in general", and not, # "supported by the particular portage environment we are in now". To answer # the latter question it is perhaps best to use epythonize. _python_nomultilib_impl_supported() { debug-print-function ${FUNCNAME} "${@}" [[ ${#} -eq 1 ]] || die "${FUNCNAME}: takes exactly 1 argument (impl)." [[ " ${_PYTHON_NOMULTILIB_ALL_IMPLS[*]} " == *" ${1} "* ]] && return 0 [[ " ${_PYTHON_NOMULTILIB_HISTORICAL_IMPLS[*]} " == *" ${1} "* ]] && return 1 # we have some kind of garbage input. now it's just a matter of die-ing usefully. # calling strip here will die if "$1" doesn't match any past/present impl _python_strip_multilib_impl_abi "${1}" "${_PYTHON_NOMULTILIB_ALL_IMPLS[@]}" \ "${_PYTHON_NOMULTILIB_HISTORICAL_IMPLS[@]}" >/dev/null # confusing, but if we made it here, $1 is a valid or historical /multilib/ impl. # valid/historical no-multilib impls were ruled out at the top of this function, # and any invalid impls died off when we invoked _python_strip_multilib_impl_abi die "impl \"${1}\" is a multilib impl, but should be a no-multilib impl like python2_7" } _python_impl_supported() { die "${FUNCNAME}: Wrong API for this framework. Use _python_multilib_impl_supported or _python_nomultilib_impl_supported." } # @ECLASS-VARIABLE: PYTHON # @DEFAULT_UNSET # @DESCRIPTION: # The absolute path to the current Python interpreter. # # This variable is set automatically in the following contexts: # # python-multilib-r1(?)/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(). # # python-single-multilib-r1: Set in functions called by python_foreach_impl() or after # calling python_export_best(). # # distutils-multilib-r1/distutils-r1: Set within any of the python sub-phase functions. # # Example values: # @CODE # /usr/bin/python2.7-x32 # /usr/bin/pypy-c2.0 # /usr/bin/python3.3 # @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-multilib-r1_pkg_setup(). # # python-mutilib-single-r1: Set in functions called by python_foreach_impl() # or after calling python_export_best(). # # (todo) distutils-multilib-r1: Set within any of the python sub-phase functions. # # The variable may take on either a no-multilib form, such as "python3.3", or # a multilib form, such as "python3.3-x32". The multilib form has an ABI value # appended to it; this is still the name of an executable. # # One exception is that, for the default ABI, only the no-multilib EPYTHON # format is fully "correct." # # When such a value is provided to the framework, for example, if one were to run # python_export python2.7-amd64 EPYTHON on an ~amd64 gentoo system, the # "-${DEFAULT-ABI}" portion is automatically stripped; so subsequent to the # above python_export invocation, the EPYTHON variable will have become the # no-multilib equivalent: python2.7. # # Example value: # @CODE # python2.7-x32 # @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/i686-pc-linux-gnu/python2.7 -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_MULTILIB_PKG_DEP # @DEFAULT_UNSET # @DESCRIPTION: # The complete dependency on a particular Python package as a string, # including multilib requirents. It is probably always preferable to # use this in lieu of PYTHON_PKG_DEP, however, the old variable # is provided in case some valid use for it exists we haven't thought of. # # Set and exported on request using python_export() # @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: epythonize # @USAGE: [] # @DESCRIPTION: # This function attempts to find a python implementation corresponding # to the provided impl. If no implementation is found, the function # will silently return a nonzero failure code; otherwise, the # function will return zero and echo a corresponding EPYTHON-format # impl value to standard output. The returned specifier is also # typically "canonicalized" as appropriate (see below). # # Because it fails silently, consumers placing this in a subshell will # need to do their own error handling, by checking whether an empty value # was returned. # # Either a PYTHON_COMPAT, PYTHON_COMPAT_MULTILIB, or multilib or # no-multilib EPYTHON-format implementation selector may be passed as the # argument. # # no-multilib EPYTHON-format implementation specifiers are returned # unchanged as they are already considered to canonically represent both # the python-r1-framework-style no-multilib EPYTHON, and, (except during # the depend phase), the canonical name of the # python-multilib-r1-framework multilib EPYTHON implementation specifier # for the best multilib ABI (in the sense that they they represent the # same thing, which happens, not coincidentally, to also name the # corresponding python executable. # # For the same reason, if a multilib EPYTHON specifier is provided with # the best-ABI suffix, the same no-multilib EPYTHON specifier is # returned, except during the depend phase, in which it would be returned # unmodified, so as to preserve invaraince. # # If a no-multilib PYTHON_COMPAT-format specifier is provided, the # corresponding no-multilib EPYTHON specifier is returned. # # Finally, if a PYTHON_MULTILIB_COMPAT-format specifier is provided, such # as python2_7_mips_n64, the corresponding canonicalized EPYTHON # implementation specifier, i.e.: python2.7-n64, is returned (for the # best ABI this is the no-multilib specifier, i.e.: python2.7), except # during the depend phase, when this would create a profile-dependant # result, as this mapping neccesarily depends on the enabled multilib # ABIs in portage due to the fact that use-flags in multilib-build are # correlated to multiple ABIs. # # During the depend phase, epythonize instead translates a # PYTHON_MULTILIB_COMPAT-format EPYTHON specifier into its corresponding # no-multilib specifier in all cases. This unfortunate corner-case is # required to retrieve the PYTHON_MULTILIB_PKG_DEP variable from # python_export. Although a bit of a kludge, this should be reasonably # safe, since only that variable may be retrieved from python_export # during the depend phase, and its value is identical for all ABIs. epythonize() { debug-print-function ${FUNCNAME} "${@}" local impl=${EPYTHON} local bestabi declare -a all_abis if [[ ${EBUILD_PHASE} == depend ]]; then bestabi="default" all_abis=( $(get_multilib_build_abis --dependmode) ) else bestabi=$(multilib_get_native_abi) all_abis=( $(multilib_get_enabled_abis) ) fi local implmap implmap_impl implmap_epython scratch_abi [[ $# -gt 1 ]] && die "${FUNCNAME}: $(mg-qm "$@"): too many arguments" # the one thing we won't permit is an empty string argument, which # most likely indicates that our caller is confused. [[ $# -eq 1 && ${1} == "" ]] && die "${FUNCNAME}: $(mg-qm "$@"): empty argument bug" # if an argument is provided, [[ $# -eq 1 ]] && impl=${1} # iterate the python impls matrix and try to find a matching prefix for implmap in "${_PYTHON_NOMULTILIB_IMPLS_MATRIX[@]}" ; do implmap_impl=${implmap%%:*} implmap_epython=${implmap#*:} case ${impl} in ${implmap_epython}) echo "${impl}" debug-print "${FUNCNAME}: result (nomultilib epython): ${impl}" return 0 ;; ${implmap_impl}) echo "${implmap_epython}" debug-print "${FUNCNAME}: result (nomultilib impl): ${implmap_epython}" return 0 ;; ${implmap_epython}-*) scratch_abi=${impl#${implmap_epython}-} debug-print "${FUNCNAME}: scratch_abi=\"${scratch_abi}\" (_epython)..." ;; ${implmap_impl}_*) if [[ ${EBUILD_PHASE} == depend ]] ; then # there's no way to do it, but unfortunately this winds up being # needed to retrieve PYTHON_MULTILIB_PKG_DEP. We have to fudge it: echo "${implmap_epython}" debug-print "${FUNCNAME}: fudging it: \"${implmap_epython}\"" return 0 else scratch_abi=$(python_get_multilib_flag_abi "${impl#${implmap_impl}_}") debug-print "${FUNCNAME}: scratch_abi=\"${scratch_abi}\" (_impl)..." fi # if no flag was found, maybe bad ABI, or maybe a false positive. keep searching. [[ ${scratch_abi} ]] || continue ;; *) # if no match, try another mapping. continue ;; esac # a partial match: we suspect ${scratch_abi} of being a usable multilib abi if [[ ${scratch_abi} == ${bestabi} ]] ; then echo "${implmap_epython}" debug-print "${FUNCNAME}: result (native abi): ${implmap_epython}" return 0 elif [[ " ${all_abis[*]} " == *" ${scratch_abi} "* ]] ; then # scratch_abi is a valid non-best-multilib-abi; so just prepend nomultilib EPYTHON echo "${implmap_epython}-${scratch_abi}" debug-print "${FUNCNAME}: result (multilib): ${implmap_epython}-${scratch_abi}" return 0 else debug-print "${FUNCNAME}: scratch fail: bestabi=\"${bestabi}\" all_abis[*]=\"${all_abis[*]}\" scratch_abi=\"${scratch_abi}\"" fi # no match, or inactive/bogus ABI: keep searching done # no dice debug-print "${FUNCNAME}: no match found for "${impl}", returning nothing" return 1 } # @FUNCTION: _python_do_with_multilib_abi # USAGE: [--abionly] [...] # @INTERNAL # @DESCRIPTION: # Agressive wrapper to run commands under ABI auspices without side-effects. # # multilib_toolchain_setup leaves all it's mess lying around in # the environment afterwards, by design, and is therefore not trivially # nestable, due to loss of nested "backup" information (it's designed # around the assumption that the best ABI is the native ABI and that # this ABI will always be activated last, when iterating; this # may not hold here). # # Here we may or may not want the best ABI, and we may or may not # already be iterating throught the ABIs under the auspices of # multilib_foreach_abi. We would need to call multilib_toolchain_setup # twice, once to set the temporary ABI up, and once, after, to restore the # old ABI... but, which one was that, again, that we wanted to restore, and # where have those variables gone? # # This kludge avoids all such questions with an aggressive hack. # # If the --abionly argument is provided as the first argument then only the # ABI variable is exported and localized and multilib_toolchain_setup is # not invoked before the provided command is executed. In that case, the # job becomes a trivial and significantly faster. # # TODO: fix python_toolchain_setup, somehow, so that the worst of this # nonsense is no longer required -- it should suffice to make the variable saving optional # and provide an interface to retrieve the list of variables affected. _python_do_with_multilib_abi() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" [[ $# -lt 2 ]] && die "${FUNCNAME}: requires more arguments" if [[ ${1} == --abionly ]] ; then shift local ABI=${1} shift export ABI else local cross_abi=${1} shift if [[ ${EBUILD_PHASE} == depend || ${ABI:-${DEFAULT_ABI:-default}} != ${cross_abi} ]] ; then local v # note: this list must be kept synchronized with that in multilib.eclass:multilib_toolchain_setup for v in {__abi_saved_,}{CHOST,CBUILD,AS,CC,CXX,LD,PKG_CONFIG{_LIBDIR,_PATH,}} __DEFAULT_ABI_SAVED ABI; do if [[ ${v+set} == set ]] ; then local ${v}="${!v}" else local ${v} unset ${v} fi done unset v multilib_toolchain_setup "${cross_abi}" fi fi "$@" } # @FUNCTION: _python_impl_abi # USAGE: # @INTERNAL # @DESCRIPTION: # Returns (to stdout) the multilib ABI corresponding to the provided impl. # if the impl is not valid, dies. _python_impl_abi() { debug-print-function ${FUNCNAME} "${@}" [[ $# -ne 1 ]] && die "${FUNCNAME} requires one argument (impl)" local impl=$(epythonize ${1}) [[ ${impl} ]] || die "${FUNCNAME}: ${1} is not a recognizable impl" local simpl=$(_python_strip_multilib_epython_abi "${impl}") if [[ ${impl} == ${simpl} ]] ; then echo "$(multilib_get_native_abi)" else echo "${impl#${simpl}-}" fi } # @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_MULTILIB value, e.g. # python2_7_x86, a PYTHON_TARGETS value, e.g. python2_7, or an # EPYTHON one, e.g. python2.7-x86, or, for the native abi, e.g. # python2.7 or python2.7-${DEFAULT_ABI} would be equivalent). # # If no implementation is passed, the current one will be obtained from # ${EPYTHON}. This will be used even if ${EPYTHON} is not valid, so # watch out for gigo problems! If in doubt, run it through epythonize. # # The variables which can be exported are: PYTHON, EPYTHON, # PYTHON_SITEDIR, PYTHON_INCLUDEDIR, PYTHON_LIBPATH, PYTHON_CFLAGS, # PYTHON_LIBS, PYTHON_MULTILIB_PKG_DEP, PYTHON_SCRIPTDIR. # They are described more completely in the eclass # variable documentation. During the depend phase, only the # PYTHON_MULTILIB_PKG_DEP variable may be exported. python_export() { debug-print-function ${FUNCNAME} "${@}" local impl var bestabi epython_provided cross_abi if [[ ${EBUILD_PHASE} == depend ]]; then bestabi=default else bestabi=$(multilib_get_native_abi) fi impl=$(epythonize ${1}) debug-print "${FUNCNAME}: epythonized first argument (\"$1\") is \"${impl}\"." [[ ${impl} ]] && shift # here we remember whether or not epython was provided as a command-line # argument. When EPYTHON flows in from the environment, we want to leave # it unchanged (except to export it if requested). epython_provided=${impl:+1} debug-print "${FUNCNAME}: epython_provided is \"${epython_provided}\"." : ${impl:=${EPYTHON}} [[ ${impl} ]] || \ die "${FUNCNAME} $(mg-qm "$@"): No valid command-line impl, no ${EPYTHON}. No clue what to do" debug-print "${FUNCNAME}: implementation: ${impl}" local simpl=$(_python_strip_multilib_epython_abi "${impl}") debug-print "${FUNCNAME}: multilib-stripped implementation: \"${simpl}\"." # we could use _python_impl_abi here but that involes some repeat work... if [[ ${simpl} == ${impl} ]] ; then cross_abi=${bestabi} else cross_abi=${impl#${simpl}-} fi debug-print "${FUNCNAME}: cross_abi is \"${cross_abi}\"" # blech... _python_export_crossabi_pkgconfig_sub() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" ${PKG_CONFIG:-$(tc-getPKG_CONFIG)} $@ } _python_export_crossabi_pkgconfig() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" _python_do_with_multilib_abi ${cross_abi} \ _python_export_crossabi_pkgconfig_sub "$@" } for var; do case "${var}" in EPYTHON) [[ ${EBUILD_PHASE} == depend ]] && die "cant export EPYTHON during depend" # if the impl is the *-${bestabi} abi, strip the -${bestabi}, # unless EPYTHON flowed in from the environment, in which case, # we leave it intact. [[ ${epython_provided} && ${impl} == *-${bestabi} ]] && impl="${impl%-${bestabi}}" export EPYTHON=${impl} debug-print "${FUNCNAME}: EPYTHON = ${EPYTHON}" ;; PYTHON) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON during depend" # if the impl is the *-${bestabi} abi, strip the -${bestabi}. [[ ${impl} == *-${bestabi} ]] && impl="${impl%-${bestabi}}" export PYTHON=${EPREFIX}/usr/bin/${impl} debug-print "${FUNCNAME}: PYTHON = ${PYTHON}" ;; PYTHON_SITEDIR) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_SITEDIR during depend" local dir case "${impl}" in python*) dir="/usr/$(_python_do_with_multilib_abi --abionly ${cross_abi} get_libdir)/${simpl}" ;; jython*) dir="/usr/share/${simpl/n/n-}/Lib" ;; pypy*) dir="/usr/$(_python_do_with_multilib_abi --abionly ${cross_abi} get_libdir)/${simpl/-c/}" ;; esac export PYTHON_SITEDIR="${EPREFIX}${dir}/site-packages" debug-print "${FUNCNAME}: PYTHON_SITEDIR = \"${PYTHON_SITEDIR}\"." ;; PYTHON_INCLUDEDIR) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_INCLUDEDIR during depend" local dir case "${impl}" in python*) dir=/usr/include/${simpl} ;; pypy*) dir=/usr/$(_python_do_with_multilib_abi --abionly ${cross_abi} get_libdir)/${simpl/-c/}/include ;; *) die "${impl} lacks header files" ;; esac export PYTHON_INCLUDEDIR=${EPREFIX}${dir} debug-print "${FUNCNAME}: PYTHON_INCLUDEDIR = ${PYTHON_INCLUDEDIR}" ;; PYTHON_LIBPATH) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_LIBPATH during depend" local libname case "${impl}" in python*) libname=lib${simpl} ;; *) die "${impl} lacks a dynamic library" ;; esac local path=${EPREFIX}/usr/$(_python_do_with_multilib_abi --abionly ${cross_abi} get_libdir) export PYTHON_LIBPATH=${path}/${libname}$(get_libname) debug-print "${FUNCNAME}: PYTHON_LIBPATH = ${PYTHON_LIBPATH}" ;; PYTHON_CFLAGS) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_CFLAGS during depend" local val case "${simpl}" in python2.5|python2.6) # old versions support python-config only val=$("${impl}-config" --includes) ;; python*) # python-2.7, python-3.2, etc. val=$(_python_export_crossabi_pkgconfig --cflags ${simpl/n/n-}) ;; *) die "${impl}: obtaining ${var} not supported" ;; esac export PYTHON_CFLAGS=${val} debug-print "${FUNCNAME}: PYTHON_CFLAGS = ${PYTHON_CFLAGS}" ;; PYTHON_LIBS) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_LIBS during depend" local val case "${simpl}" in python2.5|python2.6) # old versions support python-config only val=$("${impl}-config" --libs) ;; python*) # python-2.7, python-3.2, etc. val=$(_python_export_crossabi_pkgconfig --libs ${simpl/n/n-}) ;; *) die "${impl}: obtaining ${var} not supported" ;; esac export PYTHON_LIBS=${val} debug-print "${FUNCNAME}: PYTHON_LIBS = ${PYTHON_LIBS}" ;; PYTHON_MULTILIB_PKG_DEP) case ${simpl} in python2.6) PYTHON_MULTILIB_PKG_DEP='>=dev-lang/python-2.6.8-r3:2.6';; python2.7) PYTHON_MULTILIB_PKG_DEP='>=dev-lang/python-2.7.6-r1:2.7';; python3.2) PYTHON_MULTILIB_PKG_DEP='>=dev-lang/python-3.2.5-r2:3.2';; python3.3) PYTHON_MULTILIB_PKG_DEP='>=dev-lang/python-3.3.2-r2:3.3';; pypy-c2.0) PYTHON_MULTILIB_PKG_DEP='>=virtual/pypy-2.0.2:2.0';; jython2.5) PYTHON_MULTILIB_PKG_DEP='>=dev-java/jython-2.5.3-r2:2.5';; jython2.7) PYTHON_MULTILIB_PKG_DEP='dev-java/jython:2.7';; *) die "Invalid implementation: ${impl}" esac # use-dep if [[ ${PYTHON_REQ_USE} ]]; then PYTHON_MULTILIB_PKG_DEP+="[${PYTHON_REQ_USE},${MULTILIB_USEDEP}]" else PYTHON_MULTILIB_PKG_DEP+="[${MULTILIB_USEDEP}]" fi export PYTHON_MULTILIB_PKG_DEP debug-print "${FUNCNAME}: PYTHON_MULTILIB_PKG_DEP = ${PYTHON_MULTILIB_PKG_DEP}" ;; PYTHON_SCRIPTDIR) [[ ${EBUILD_PHASE} == depend ]] && die "cant export PYTHON_SCRIPTDIR during depend" local dir export PYTHON_SCRIPTDIR=${EPREFIX}/usr/lib/python-exec/${simpl} debug-print "${FUNCNAME}: PYTHON_SCRIPTDIR = ${PYTHON_SCRIPTDIR}" ;; *) die "python_export: unknown variable ${var}" esac done } # @FUNCTION: do_with_python_exports # @USAGE: [] [...] # @DESCRIPTION: # A wrapper function for python exports. Sets and exports the # provided variables names (or PYTHON and EPYTHON if no variables # are named) using python_export, and then invokes the provided # action with those variables set. All named variables (or PYTHON # and EPYTHON, if no variables are named) are put back to their # original states once the function terminates. If the optional # argument is provided it will be passed along to python_export # as well. Otherwise, the default behavior of taking EPYTHON from # the environment will be used. do_with_python_exports() { debug-print-function ${FUNCNAME} "$(mg-qm $@)" [[ $# -eq 0 ]] && die "${FUNCNAME}: requires at least one argument" declare -a variables_to_export=() local impl=$(epythonize "$1") # shift first arg away if it was an impl [[ ${impl} ]] && shift : ${impl:=${EPYTHON}} [[ ${impl} ]] || \ die "${FUNCNAME} (args: $(mg-qm "$@")): cannot find EPYTHON to work with" debug-print "${FUNCNAME}: implementation selected: \"${impl}\"" local isvar while [[ $# -gt 0 ]] ; do isvar= case ${1} in EPYTHON|PYTHON|PYTHON_SITEDIR|PYTHON_INCLUDEDIR|PYTHON_LIBPATH|PYTHON_CFLAGS) isvar=yes ;; PYTHON_LIBS|PYTHON_PKG_DEP|PYTHON_MULTILIB_PKG_DEP|PYTHON_SCRIPTDIR) isvar=yes ;; esac if [[ ${isvar} ]] ; then variables_to_export+=("${1}") shift continue else # from here on out we'll take them all to be command/arguments break fi done [[ $# -eq 0 ]] && die "${FUNCNAME}: no command provided" # if no variables were requested use the default: [[ ${#variables_to_export[@]} -eq 0 ]] && \ variables_to_export=( EPYTHON PYTHON ) debug-print "${FUNCNAME}: variables to export: $(mg-qm "${variables_to_export[@]}")" # save all the variables by localizing them (abuses isvar variable) for isvar in "${variables_to_export[@]}" ; do if [[ ${isvar+set} == set ]] ; then local ${isvar}="${!isvar}" else local ${isvar} unset ${isvar} fi done # already epythonized if neccesary so don't induce repeat-work by passing ${impl} EPYTHON="${impl}" python_export "${variables_to_export[@]}" # clean up our locals to give the callee a 99.5% pristine environment unset isvar unset impl unset variables_to_export "$@" } # @FUNCTION: python_get_epython # @USAGE: [] # @DESCRIPTION: # Obtain and print the EPYTHON value for the given implementation. # If no implementation is provided, ${EPYTHON} will be used. python_get_epython() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" EPYTHON echo "${EPYTHON}" } # @FUNCTION: python_get_python # @USAGE: [] # @DESCRIPTION: # Obtain and print the python binary path for the given # implementation. If no implementation is provided, ${EPYTHON} will # be used. python_get_python() { debug-print-function ${FUNCNAME} "${@}" python_export "${@}" PYTHON echo "${PYTHON}" } # @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. 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. 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. # # This function will die if a non-cpython implementation is queried, # as there is no such animal for the other implementations. 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_rewrite_shebang_multilib # @USAGE: [] [--nomultilib|-n] ... # @INTERNAL # @DESCRIPTION: # Replaces 'python' executable in the shebang with the executable name # of the specified interpreter(s). If no EPYTHON value (implementation) is # used, the current ${EPYTHON} will be used. # # Unless --nomultilib is specified, multilib python abis will be used # where applicable (the native mutlilib ABI is not applicable). # # All specified files must start with a 'python' shebang. A file not # having a matching shebang will be refused. The exact shebang style # will be preserved in order not to break anything. # # If --nomultilib or -n is specified (and, no, I guess this API doesn't # support files with those names), then all the files on the command-line # after that point will receive native-abi-only shebangs. So for example if # your ABI is amd64, but the multilib ABI is x32 at the moment, and # EPYTHON="pypy3.3-x86_x32", absolutely none of that will matter, you'll still # end up with shebangs ending in just "pypy3.3", just like in the old # _python_rewrite_shebang from python-utils-r1.eclass. # # Unlike _python_rewrite_shebang, if the correct shebang is already in your file, # nothing bad will happen. # # Example conversions: # @CODE # From: #!/usr/bin/python2.7 -R # To: #!/usr/bin/python2.7-x86_32 -R # # From: #!/usr/bin/env FOO=bar python # To: #!/usr/bin/env FOO=bar python2.7-x86_32 # @CODE python_rewrite_shebang_multilib() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" [[ $# -lt 1 ]] && return 0 local impl=$(epythonize ${1}) [[ ${impl} ]] && shift : ${impl:=${EPYTHON}} [[ ${impl} ]] || \ die "${FUNCNAME}: neither EPYTHON=\"${EPYTHON}\" nor \$1=\"$1\" look like a valid EPYTHON" # translate ${simpl}-${bestabi} to ${impl}, '_' to '.', etc. debug-print "${FUNCNAME}: effective implementation selected: \"${impl}\"" local simpl=$(_python_strip_multilib_epython_abi "${impl}") # we need this in nomultilib mode for shrinking multilib shebangs local origimpl=${impl} local f local nomultilib= for f; do if [[ ${f} == --nomultilib || ${f} == -n ]]; then nomultilib="yes, no" debug-print "${FUNCNAME}: nomultilib mode activated for subsequent files in queue" impl=${simpl} continue fi local from shebang read -r shebang < "${f}" shebang=${shebang%$'\r'} debug-print "${FUNCNAME}: path = ${f}" debug-print "${FUNCNAME}: shebang = ${shebang}" if [[ "${shebang} " == *'python '* ]]; then from=python elif [[ "${shebang} " == *'python2 '* ]]; then from=python2 elif [[ "${shebang} " == *'python3 '* ]]; then from=python3 elif [[ "${shebang} " == *${simpl}' '* ]] ; then # nothing to do when in nomultilib mode or ${simpl} == ${impl} if [[ ${nomultilib} || ${simpl} == ${impl} ]] ; then debug-print "${FUNCNAME}: No need to shebangify \"${shebang}\", its GTG for nomultilib" continue fi from=${simpl} elif [[ "${shebang} " == *${impl}' '* ]] ; then # nothing to do. debug-print "${FUNCNAME}: No need to shebangify \"${shebang}\", its GTG for \"${impl}\"" continue elif [[ "${shebang} " == *${origimpl}' '* ]] ; then # obviously ${origimpl} != ${impl} therefore we're in nomultilib mode, yet # this is a multilib shebang (otherwise, we'd have matched ${simpl} above. from=${origimpl} else eerror "A file does not seem to have a supported shebang:" eerror " file: ${f}" eerror " shebang: ${shebang}" echo die "${FUNCNAME}: ${f} does not seem to have a valid shebang" fi if { [[ ${from} == python2* ]] && python_is_python3 "${impl}"; } \ || { [[ ${from} == python3* ]] && ! python_is_python3 "${impl}"; } then eerror "A file does have shebang not supporting requested impl:" eerror " file: ${f}" eerror " shebang: ${shebang}" eerror " impl: ${impl}" echo die "${FUNCNAME}: ${f} does have shebang not supporting ${EPYTHON}" fi sed -i -e "1s:${from}:${impl}:" "${f}" || die done } _python_rewrite_shebang() { die "${FUNCNAME}: not supported here. Somebody forgot to use python_rewrite_shebang_multilib" } # pretty easy to make this typo since we dropped the '_'. _python_rewrite_shebang_multilib() { ewarn "${FUNCNAME}: I'm gonna let it slide, but the correct function" ewarn "name is python_multilib_rewrite_shebang with no '_' in front of it." echo python_rewrite_shebang_multilib "$@" } python_fix_shebang() { die "${FUNCNAME}: not supported here. Somebody forgot to use python_fix_shebang_multilib" } python_fix_shebang_multilib() { # FIXME: drift vs upstream equivalent python_rewrite_shebang_multilib "$@" } # @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}). # # Requires ${EPYTHON} to be set from without. 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... # 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="${instpath#${BASH_REMATCH[0]}}" 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} "$(mg-qm "${@}")" python_scriptroot=${1} } # @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, the file will be renamed to be EPYTHON-suffixed # and a wrapper will be installed in place of the original name. # # Example: # @CODE # src_install() { # python_foreach_impl python_doscript ${PN} # } # @CODE python_doscript() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" local f for f; do python_newscript "${f}" "${f##*/}" done } # @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, the file will be renamed to be EPYTHON-suffixed # and a wrapper will be installed in place of the . # # Requires EPYTHON to be set from without before invocation. # # Example: # @CODE # src_install() { # python_foreach_impl python_newscript foo.py foo # } # @CODE python_newscript() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" [[ ${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 ) python_rewrite_shebang_multilib "${ED%/}/${d}/${newfn}" # install the wrapper _python_ln_rel "${ED%/}"$(_python_get_wrapper_path) \ "${ED%/}/${wrapd}/${barefn}" || die } # @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} "$(mg-qm "${@}")" 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} "$(mg-qm "${@}")" [[ ${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} "$(mg-qm "${@}")" [[ ${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} "$(mg-qm "${@}")" [[ "${2}" ]] && local impl=$(epythonize "${2}") impl="${impl:-${EPYTHON}}" [[ ${impl} ]] || die "${FUNCNAME}: neither impl nor EPYTHON specified." local workdir if [[ $# -eq 0 ]] ; then workdir=$(epythonize "${impl}") [[ ${workdir} ]] || \ die "Can't epythonize \"${impl}\" to construct workdir" workdir="${T}/${workdir}" else workdir="${1:-${T}/${impl}}" fi [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified." debug-print "${FUNCNAME}: using: impl=\"${impl}\" workdir=\"${workdir}\"" 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 EPYTHON="${impl}" python_export EPYTHON PYTHON local SEPYTHON=$(_python_strip_multilib_epython_abi "${EPYTHON}") 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+. if [[ ${SEPYTHON} != python2.5 ]]; then ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die else nonsupp+=( 2to3 ) fi # Python 2.7+. if [[ ${SEPYTHON} != python2.[56] ]]; then ln -s "${EPREFIX}"/usr/$(python_get_library_path ${EPYTHON})/pkgconfig/${SEPYTHON/n/n-}.pc \ "${workdir}"/pkgconfig/python.pc || die else # to ensure we don't pick up any python.pc we don't want i guess? -gmt ln -s /dev/null "${workdir}"/pkgconfig/python.pc || die fi 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/bash source "${PORTAGE_BIN_PATH:-${EPREFIX}/usr/lib/portage/bin}"/isolated-functions.sh ewarn "${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=$(epythonize ${1:-${EPYTHON}}) [[ ${impl} ]] || die "python_is_python3: no impl nor EPYTHON" [[ ${impl} == python3* ]] } # @FUNCTION: python_want_python_exec2 # @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 ]] } # don't punish people to hard for forgetting to migrate to no-_ name _python_want_python_exec2() { ewarn "_python_want_python_exec2 has no leading underscore in the python-multilib-r1 framework." python_want_python_exec2 "$@" } # @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_multilib_automagic_wrapper # @USAGE: [] # @DESCRIPTION: # Automatically strings together various wrapper and utility functions to # automagically construct a temporary and fully reversible environment # suitable for doing python-multilib-r1-framework "things" such as # running python programs. # # The multilib ABI corresponding to the provided impl is determined, and, # _python_do_with_multilib_abi is called upon to activate the appropriate # multilib ABI environment. # # Once that is done, export_python is used to set sane values for as many # variables as possible, and finally, python_wrapper_setup is invoked. # # Then, the provided command is invoked with the provided arguments, # and finally, all environment variables and bash variables are returned # to their original states. # # This is an expensive, but comprehensive, meta-wrapper intended for use in code # which does not run frequently -- preferably, no more than once-per-enabled- # python_multilib-abi-per-ebuild-phase. If a no-multilib ABI is provided, the # default portage ABI (not the current ABI) will be treated as the one requested. # # Any impl that epythonize accepts will work as the first argument. # # This function will not look at the current value of the EPYTHON variable. # It expects an explicit argument selecting the appropriate implementation. # # It is a deadly error to invoke this function during the depend ebuild phase. python_multilib_automagic_wrapper() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" [[ ${EBUILD_PHASE} == depend ]] && die "${FUNCNAME}: not supported during depend phase" local impl=$(epythonize "${1}") [[ ${impl} ]] && shift [[ ${impl} ]] || die "${FUNCNAME}: Could not find EPYTHON corresponding to \"${1}\"" local epython_vars_to_export=(EPYTHON PYTHON{,_SITEDIR,_MULTILIB_PKG_DEP,_SCRIPTDIR}) [[ ${impl} == python* || ${impl} == pypy* ]] && \ epython_vars_to_export+=(PYTHON_INCLUDEDIR) [[ ${impl} == python* ]] && \ epython_vars_to_export+=(PYTHON_{LIBPATH,CFLAGS,LIBS}) local ABI="${ABI:-${DEFAULT_ABI:-default}}" local v for v in "${epython_vars_to_export[@]}" PATH PKG_CONFIG_PATH; do if [[ ${v+set} == set ]] ; then local ${v}="${!v}" else local ${v} unset ${v} fi done export PATH PKG_CONFIG_PATH _python_magic_multilib_wrapper_wrapper() { debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" python_wrapper_setup "${@}" } _python_do_with_multilib_abi "$(_python_impl_abi "${impl}")" \ debug-print-function ${FUNCNAME} "$(mg-qm "${@}")" do_with_python_exports "${impl}" "${epython_vars_to_export[@]}" \ _python_magic_multilib_wrapper_wrapper \ "${@}" } _PYTHON_MULTILIB_UTILS_R1=1 fi