Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Do not export PMS variables
Browse files Browse the repository at this point in the history
Instead of passing PMS variables as part of the process environment,
we pass them via a file that is later sourced by the ebuild's
bash.

Since, for example, A is usually the greatest contributor to the
process environment, removing it from the process environment
significantly avoids running into MAX_ARG_STRLEN when spawning a new
child process.

This means that A and other PMS variables are no longer exported in
the ebuild and hence unavaiable to child processes. However, A is
mostly used as part of the default_src_unpack function and there A
does not need to be exported.

This started as a change that only unexported A, but was later
extended to most PMS variables as suggested by ulm in
https://bugs.gentoo.org/721088#c23.

Thanks to Zac Medico for helpful input on this change, and to Eli
Schwartz for suggesting that (bash) helpers should simply source the
environment file introduced by this change.

Closes: https://bugs.gentoo.org/721088
Signed-off-by: Florian Schmaus <flow@gentoo.org>
Flowdalic committed Jan 7, 2025
1 parent 6f958be commit ddf56a9
Showing 5 changed files with 102 additions and 3 deletions.
9 changes: 9 additions & 0 deletions bin/isolated-functions.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion cnf/make.globals
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/portage/const.py
Original file line number Diff line number Diff line change
@@ -183,6 +183,7 @@
"distlocks",
"downgrade-backup",
"ebuild-locks",
"export-pms-vars",
"fail-clean",
"fakeroot",
"fixlafiles",
10 changes: 9 additions & 1 deletion lib/portage/eapi.py
Original file line number Diff line number Diff line change
@@ -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,
83 changes: 82 additions & 1 deletion lib/portage/package/ebuild/doebuild.py
Original file line number Diff line number Diff line change
@@ -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.quote(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,

0 comments on commit ddf56a9

Please sign in to comment.