diff --git a/bin/ebuild.sh b/bin/ebuild.sh index a742397db9..bb94062f77 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -114,7 +114,7 @@ export PORTAGE_BZIP2_COMMAND=${PORTAGE_BZIP2_COMMAND:-bzip2} # These two functions wrap sourcing and calling respectively. At present they # perform a qa check to make sure eclasses and ebuilds and profiles don't mess -# with shell opts (shopts). Ebuilds/eclasses changing shopts should reset them +# with shell opts (shopts). Ebuilds/eclasses changing shopts should reset them # when they are done. __qa_source() { diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index cbd93fce97..369111e78b 100644 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -8,6 +8,11 @@ if ___eapi_has_version_functions; then source "${PORTAGE_BIN_PATH}/eapi7-ver-funcs.sh" || exit 1 fi +if [[ -v PORTAGE_EBUILD_EXTRA_SOURCE ]]; then + source "${PORTAGE_EBUILD_EXTRA_SOURCE}" || exit 1 + unset PORTAGE_EBUILD_EXTRA_SOURCE +fi + # We need this next line for "die" and "assert". It expands # It _must_ preceed all the calls to die and assert. shopt -s expand_aliases diff --git a/lib/portage/const.py b/lib/portage/const.py index c9a71009a7..d2d0d0e145 100644 --- a/lib/portage/const.py +++ b/lib/portage/const.py @@ -181,6 +181,7 @@ "digest", "distcc", "distlocks", + "dont-export-pms-vars", "downgrade-backup", "ebuild-locks", "fail-clean", diff --git a/lib/portage/eapi.py b/lib/portage/eapi.py index 86b27bdbc5..28ea8be4b7 100644 --- a/lib/portage/eapi.py +++ b/lib/portage/eapi.py @@ -1,4 +1,4 @@ -# Copyright 2010-2021 Gentoo Authors +# Copyright 2010-2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import collections @@ -48,6 +48,10 @@ def eapi_supports_prefix(eapi: str) -> bool: return _get_eapi_attrs(eapi).prefix +def eapi_exports_pms_vars(eapi: str) -> bool: + return _get_eapi_attrs(eapi).exports_pms_vars + + def eapi_exports_AA(eapi: str) -> bool: return _get_eapi_attrs(eapi).exports_AA @@ -157,6 +161,7 @@ def eapi_has_sysroot(eapi: str) -> bool: "exports_ECLASSDIR", "exports_KV", "exports_merge_type", + "exports_pms_vars", "exports_PORTDIR", "exports_replace_vars", "feature_flag_test", @@ -198,6 +203,7 @@ class Eapi: "6", "7", "8", + "9", ) _eapi_val: int = -1 @@ -236,6 +242,7 @@ def _get_eapi_attrs(eapi_str: Optional[str]) -> _eapi_attrs: exports_ECLASSDIR=False, exports_KV=False, exports_merge_type=True, + exports_pms_vars=True, exports_PORTDIR=True, exports_replace_vars=True, feature_flag_test=False, @@ -275,6 +282,7 @@ def _get_eapi_attrs(eapi_str: Optional[str]) -> _eapi_attrs: exports_ECLASSDIR=eapi <= Eapi("6"), exports_KV=eapi <= Eapi("3"), exports_merge_type=eapi >= Eapi("4"), + exports_pms_vars=eapi <= Eapi("8"), exports_PORTDIR=eapi <= Eapi("6"), exports_replace_vars=eapi >= Eapi("4"), feature_flag_test=False, diff --git a/lib/portage/package/ebuild/doebuild.py b/lib/portage/package/ebuild/doebuild.py index 1d257d52db..446b073862 100644 --- a/lib/portage/package/ebuild/doebuild.py +++ b/lib/portage/package/ebuild/doebuild.py @@ -84,6 +84,7 @@ from portage.eapi import ( eapi_exports_KV, eapi_exports_merge_type, + eapi_exports_pms_vars, eapi_exports_replace_vars, eapi_has_required_use, eapi_has_src_prepare_and_src_configure, @@ -189,6 +190,65 @@ "RESTRICT", ) +# The following is a set of PMS § 11.1 and § 7.4 without +# - TMPDIR +# - HOME +# because these variables are often assumed to be exported and +# therefore consumed by child processes, without +# - T +# which is required to be exported e.g., for portage's newins +# without, +# - ED +# which is required for doins. +# TODO: There are probably more variables that should be +# exported, because forked processes expect them in their +# environment. +_unexported_pms_vars = frozenset( + # fmt: off + [ + # PMS § 11.1 Defined Variables + "P", # NOT-EXPORTED: tendency to break Makefiles when exported + "PF", + "PN", + "CATEGORY", + "PV", + "PR", + "PVR", + "A", # NOT-EXPORTED: largest contributor to process environment when exported + "AA", # NOT-EXPORTED: unused after EAPI 4 + "FILESDIR", + "DISTDIR", + "WORKDIR", + "S", + "PORTDIR", + "ECLASSDIR", + "ROOT", + "EROOT", + "SYSROOT", + "ESYSROOT", + "BROOT", + "T", +# "TMPDIR", # EXPORTED: often assumed to be exported and available to child processes +# "HOME", # EXPORTED: often assumed to be exported and available to child processes + "EPREFIX", + "D", # NOT-EXPORTED: tendency to break Makefiles when exported + "ED", + "DESTTREE", + "INSDESTTREE", + "EBUILD_PHASE", + "EBUILD_PHASE_FUNC", + "KV", + "MERGE_TYPE", + "REPLACING_VERSIONS", + "REPLACED_BY_VERSION", + # PMS 7.4 Magic Ebuild-defined Variables + "ECLASS", + "INHERITED", + "DEFINED_PHASES", + ] + # fmt: on +) + def _doebuild_spawn(phase, settings, actionmap=None, **kwargs): """ @@ -2133,9 +2193,37 @@ def spawn( logname_backup = mysettings.configdict["env"].get("LOGNAME") mysettings.configdict["env"]["LOGNAME"] = logname + eapi = mysettings["EAPI"] + + unexported_env_vars = None + if "dont-export-pms-vars" in mysettings.features or not eapi_exports_pms_vars(eapi): + unexported_env_vars = _unexported_pms_vars + + if unexported_env_vars: + orig_env = mysettings.environ() + # Copy since we are potentially removing keys from the dict. + env = orig_env.copy() + + t = env["T"] + if not os.path.isdir(t): + os.makedirs(t) + + ebuildExtraSource = os.path.join(t, ".portage-ebuild-extra-source") + with open(ebuildExtraSource, mode="w") as f: + for var_name in unexported_env_vars: + var_value = orig_env.get(var_name) + if var_value is None: + continue + f.write(f"{var_name}='{var_value}'\n") + del env[var_name] + + env["PORTAGE_EBUILD_EXTRA_SOURCE"] = str(ebuildExtraSource) + else: + env = mysettings.environ() + try: if keywords.get("returnpid") or keywords.get("returnproc"): - return spawn_func(mystring, env=mysettings.environ(), **keywords) + return spawn_func(mystring, env=env, **keywords) proc = EbuildSpawnProcess( background=False,