diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index cbd93fce97..8cf8f285c2 100644 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -8,6 +8,15 @@ 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 + # We delierbately do not unset PORTABE_EBUILD_EXTRA_SOURCE, so + # that it keeps being exported in the environment of this + # processes and its child processeses. There, for example portage + # helper like doins, can pick it up and set the PMS variables + # (usually by sourcing isolated-functions.sh). +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/cnf/make.globals b/cnf/make.globals index 94eac65684..dba24c73c7 100644 --- a/cnf/make.globals +++ b/cnf/make.globals @@ -81,7 +81,7 @@ FEATURES="assume-digests binpkg-docompress binpkg-dostrip binpkg-logs network-sandbox news parallel-fetch pkgdir-index-trusted pid-sandbox preserve-libs protect-owned qa-unresolved-soname-deps sandbox strict unknown-features-warn unmerge-logs unmerge-orphans userfetch - userpriv usersandbox usersync" + userpriv usersandbox usersync export-pms-vars" # Ignore file collisions in /lib/modules since files inside this directory # are never unmerged, and therefore collisions must be ignored in order for diff --git a/lib/portage/const.py b/lib/portage/const.py index c9a71009a7..2049f51311 100644 --- a/lib/portage/const.py +++ b/lib/portage/const.py @@ -183,6 +183,7 @@ "distlocks", "downgrade-backup", "ebuild-locks", + "export-pms-vars", "fail-clean", "fakeroot", "fixlafiles", 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..e329b66aa0 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,57 @@ "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. +_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 +2185,38 @@ def spawn( logname_backup = mysettings.configdict["env"].get("LOGNAME") mysettings.configdict["env"]["LOGNAME"] = logname + eapi = mysettings["EAPI"] + + unexported_env_vars = None + if "export-pms-vars" not 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) + + ebuild_extra_source_path = os.path.join(t, ".portage-ebuild-extra-source") + with open(ebuild_extra_source_path, mode="w") as f: + for var_name in unexported_env_vars: + var_value = orig_env.get(var_name) + if var_value is None: + continue + quoted_var_value = shlex.quite(var_value) + f.write(f"{var_name}={quoted_var_value}\n") + del env[var_name] + + env["PORTAGE_EBUILD_EXTRA_SOURCE"] = str(ebuild_extra_source_path) + 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,