-# Copyright 1999-2015 Gentoo Foundation
+# Copyright 1999-2017 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
-# $Id$
# @ECLASS: python-utils-r1.eclass
# @MAINTAINER:
# https://wiki.gentoo.org/wiki/Project:Python/python-utils-r1
case "${EAPI:-0}" in
- 0|1|2|3|4|5)
+ 0|1|2|3|4|5|6)
;;
*)
die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
if [[ ! ${_PYTHON_UTILS_R1} ]]; then
-inherit eutils multilib toolchain-funcs
+[[ ${EAPI:-0} == [012345] ]] && inherit eutils multilib
+inherit toolchain-funcs
# @ECLASS-VARIABLE: _PYTHON_ALL_IMPLS
# @INTERNAL
# @DESCRIPTION:
# All supported Python implementations, most preferred last.
_PYTHON_ALL_IMPLS=(
- jython2_5 jython2_7
+ jython2_7
pypy pypy3
- python3_3 python3_4 python3_5
python2_7
+ python3_4 python3_5 python3_6
)
+readonly _PYTHON_ALL_IMPLS
# @FUNCTION: _python_impl_supported
# @USAGE: <impl>
# keep in sync with _PYTHON_ALL_IMPLS!
# (not using that list because inline patterns shall be faster)
case "${impl}" in
- python2_7|python3_[345]|jython2_[57])
+ python2_7|python3_[456]|jython2_7)
return 0
;;
- pypy1_[89]|pypy2_0|python2_[56]|python3_[12])
+ pypy1_[89]|pypy2_0|python2_[56]|python3_[123])
return 1
;;
pypy|pypy3)
esac
}
+# @FUNCTION: _python_set_impls
+# @INTERNAL
+# @DESCRIPTION:
+# Check PYTHON_COMPAT for well-formedness and validity, then set
+# two global variables:
+#
+# - _PYTHON_SUPPORTED_IMPLS containing valid implementations supported
+# by the ebuild (PYTHON_COMPAT - dead implementations),
+#
+# - and _PYTHON_UNSUPPORTED_IMPLS containing valid implementations that
+# are not supported by the ebuild.
+#
+# Implementations in both variables are ordered using the pre-defined
+# eclass implementation ordering.
+#
+# This function must be called once in global scope by an eclass
+# utilizing PYTHON_COMPAT.
+_python_set_impls() {
+ local i
+
+ if ! declare -p PYTHON_COMPAT &>/dev/null; then
+ die 'PYTHON_COMPAT not declared.'
+ fi
+ if [[ $(declare -p PYTHON_COMPAT) != "declare -a"* ]]; then
+ die 'PYTHON_COMPAT must be an array.'
+ fi
+ for i in "${PYTHON_COMPAT[@]}"; do
+ # trigger validity checks
+ _python_impl_supported "${i}"
+ done
+
+ local supp=() unsupp=()
+
+ for i in "${_PYTHON_ALL_IMPLS[@]}"; do
+ if has "${i}" "${PYTHON_COMPAT[@]}"; then
+ supp+=( "${i}" )
+ else
+ unsupp+=( "${i}" )
+ fi
+ done
+
+ if [[ ! ${supp[@]} ]]; then
+ die "No supported implementation in PYTHON_COMPAT."
+ fi
+
+ if [[ ${_PYTHON_SUPPORTED_IMPLS[@]} ]]; then
+ # set once already, verify integrity
+ if [[ ${_PYTHON_SUPPORTED_IMPLS[@]} != ${supp[@]} ]]; then
+ eerror "Supported impls (PYTHON_COMPAT) changed between inherits!"
+ eerror "Before: ${_PYTHON_SUPPORTED_IMPLS[*]}"
+ eerror "Now : ${supp[*]}"
+ die "_PYTHON_SUPPORTED_IMPLS integrity check failed"
+ fi
+ if [[ ${_PYTHON_UNSUPPORTED_IMPLS[@]} != ${unsupp[@]} ]]; then
+ eerror "Unsupported impls changed between inherits!"
+ eerror "Before: ${_PYTHON_UNSUPPORTED_IMPLS[*]}"
+ eerror "Now : ${unsupp[*]}"
+ die "_PYTHON_UNSUPPORTED_IMPLS integrity check failed"
+ fi
+ else
+ _PYTHON_SUPPORTED_IMPLS=( "${supp[@]}" )
+ _PYTHON_UNSUPPORTED_IMPLS=( "${unsupp[@]}" )
+ readonly _PYTHON_SUPPORTED_IMPLS _PYTHON_UNSUPPORTED_IMPLS
+ fi
+}
+
# @ECLASS-VARIABLE: PYTHON
# @DEFAULT_UNSET
# @DESCRIPTION:
python*)
PYTHON_PKG_DEP="dev-lang/python:${impl#python}";;
pypy)
- PYTHON_PKG_DEP='virtual/pypy:0=';;
+ PYTHON_PKG_DEP='>=virtual/pypy-5:0=';;
pypy3)
- PYTHON_PKG_DEP='virtual/pypy3:0=';;
- jython2.5)
- PYTHON_PKG_DEP='>=dev-java/jython-2.5.3-r2:2.5';;
+ PYTHON_PKG_DEP='>=virtual/pypy3-5:0=';;
jython2.7)
PYTHON_PKG_DEP='dev-java/jython:2.7';;
*)
local PYTHON=${PYTHON}
[[ ${PYTHON} ]] || python_export PYTHON
- # Note: python2.6 can't handle passing files to compileall...
- # TODO: we do not support 2.6 any longer
-
# default to sys.path
if [[ ${#} -eq 0 ]]; then
local f
instpath=/${instpath##/}
case "${EPYTHON}" in
- python*)
+ python2.7|python3.[34])
"${PYTHON}" -m compileall -q -f -d "${instpath}" "${d}"
"${PYTHON}" -OO -m compileall -q -f -d "${instpath}" "${d}"
;;
+ python*|pypy3)
+ # both levels of optimization are separate since 3.5
+ "${PYTHON}" -m compileall -q -f -d "${instpath}" "${d}"
+ "${PYTHON}" -O -m compileall -q -f -d "${instpath}" "${d}"
+ "${PYTHON}" -OO -m compileall -q -f -d "${instpath}" "${d}"
+ ;;
*)
"${PYTHON}" -m compileall -q -f -d "${instpath}" "${d}"
;;
done
}
-# @ECLASS-VARIABLE: python_scriptroot
-# @DEFAULT_UNSET
+# @FUNCTION: python_scriptinto
+# @USAGE: <new-path>
# @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.
+# Set the directory to which files passed to python_doexe(),
+# python_doscript(), python_newexe() and python_newscript()
+# are going to be installed. The new value needs to be relative
+# to the installation root (${ED}).
#
-# Can be set indirectly through the python_scriptinto() function.
+# If not set explicitly, the directory defaults to /usr/bin.
#
# Example:
# @CODE
# src_install() {
-# local python_scriptroot=${GAMES_BINDIR}
+# python_scriptinto /usr/sbin
# python_foreach_impl python_doscript foo
# }
# @CODE
-
-# @FUNCTION: python_scriptinto
-# @USAGE: <new-path>
-# @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} "${@}"
# @FUNCTION: python_doexe
# @USAGE: <files>...
# @DESCRIPTION:
-# Install the given executables into current python_scriptroot,
+# Install the given executables into the executable install directory,
# for the current Python implementation (${EPYTHON}).
#
# The executable will be wrapped properly for the Python implementation,
# @FUNCTION: python_newexe
# @USAGE: <path> <new-name>
# @DESCRIPTION:
-# Install the given executable into current python_scriptroot,
+# Install the given executable into the executable install directory,
# for the current Python implementation (${EPYTHON}).
#
# The executable will be wrapped properly for the Python implementation,
[[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).'
[[ ${#} -eq 2 ]] || die "Usage: ${FUNCNAME} <path> <new-name>"
+ if [[ ${EAPI:-0} == [0123] ]]; then
+ die "python_do* and python_new* helpers are banned in EAPIs older than 4."
+ fi
- local wrapd=${python_scriptroot:-${DESTTREE}/bin}
+ local wrapd=${python_scriptroot:-/usr/bin}
local f=${1}
local newfn=${2}
(
dodir "${wrapd}"
exeinto "${d}"
- newexe "${f}" "${newfn}" || die
+ newexe "${f}" "${newfn}" || return ${?}
)
# install the wrapper
# @FUNCTION: python_doscript
# @USAGE: <files>...
# @DESCRIPTION:
-# Install the given scripts into current python_scriptroot,
+# Install the given scripts into the executable install directory,
# for the current Python implementation (${EPYTHON}).
#
# All specified files must start with a 'python' shebang. The shebang
# @FUNCTION: python_newscript
# @USAGE: <path> <new-name>
# @DESCRIPTION:
-# Install the given script into current python_scriptroot
+# Install the given script into the executable install directory
# for the current Python implementation (${EPYTHON}), and name it
# <new-name>.
#
python_newexe "${@}"
}
-# @ECLASS-VARIABLE: python_moduleroot
-# @DEFAULT_UNSET
+# @FUNCTION: python_moduleinto
+# @USAGE: <new-path>
# @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).
+# Set the Python module install directory for python_domodule().
+# The <new-path> can either be an absolute target system path (in which
+# case it needs to start with a slash, and ${ED} will be prepended to
+# it) or relative to the implementation's site-packages directory
+# (then it must not start with a slash). The relative path can be
+# specified either using the Python package notation (separated by dots)
+# or the directory notation (using slashes).
#
-# When unset, the modules will be installed in the site-packages root.
+# When not set explicitly, the modules are installed to the top
+# site-packages directory.
#
-# Can be set indirectly through the python_moduleinto() function.
+# In the relative case, the exact path is determined directly
+# by each python_doscript/python_newscript function. Therefore,
+# python_moduleinto can be safely called before establishing the Python
+# interpreter and/or a single call can be used to set the path correctly
+# for multiple implementations, as can be seen in the following example.
#
# Example:
# @CODE
# src_install() {
-# local python_moduleroot=bar
+# python_moduleinto bar
# # installs ${PYTHON_SITEDIR}/bar/baz.py
# python_foreach_impl python_domodule baz.py
# }
# @CODE
-
-# @FUNCTION: python_moduleinto
-# @USAGE: <new-path>
-# @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} "${@}"
# @FUNCTION: python_domodule
# @USAGE: <files>...
# @DESCRIPTION:
-# Install the given modules (or packages) into the current
-# python_moduleroot. The list can mention both modules (files)
+# Install the given modules (or packages) into the current Python module
+# installation directory. The list can mention both modules (files)
# and packages (directories). All listed files will be installed
# for all enabled implementations, and compiled afterwards.
#
debug-print-function ${FUNCNAME} "${@}"
[[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).'
+ if [[ ${EAPI:-0} == [0123] ]]; then
+ die "python_do* and python_new* helpers are banned in EAPIs older than 4."
+ fi
local d
if [[ ${python_moduleroot} == /* ]]; then
local PYTHON_SITEDIR=${PYTHON_SITEDIR}
[[ ${PYTHON_SITEDIR} ]] || python_export PYTHON_SITEDIR
- d=${PYTHON_SITEDIR#${EPREFIX}}/${python_moduleroot}
+ d=${PYTHON_SITEDIR#${EPREFIX}}/${python_moduleroot//.//}
fi
- local INSDESTTREE
-
- insinto "${d}"
- doins -r "${@}" || die
+ (
+ insinto "${d}"
+ doins -r "${@}" || return ${?}
+ )
python_optimize "${ED}/${d}"
}
debug-print-function ${FUNCNAME} "${@}"
[[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is null).'
+ if [[ ${EAPI:-0} == [0123] ]]; then
+ die "python_do* and python_new* helpers are banned in EAPIs older than 4."
+ fi
local d PYTHON_INCLUDEDIR=${PYTHON_INCLUDEDIR}
[[ ${PYTHON_INCLUDEDIR} ]] || python_export PYTHON_INCLUDEDIR
d=${PYTHON_INCLUDEDIR#${EPREFIX}}
- local INSDESTTREE
-
- insinto "${d}"
- doins -r "${@}" || die
+ (
+ insinto "${d}"
+ doins -r "${@}" || return ${?}
+ )
}
# @FUNCTION: python_wrapper_setup
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} || die
+ rm -f "${workdir}"/bin/python{,2,3}{,-config} || die
rm -f "${workdir}"/bin/2to3 || die
rm -f "${workdir}"/pkgconfig/python{,2,3}.pc || die
- local EPYTHON PYTHON PYTHON_CONFIG
+ local EPYTHON PYTHON
python_export "${impl}" EPYTHON PYTHON
local pyver pyother
# note: we don't use symlinks because python likes to do some
# symlink reading magic that breaks stuff
# https://bugs.gentoo.org/show_bug.cgi?id=555752
- cat > "${workdir}/bin/python" <<-_EOF_
+ cat > "${workdir}/bin/python" <<-_EOF_ || die
#!/bin/sh
exec "${PYTHON}" "\${@}"
_EOF_
# CPython-specific
if [[ ${EPYTHON} == python* ]]; then
- python_export "${impl}" PYTHON_CONFIG
-
- cat > "${workdir}/bin/python-config" <<-_EOF_
+ cat > "${workdir}/bin/python-config" <<-_EOF_ || die
#!/bin/sh
- exec "${PYTHON_CONFIG}" "\${@}"
+ exec "${PYTHON}-config" "\${@}"
_EOF_
cp "${workdir}/bin/python-config" \
"${workdir}/bin/python${pyver}-config" || die
local x
for x in "${nonsupp[@]}"; do
- cat >"${workdir}"/bin/${x} <<__EOF__
-#!/bin/sh
-echo "${x} is not supported by ${EPYTHON}" >&2
-exit 127
-__EOF__
+ cat >"${workdir}"/bin/${x} <<-_EOF_ || die
+ #!/bin/sh
+ echo "${x} is not supported by ${EPYTHON}" >&2
+ exit 127
+ _EOF_
chmod +x "${workdir}"/bin/${x} || die
done
+ fi
- # 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
+ # 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
}
# @FUNCTION: python_is_python3
local shebang i
local error= from=
+ # note: we can't ||die here since read will fail if file
+ # has no newline characters
IFS= read -r shebang <"${f}"
# First, check if it's shebang at all...
if [[ ${shebang} == '#!'* ]]; then
local split_shebang=()
- read -r -a split_shebang <<<${shebang}
+ read -r -a split_shebang <<<${shebang} || die
# Match left-to-right in a loop, to avoid matching random
# repetitions like 'python2.7 python2'.
eerror " requested impl: ${EPYTHON}"
die "${FUNCNAME}: conversion of incompatible shebang requested"
fi
- done < <(find "${path}" -type f -print0 || die)
+ done < <(find -H "${path}" -type f -print0 || die)
if [[ ! ${any_fixed} ]]; then
- eqawarn "QA warning: ${FUNCNAME}, ${path#${D}} did not match any fixable files."
+ local cmd=eerror
+ [[ ${EAPI:-0} == [012345] ]] && cmd=eqawarn
+
+ "${cmd}" "QA warning: ${FUNCNAME}, ${path#${D}} did not match any fixable files."
if [[ ${any_correct} ]]; then
- eqawarn "All files have ${EPYTHON} shebang already."
+ "${cmd}" "All files have ${EPYTHON} shebang already."
else
- eqawarn "There are no Python files in specified directory."
+ "${cmd}" "There are no Python files in specified directory."
fi
+
+ [[ ${cmd} == eerror ]] && die "${FUNCNAME} did not match any fixable files (QA warning fatal in EAPI ${EAPI})"
fi
done
}
# Check whether the specified locale sanely maps between lowercase
# and uppercase ASCII characters.
_python_check_locale_sanity() {
- local -x LC_CTYPE=${1}
+ local -x LC_ALL=${1}
local IFS=
local lc=( {a..z} )
local uc=( {A..Z} )
- local input=${lc[*]}${uc[*]}
+ local input="${lc[*]}${uc[*]}"
local output=$(tr '[:lower:][:upper:]' '[:upper:][:lower:]' <<<"${input}")
[[ ${output} == "${uc[*]}${lc[*]}" ]]
type locale >/dev/null || return 0
if [[ $(locale charmap) != UTF-8 ]]; then
- if [[ -n ${LC_ALL} ]]; then
- ewarn "LC_ALL is set to a locale with a charmap other than UTF-8."
- ewarn "This may trigger build failures in some python packages."
- return 1
- fi
-
# Try English first, then everything else.
- local lang locales="en_US.UTF-8 $(locale -a)"
+ local lang locales="C.UTF-8 en_US.UTF-8 en_GB.UTF-8 $(locale -a)"
for lang in ${locales}; do
- if [[ $(LC_CTYPE=${lang} locale charmap 2>/dev/null) == UTF-8 ]]; then
+ if [[ $(LC_ALL=${lang} locale charmap 2>/dev/null) == UTF-8 ]]; then
if _python_check_locale_sanity "${lang}"; then
export LC_CTYPE=${lang}
+ if [[ -n ${LC_ALL} ]]; then
+ export LC_NUMERIC=${LC_ALL}
+ export LC_TIME=${LC_ALL}
+ export LC_COLLATE=${LC_ALL}
+ export LC_MONETARY=${LC_ALL}
+ export LC_MESSAGES=${LC_ALL}
+ export LC_PAPER=${LC_ALL}
+ export LC_NAME=${LC_ALL}
+ export LC_ADDRESS=${LC_ALL}
+ export LC_TELEPHONE=${LC_ALL}
+ export LC_MEASUREMENT=${LC_ALL}
+ export LC_IDENTIFICATION=${LC_ALL}
+ export LC_ALL=
+ fi
return 0
fi
- fi
+ fi
done
ewarn "Could not find a UTF-8 locale. This may trigger build failures in"
ewarn "some python packages. Please ensure that a UTF-8 locale is listed in"
ewarn "/etc/locale.gen and run locale-gen."
return 1
- fi
+ fi
return 0
}