From a9e845b6ee03a926441f2c788a4b7a19b551ad73 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Wed, 28 Oct 2020 21:40:45 +0100 Subject: [PATCH] eclass: Update distutils eclass The updated app-portage/gentoolkit is using some function from distutils-r1 that was not here before. --- .../portage-stable/eclass/distutils-r1.eclass | 400 ++++++++++++++++-- 1 file changed, 359 insertions(+), 41 deletions(-) diff --git a/sdk_container/src/third_party/portage-stable/eclass/distutils-r1.eclass b/sdk_container/src/third_party/portage-stable/eclass/distutils-r1.eclass index 66920d1f75..5ffc91be47 100644 --- a/sdk_container/src/third_party/portage-stable/eclass/distutils-r1.eclass +++ b/sdk_container/src/third_party/portage-stable/eclass/distutils-r1.eclass @@ -1,4 +1,4 @@ -# Copyright 1999-2018 Gentoo Foundation +# Copyright 1999-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 # @ECLASS: distutils-r1.eclass @@ -40,8 +40,8 @@ # as well. Thus, all the variables defined and documented there are # relevant to the packages using distutils-r1. # -# For more information, please see the wiki: -# https://wiki.gentoo.org/wiki/Project:Python/distutils-r1 +# For more information, please see the Python Guide: +# https://dev.gentoo.org/~mgorny/python-guide/ case "${EAPI:-0}" in 0|1|2|3|4) @@ -77,9 +77,27 @@ esac # to be exported. It must be run in order for the eclass functions # to function properly. +# @ECLASS-VARIABLE: DISTUTILS_USE_SETUPTOOLS +# @PRE_INHERIT +# @DESCRIPTION: +# Controls adding dev-python/setuptools dependency. The allowed values +# are: +# +# - no -- do not add the dependency (pure distutils package) +# - bdepend -- add it to BDEPEND (the default) +# - rdepend -- add it to BDEPEND+RDEPEND (when using entry_points) +# - pyproject.toml -- use pyproject2setuptools to install a project +# using pyproject.toml (flit, poetry...) +# - manual -- do not add the depedency and suppress the checks +# (assumes you will take care of doing it correctly) +# +# This variable is effective only if DISTUTILS_OPTIONAL is disabled. +# It needs to be set before the inherit line. +: ${DISTUTILS_USE_SETUPTOOLS:=bdepend} + if [[ ! ${_DISTUTILS_R1} ]]; then -[[ ${EAPI} == [45] ]] && inherit eutils +[[ ${EAPI} == [456] ]] && inherit eutils [[ ${EAPI} == [56] ]] && inherit xdg-utils inherit multiprocessing toolchain-funcs @@ -97,15 +115,46 @@ fi if [[ ! ${_DISTUTILS_R1} ]]; then -if [[ ! ${DISTUTILS_OPTIONAL} ]]; then - RDEPEND=${PYTHON_DEPS} - if [[ ${EAPI} != [56] ]]; then - BDEPEND=${PYTHON_DEPS} +_distutils_set_globals() { + local rdep bdep + local setuptools_dep='>=dev-python/setuptools-42.0.2[${PYTHON_USEDEP}]' + + case ${DISTUTILS_USE_SETUPTOOLS} in + no|manual) + ;; + bdepend) + bdep+=" ${setuptools_dep}" + ;; + rdepend) + bdep+=" ${setuptools_dep}" + rdep+=" ${setuptools_dep}" + ;; + pyproject.toml) + bdep+=' dev-python/pyproject2setuppy[${PYTHON_USEDEP}]' + ;; + *) + die "Invalid DISTUTILS_USE_SETUPTOOLS=${DISTUTILS_USE_SETUPTOOLS}" + ;; + esac + + if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then + bdep=${bdep//\$\{PYTHON_USEDEP\}/${PYTHON_USEDEP}} + rdep=${rdep//\$\{PYTHON_USEDEP\}/${PYTHON_USEDEP}} else - DEPEND=${PYTHON_DEPS} + [[ -n ${bdep} ]] && bdep="$(python_gen_cond_dep "${bdep}")" + [[ -n ${rdep} ]] && rdep="$(python_gen_cond_dep "${rdep}")" + fi + + RDEPEND="${PYTHON_DEPS} ${rdep}" + if [[ ${EAPI} != [56] ]]; then + BDEPEND="${PYTHON_DEPS} ${bdep}" + else + DEPEND="${PYTHON_DEPS} ${bdep}" fi REQUIRED_USE=${PYTHON_REQUIRED_USE} -fi +} +[[ ! ${DISTUTILS_OPTIONAL} ]] && _distutils_set_globals +unset -f _distutils_set_globals # @ECLASS-VARIABLE: PATCHES # @DEFAULT_UNSET @@ -232,6 +281,222 @@ fi # } # @CODE +# @FUNCTION: distutils_enable_sphinx +# @USAGE: [--no-autodoc | ...] +# @DESCRIPTION: +# Set up IUSE, BDEPEND, python_check_deps() and python_compile_all() for +# building HTML docs via dev-python/sphinx. python_compile_all() will +# append to HTML_DOCS if docs are enabled. +# +# This helper is meant for the most common case, that is a single Sphinx +# subdirectory with standard layout, building and installing HTML docs +# behind USE=doc. It assumes it's the only consumer of the three +# aforementioned functions. If you need to use a custom implemention, +# you can't use it. +# +# If your package uses additional Sphinx plugins, they should be passed +# (without PYTHON_USEDEP) as . The function will take care +# of setting appropriate any-of dep and python_check_deps(). +# +# If no plugin packages are specified, the eclass will still utilize +# any-r1 API to support autodoc (documenting source code). +# If the package uses neither autodoc nor additional plugins, you should +# pass --no-autodoc to disable this API and simplify the resulting code. +# +# This function must be called in global scope. Take care not to +# overwrite the variables set by it. If you need to extend +# python_compile_all(), you can call the original implementation +# as sphinx_compile_all. +distutils_enable_sphinx() { + debug-print-function ${FUNCNAME} "${@}" + [[ ${#} -ge 1 ]] || die "${FUNCNAME} takes at least one arg: " + + _DISTUTILS_SPHINX_SUBDIR=${1} + shift + _DISTUTILS_SPHINX_PLUGINS=( "${@}" ) + + local deps autodoc=1 d + for d; do + if [[ ${d} == --no-autodoc ]]; then + autodoc= + else + deps+=" + ${d}[\${PYTHON_USEDEP}]" + fi + done + + if [[ ! ${autodoc} && -n ${deps} ]]; then + die "${FUNCNAME}: do not pass --no-autodoc if external plugins are used" + fi + if [[ ${autodoc} ]]; then + deps="$(python_gen_any_dep " + dev-python/sphinx[\${PYTHON_USEDEP}] + ${deps}")" + + python_check_deps() { + use doc || return 0 + local p + for p in dev-python/sphinx "${_DISTUTILS_SPHINX_PLUGINS[@]}"; do + has_version "${p}[${PYTHON_USEDEP}]" || return 1 + done + } + else + deps="dev-python/sphinx" + fi + + sphinx_compile_all() { + use doc || return + + local confpy=${_DISTUTILS_SPHINX_SUBDIR}/conf.py + [[ -f ${confpy} ]] || + die "${confpy} not found, distutils_enable_sphinx call wrong" + + if [[ ${_DISTUTILS_SPHINX_PLUGINS[0]} == --no-autodoc ]]; then + if grep -F -q 'sphinx.ext.autodoc' "${confpy}"; then + die "distutils_enable_sphinx: --no-autodoc passed but sphinx.ext.autodoc found in ${confpy}" + fi + elif [[ -z ${_DISTUTILS_SPHINX_PLUGINS[@]} ]]; then + if ! grep -F -q 'sphinx.ext.autodoc' "${confpy}"; then + die "distutils_enable_sphinx: sphinx.ext.autodoc not found in ${confpy}, pass --no-autodoc" + fi + fi + + build_sphinx "${_DISTUTILS_SPHINX_SUBDIR}" + } + python_compile_all() { sphinx_compile_all; } + + IUSE+=" doc" + if [[ ${EAPI} == [56] ]]; then + DEPEND+=" doc? ( ${deps} )" + else + BDEPEND+=" doc? ( ${deps} )" + fi + + # we need to ensure successful return in case we're called last, + # otherwise Portage may wrongly assume sourcing failed + return 0 +} + +# @FUNCTION: distutils_enable_tests +# @USAGE: [--install] +# @DESCRIPTION: +# Set up IUSE, RESTRICT, BDEPEND and python_test() for running tests +# with the specified test runner. Also copies the current value +# of RDEPEND to test?-BDEPEND. The test-runner argument must be one of: +# +# - nose: nosetests (dev-python/nose) +# - pytest: dev-python/pytest +# - setup.py: setup.py test (no deps included) +# - unittest: for built-in Python unittest module +# +# Additionally, if --install is passed as the first parameter, +# 'distutils_install_for_testing --via-root' is called before running +# the test suite. +# +# This function is meant as a helper for common use cases, and it only +# takes care of basic setup. You still need to list additional test +# dependencies manually. If you have uncommon use case, you should +# not use it and instead enable tests manually. +# +# This function must be called in global scope, after RDEPEND has been +# declared. Take care not to overwrite the variables set by it. +distutils_enable_tests() { + debug-print-function ${FUNCNAME} "${@}" + + local do_install= + case ${1} in + --install) + do_install=1 + shift + ;; + esac + + [[ ${#} -eq 1 ]] || die "${FUNCNAME} takes exactly one argument: test-runner" + local test_pkg + case ${1} in + nose) + test_pkg=">=dev-python/nose-1.3.7-r4" + if [[ ${do_install} ]]; then + python_test() { + distutils_install_for_testing --via-root + nosetests -v || die "Tests fail with ${EPYTHON}" + } + else + python_test() { + nosetests -v || die "Tests fail with ${EPYTHON}" + } + fi + ;; + pytest) + test_pkg=">=dev-python/pytest-4.5.0" + if [[ ${do_install} ]]; then + python_test() { + distutils_install_for_testing --via-root + pytest -vv || die "Tests fail with ${EPYTHON}" + } + else + python_test() { + pytest -vv || die "Tests fail with ${EPYTHON}" + } + fi + ;; + setup.py) + if [[ ${do_install} ]]; then + python_test() { + distutils_install_for_testing --via-root + nonfatal esetup.py test --verbose || + die "Tests fail with ${EPYTHON}" + } + else + python_test() { + nonfatal esetup.py test --verbose || + die "Tests fail with ${EPYTHON}" + } + fi + ;; + unittest) + if [[ ${do_install} ]]; then + python_test() { + distutils_install_for_testing --via-root + "${EPYTHON}" -m unittest discover -v || + die "Tests fail with ${EPYTHON}" + } + else + python_test() { + "${EPYTHON}" -m unittest discover -v || + die "Tests fail with ${EPYTHON}" + } + fi + ;; + *) + die "${FUNCNAME}: unsupported argument: ${1}" + esac + + local test_deps=${RDEPEND} + if [[ -n ${test_pkg} ]]; then + if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then + test_deps+=" ${test_pkg}[${PYTHON_USEDEP}]" + else + test_deps+=" $(python_gen_cond_dep " + ${test_pkg}[\${PYTHON_MULTI_USEDEP}] + ")" + fi + fi + if [[ -n ${test_deps} ]]; then + IUSE+=" test" + RESTRICT+=" !test? ( test )" + if [[ ${EAPI} == [56] ]]; then + DEPEND+=" test? ( ${test_deps} )" + else + BDEPEND+=" test? ( ${test_deps} )" + fi + fi + + # we need to ensure successful return in case we're called last, + # otherwise Portage may wrongly assume sourcing failed + return 0 +} + # @FUNCTION: esetup.py # @USAGE: [...] # @DESCRIPTION: @@ -269,7 +534,7 @@ esetup.py() { } # @FUNCTION: distutils_install_for_testing -# @USAGE: [...] +# @USAGE: [--via-root|--via-home] [...] # @DESCRIPTION: # Install the package into a temporary location for running tests. # Update PYTHONPATH appropriately and set TEST_DIR to the test @@ -280,11 +545,19 @@ esetup.py() { # namespaces (and therefore proper install needs to be done to enforce # PYTHONPATH) or tests rely on the results of install command. # For most of the packages, tests built in BUILD_DIR are good enough. +# +# The function supports two install modes. The current default is +# the legacy --via-home mode. However, it has problems with newer +# versions of setuptools (50.3.0+). The --via-root mode generally +# works for these packages, and it will probably become the default +# in the future, once we test all affected packages. Please note +# that proper testing sometimes requires unmerging the package first. distutils_install_for_testing() { debug-print-function ${FUNCNAME} "${@}" # A few notes: - # 1) because of namespaces, we can't use 'install --root', + # 1) because of namespaces, we can't use 'install --root' + # (NB: this is probably no longer true with py3), # 2) 'install --home' is terribly broken on pypy, so we need # to override --install-lib and --install-scripts, # 3) non-root 'install' complains about PYTHONPATH and missing dirs, @@ -296,16 +569,42 @@ distutils_install_for_testing() { TEST_DIR=${BUILD_DIR}/test local bindir=${TEST_DIR}/scripts local libdir=${TEST_DIR}/lib + PATH=${bindir}:${PATH} PYTHONPATH=${libdir}:${PYTHONPATH} - local add_args=( - install - --home="${TEST_DIR}" - --install-lib="${libdir}" - --install-scripts="${bindir}" - ) + local install_method=home + case ${1} in + --via-home) + install_method=home + shift + ;; + --via-root) + install_method=root + shift + ;; + esac + + local -a add_args + case ${install_method} in + home) + add_args=( + install + --home="${TEST_DIR}" + --install-lib="${libdir}" + --install-scripts="${bindir}" + ) + mkdir -p "${libdir}" || die + ;; + root) + add_args=( + install + --root="${TEST_DIR}" + --install-lib=lib + --install-scripts=scripts + ) + ;; + esac - mkdir -p "${libdir}" || die esetup.py "${add_args[@]}" "${@}" } @@ -324,6 +623,28 @@ _distutils-r1_disable_ez_setup() { fi } +# @FUNCTION: _distutils-r1_handle_pyproject_toml +# @INTERNAL +# @DESCRIPTION: +# Generate setup.py for pyproject.toml if requested. +_distutils-r1_handle_pyproject_toml() { + if [[ ! -f setup.py && -f pyproject.toml ]]; then + if [[ ${DISTUTILS_USE_SETUPTOOLS} == pyproject.toml ]]; then + cat > setup.py <<-EOF || die + #!/usr/bin/env python + from pyproject2setuppy.main import main + main() + EOF + chmod +x setup.py || die + else + eerror "No setup.py found but pyproject.toml is present. In order to enable" + eerror "pyproject.toml support in distutils-r1, set:" + eerror " DISTUTILS_USE_SETUPTOOLS=pyproject.toml" + die "No setup.py found and DISTUTILS_USE_SETUPTOOLS!=pyproject.toml" + fi + fi +} + # @FUNCTION: distutils-r1_python_prepare_all # @DESCRIPTION: # The default python_prepare_all(). It applies the patches from PATCHES @@ -352,6 +673,7 @@ distutils-r1_python_prepare_all() { fi _distutils-r1_disable_ez_setup + _distutils-r1_handle_pyproject_toml if [[ ${DISTUTILS_IN_SOURCE_BUILD} && ! ${DISTUTILS_SINGLE_IMPL} ]] then @@ -486,13 +808,11 @@ _distutils-r1_wrap_scripts() { local path=${1} local bindir=${2} - local PYTHON_SCRIPTDIR - python_export PYTHON_SCRIPTDIR - + local scriptdir=$(python_get_scriptdir) local f python_files=() non_python_files=() - if [[ -d ${path}${PYTHON_SCRIPTDIR} ]]; then - for f in "${path}${PYTHON_SCRIPTDIR}"/*; do + if [[ -d ${path}${scriptdir} ]]; then + for f in "${path}${scriptdir}"/*; do [[ -d ${f} ]] && die "Unexpected directory: ${f}" debug-print "${FUNCNAME}: found executable at ${f#${path}/}" @@ -547,6 +867,8 @@ distutils-r1_python_install() { # failures if some packages haven't compiled their modules yet. addpredict "${EPREFIX}/usr/lib/${EPYTHON}" addpredict "${EPREFIX}/usr/$(get_libdir)/${EPYTHON}" + addpredict /usr/lib/pypy2.7 + addpredict /usr/lib/pypy3.6 addpredict /usr/lib/portage/pym addpredict /usr/local # bug 498232 @@ -588,9 +910,9 @@ distutils-r1_python_install() { local root=${D%/}/_${EPYTHON} [[ ${DISTUTILS_SINGLE_IMPL} ]] && root=${D%/} - esetup.py install --root="${root}" "${args[@]}" + esetup.py install --skip-build --root="${root}" "${args[@]}" - local forbidden_package_names=( examples test tests ) + local forbidden_package_names=( examples test tests .pytest_cache ) local p for p in "${forbidden_package_names[@]}"; do if [[ -d ${root}$(python_get_sitedir)/${p} ]]; then @@ -607,9 +929,7 @@ distutils-r1_python_install() { ${shopt_save} if [[ -n ${pypy_dirs} ]]; then - local cmd=die - [[ ${EAPI} == [45] ]] && cmd=eqawarn - "${cmd}" "Package installs 'share' in PyPy prefix, see bug #465546." + die "Package installs 'share' in PyPy prefix, see bug #465546." fi if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then @@ -635,8 +955,6 @@ distutils-r1_python_install_all() { ) docompress -x "/usr/share/doc/${PF}/examples" fi - - _DISTUTILS_DEFAULT_CALLED=1 } # @FUNCTION: distutils-r1_run_phase @@ -656,13 +974,22 @@ distutils-r1_run_phase() { debug-print-function ${FUNCNAME} "${@}" if [[ ${DISTUTILS_IN_SOURCE_BUILD} ]]; then - if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then + # only force BUILD_DIR if implementation is explicitly enabled + # for building; any-r1 API may select one that is not + # https://bugs.gentoo.org/701506 + if [[ ! ${DISTUTILS_SINGLE_IMPL} ]] && + has "${EPYTHON/./_}" ${PYTHON_TARGETS}; then cd "${BUILD_DIR}" || die fi local BUILD_DIR=${BUILD_DIR}/build fi local -x PYTHONPATH="${BUILD_DIR}/lib:${PYTHONPATH}" + # make PATH local for distutils_install_for_testing calls + # it makes little sense to let user modify PATH in per-impl phases + # and _all() already localizes it + local -x PATH=${PATH} + # Bug 559644 # using PYTHONPATH when the ${BUILD_DIR}/lib is not created yet might lead to # problems in setup.py scripts that try to import modules/packages from that path @@ -855,21 +1182,12 @@ distutils-r1_src_install() { _distutils-r1_run_foreach_impl distutils-r1_python_install fi - local _DISTUTILS_DEFAULT_CALLED - if declare -f python_install_all >/dev/null; then _distutils-r1_run_common_phase python_install_all else _distutils-r1_run_common_phase distutils-r1_python_install_all fi - if [[ ! ${_DISTUTILS_DEFAULT_CALLED} ]]; then - local cmd=die - [[ ${EAPI} == [45] ]] && cmd=eqawarn - - "${cmd}" "QA: python_install_all() didn't call distutils-r1_python_install_all" - fi - _distutils-r1_check_namespace_pth }