commit:     7cc778fbf3aa7919036f2bb224b118d96c14f31e
Author:     Arfrever Frehtes Taifersar Arahesis <arfrever <AT> apache <DOT> org>
AuthorDate: Mon Jul 11 02:54:26 2022 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Tue Jul 12 23:30:05 2022 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=7cc778fb

env-update: handle BROOT vs ROOT distinction for e.g. PATH

When Portage builds packages with a non-trivial ROOT setting, say,
ROOT=/tmp/stage1root, the build dependencies (compilers, tools, etc.)
should be taken from the actual build environment BROOT, say BROOT=/ ,
and that must also be reflected in the environment.

An example situation that leads to problems otherwise is the following:
* We build a clang-based stage1 with CC=clang
* The clang binary exists only in a custom directory pulled into PATH
  via env.d.
* ROOT=/tmp/stage1root is initially empty, meaning portage tries to
  build with settings from an empty env.d, and suddently cannot find
  the clang that perfectly exists in BROOT=/ anymore.
* The build fails.

The patch takes a more versatile approach and separates environment
variables into three categories:
* constructed from ROOT
* constructed from BROOT
* combined from ROOT and BROOT (checks EROOT, then BROOT. If present in both,
only EROOT is used.)

Thanks-to: Andreas K. Hüttel <dilfridge <AT> gentoo.org>
Thanks-to: James Le Cuirot <chewi <AT> gentoo.org>
Signed-off-by: Sam James <sam <AT> gentoo.org>
Closes: https://github.com/gentoo/portage/pull/851
Signed-off-by: Sam James <sam <AT> gentoo.org>

 lib/portage/package/ebuild/config.py | 77 +++++++++++++++++++++++++++++-------
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/lib/portage/package/ebuild/config.py 
b/lib/portage/package/ebuild/config.py
index 49a906559..2530c9d31 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -485,14 +485,7 @@ class config:
             # interaction with the calling environment that might
             # lead to unexpected results.
 
-            env_d = (
-                getconfig(
-                    os.path.join(eroot, "etc", "profile.env"),
-                    tolerant=tolerant,
-                    expand=False,
-                )
-                or {}
-            )
+            env_d = self._get_env_d(broot=broot, eroot=eroot, 
tolerant=tolerant)
             expand_map = env_d.copy()
             self._expand_map = expand_map
 
@@ -1209,6 +1202,61 @@ class config:
         if mycpv:
             self.setcpv(mycpv)
 
+    def _get_env_d(self, broot, eroot, tolerant):
+        broot_only_variables = (
+            "PATH",
+            "PREROOTPATH",
+            "ROOTPATH",
+        )
+        eroot_only_variables = (
+            "CONFIG_PROTECT",
+            "CONFIG_PROTECT_MASK",
+            "INFODIR",
+            "INFOPATH",
+            "MANPATH",
+            "PKG_CONFIG_.*",
+        )
+
+        broot_only_variables_re = re.compile(r"^(%s)$" % 
"|".join(broot_only_variables))
+        eroot_only_variables_re = re.compile(r"^(%s)$" % 
"|".join(eroot_only_variables))
+
+        broot_env_d_path = os.path.join(broot or "/", "etc", "profile.env")
+        eroot_env_d_path = os.path.join(eroot or "/", "etc", "profile.env")
+
+        if (
+            os.path.exists(broot_env_d_path)
+            and os.path.exists(eroot_env_d_path)
+            and os.path.samefile(broot_env_d_path, eroot_env_d_path)
+        ):
+            broot_env_d = (
+                getconfig(broot_env_d_path, tolerant=tolerant, expand=False) 
or {}
+            )
+            eroot_env_d = broot_env_d
+        else:
+            broot_env_d = (
+                getconfig(broot_env_d_path, tolerant=tolerant, expand=False) 
or {}
+            )
+            eroot_env_d = (
+                getconfig(eroot_env_d_path, tolerant=tolerant, expand=False) 
or {}
+            )
+
+        env_d = {}
+
+        for k in broot_env_d.keys() | eroot_env_d.keys():
+            if broot_only_variables_re.match(k):
+                if k in broot_env_d:
+                    env_d[k] = broot_env_d[k]
+            elif eroot_only_variables_re.match(k):
+                if k in eroot_env_d:
+                    env_d[k] = eroot_env_d[k]
+            else:
+                if k in eroot_env_d:
+                    env_d[k] = eroot_env_d[k]
+                elif k in broot_env_d:
+                    env_d[k] = broot_env_d[k]
+
+        return env_d
+
     def _init_iuse(self):
         self._iuse_effective = self._calc_iuse_effective()
         self._iuse_implicit_match = _iuse_implicit_match_cache(self)
@@ -2640,14 +2688,13 @@ class config:
 
     def reload(self):
         """Reload things like /etc/profile.env that can change during 
runtime."""
-        env_d_filename = os.path.join(self["EROOT"], "etc", "profile.env")
         self.configdict["env.d"].clear()
-        env_d = getconfig(env_d_filename, tolerant=self._tolerant, 
expand=False)
-        if env_d:
-            # env_d will be None if profile.env doesn't exist.
-            for k in self._env_d_blacklist:
-                env_d.pop(k, None)
-            self.configdict["env.d"].update(env_d)
+        env_d = self._get_env_d(
+            broot=self["BROOT"], eroot=self["EROOT"], tolerant=self._tolerant
+        )
+        for k in self._env_d_blacklist:
+            env_d.pop(k, None)
+        self.configdict["env.d"].update(env_d)
 
     def regenerate(self, useonly=0, use_cache=None):
         """

Reply via email to