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() {

Reply via email to