Support replacing installed files with cross-implementation symlinks to save space. Opt-in via `DISTUTILS_ALLOW_CROSS_IMPL_SYMLINKS` variable.
Closes: https://bugs.gentoo.org/954762 Signed-off-by: Michał Górny <mgo...@gentoo.org> --- eclass/distutils-r1.eclass | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/eclass/distutils-r1.eclass b/eclass/distutils-r1.eclass index 32cff457e996..d2e4f9ce7ba0 100644 --- a/eclass/distutils-r1.eclass +++ b/eclass/distutils-r1.eclass @@ -227,6 +227,18 @@ # Note that it requires >=dev-python/gpep517-19. : "${DISTUTILS_ALLOW_PYC_SYMLINKS=}" +# @ECLASS_VARIABLE: DISTUTILS_ALLOW_CROSS_IMPL_SYMLINKS +# @USER_VARIABLE +# @DESCRIPTION: +# If set to a non-empty value, the eclass will replace identical files +# from installed wheels with symlinks, across different Python +# implementations. This may include .py files, variety of data files +# and stable ABI extensions. +# +# This is an optimization that can reduce disk space usage when packages +# are built for multiple Python implementations. +: "${DISTUTILS_ALLOW_CROSS_IMPL_SYMLINKS=}" + # @ECLASS_VARIABLE: BUILD_DIR # @OUTPUT_VARIABLE # @DEFAULT_UNSET @@ -1233,6 +1245,33 @@ distutils_wheel_install() { if [[ ${DISTUTILS_ALLOW_PYC_SYMLINKS} ]]; then cmd+=( --symlink-pyc ) fi + if [[ ${DISTUTILS_ALLOW_CROSS_IMPL_SYMLINKS} && ${_DISTUTILS_SITES[@]} ]] + then + set -- "${_DISTUTILS_SITES[@]}" + while [[ ${#} -gt 0 ]]; do + local full_site=${1} + local site=${2} + shift 2 + + mkdir -p "${BUILD_DIR}/install${site%/*}" || die + ln -s "${full_site}" "${BUILD_DIR}/install${site}" || die + done + + # construct relative path from new sitedir to previous sitedir + local new_site=$(python_get_sitedir) + local relative_path= + while [[ ${site%%/*} == ${new_site%%/*} ]]; do + site=${site#*/} + new_site=${new_site#*/} + done + while [[ ${new_site} == */* ]]; do + relative_path+=../ + new_site=${new_site%/*} + done + relative_path+=../${site} + + cmd+=( --symlink-to "${relative_path}" ) + fi printf '%s\n' "${cmd[*]}" "${cmd[@]}" || die "Wheel install failed" @@ -1475,6 +1514,15 @@ distutils_pep517_install() { # distutils_pep517_install calls in the ebuild. declare -g -A DISTUTILS_WHEELS=() +# @VARIABLE: _DISTUTILS_SITES +# @INTERNAL +# @DESCRIPTION: +# A list of site-packages directories for Python implementations +# previously built. Each directory is represented by a pair of values: +# absolute path to it in the build directory, and the final path +# returned by python_get_sitedir. +declare -g _DISTUTILS_SITES=() + # @FUNCTION: distutils-r1_python_compile # @USAGE: [additional-args...] # @DESCRIPTION: @@ -1705,6 +1753,20 @@ distutils-r1_python_install() { mv "${wrapped_scriptdir}" "${reg_scriptdir}" || die fi fi + + # remove site-packages symlinks if we created them + if [[ ${DISTUTILS_ALLOW_CROSS_IMPL_SYMLINKS} ]]; then + set -- "${_DISTUTILS_SITES[@]}" + while [[ ${#} -gt 0 ]]; do + local site=${2} + shift 2 + + if [[ -L "${BUILD_DIR}/install${site}" ]]; then + rm "${BUILD_DIR}/install${site}" || die + fi + done + fi + # prune empty directories to see if ${root} contains anything # to merge find "${BUILD_DIR}"/install -type d -empty -delete || die @@ -2054,6 +2116,10 @@ _distutils-r1_post_python_compile() { _distutils-r1_compare_installed_files fi + + local site=$(python_get_sitedir) + local full_site="${BUILD_DIR}/install${site}" + _DISTUTILS_SITES+=( "${full_site}" "${site}" ) } distutils-r1_src_compile() {