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): """