commit: 4b7e34b956d910b3723354b9351d21d8f010788f Author: Michael Haubenwallner <haubi <AT> gentoo <DOT> org> AuthorDate: Mon Sep 18 14:08:44 2017 +0000 Commit: Michael Haubenwallner <haubi <AT> gentoo <DOT> org> CommitDate: Mon Sep 18 14:08:44 2017 +0000 URL: https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=4b7e34b9
sys-apps/portage: bump prefix-chaining.patch Package-Manager: Portage-2.3.8-prefix, Repoman-2.3.3 .../files/portage-2.3.8-prefix-chaining.patch | 927 +++++++++++++++++++++ sys-apps/portage/portage-2.3.8.ebuild | 2 +- 2 files changed, 928 insertions(+), 1 deletion(-) diff --git a/sys-apps/portage/files/portage-2.3.8-prefix-chaining.patch b/sys-apps/portage/files/portage-2.3.8-prefix-chaining.patch new file mode 100644 index 0000000000..036d022191 --- /dev/null +++ b/sys-apps/portage/files/portage-2.3.8-prefix-chaining.patch @@ -0,0 +1,927 @@ +From 448d210bb312ed0930763376d5182ebfbed1abd8 Mon Sep 17 00:00:00 2001 +From: Michael Haubenwallner <[email protected]> +Date: Thu, 23 Mar 2017 13:52:32 +0100 +Subject: [PATCH] add prefix-chaining support + +--- + bin/install-qa-check.d/05prefix | 30 ++++++- + bin/phase-helpers.sh | 28 ++++++ + pym/_emerge/actions.py | 6 +- + pym/_emerge/depgraph.py | 51 ++++++----- + pym/_emerge/resolver/output.py | 40 ++++++++- + pym/portage/_sets/__init__.py | 5 ++ + pym/portage/const.py | 6 ++ + pym/portage/dbapi/vartree.py | 34 ++++++-- + pym/portage/dep/dep_check.py | 99 +++++++++++++++++++++- + .../package/ebuild/_config/LocationsManager.py | 3 + + pym/portage/package/ebuild/config.py | 62 ++++++++++++++ + pym/portage/package/ebuild/doebuild.py | 24 +++++- + pym/portage/package/ebuild/fetch.py | 4 + + pym/portage/sync/controller.py | 27 +++--- + pym/portage/util/_dyn_libs/LinkageMapELF.py | 4 +- + 15 files changed, 376 insertions(+), 47 deletions(-) + +diff --git a/bin/install-qa-check.d/05prefix b/bin/install-qa-check.d/05prefix +index 32561e2..0c11473 100644 +--- a/bin/install-qa-check.d/05prefix ++++ b/bin/install-qa-check.d/05prefix +@@ -79,16 +79,42 @@ install_qa_check_prefix() { + # unprefixed shebang, is the script directly in $PATH or an init + # script? + if [[ ":${PATH}:${EPREFIX}/etc/init.d:" == *":${fp}:"* ]] ; then +- if [[ -e ${EROOT}${line[0]} || -e ${ED}${line[0]} ]] ; then ++ all_epfs="$PORTAGE_READONLY_EPREFIXES:$EPREFIX:$EROOT:$ED" ++ save_IFS=$IFS ++ IFS=: ++ epfs=( $all_epfs ) ++ IFS=$save_IFS ++ ++ found= ++ for x in "${epfs[@]}"; do ++ [[ -z "${x}" ]] && continue ++ check="${x}${line[0]}" ++ ++ # might already contain a prefix ++ if [[ "${line[0]}" == "${x}"* ]]; then ++ check="${line[0]}" ++ fi ++ ++ if [[ -e ${check} ]]; then ++ found="${check}" ++ fi ++ done ++ ++ if [[ -n ${found} ]] ; then + # is it unprefixed, but we can just fix it because a + # prefixed variant exists + eqawarn "prefixing shebang of ${fn#${D}}" ++ ++ if [[ ${found} == "${ED}"* || ${found} == "${EROOT}"* ]]; then ++ found="${EPREFIX}${line[0]}" ++ fi ++ + # statement is made idempotent on purpose, because + # symlinks may point to the same target, and hence the + # same real file may be sedded multiple times since we + # read the shebangs in one go upfront for performance + # reasons +- sed -i -e '1s:^#! \?'"${line[0]}"':#!'"${EPREFIX}"${line[0]}':' "${rf}" ++ sed -i -e '1s:^#! \?'"${line[0]}"':#!'"${found}"':' "${rf}" + continue + else + # this is definitely wrong: script in $PATH and invalid shebang +diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh +index b28fd92..dcfd263 100644 +--- a/bin/phase-helpers.sh ++++ b/bin/phase-helpers.sh +@@ -867,6 +867,10 @@ has_version() { + "${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" has_version "${eroot}" "${atom}" + fi + local retval=$? ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ ${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' has_version '${READONLY_EPREFIX%:*}' '${atom}'" ++ retval=$? ++ fi + case "${retval}" in + 0|1) + return ${retval} +@@ -926,6 +930,10 @@ best_version() { + "${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" best_version "${eroot}" "${atom}" + fi + local retval=$? ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ ${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' best_version '${READONLY_EPREFIX%:*}' '${atom}'" ++ retval=$? ++ fi + case "${retval}" in + 0|1) + return ${retval} +@@ -1166,6 +1174,10 @@ if ___eapi_has_master_repositories; then + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" master_repositories "${EROOT}" "${repository}") + fi + retval=$? ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' master_repositories '${READONLY_EPREFIX%:*}' '${repository}'") ++ retval=$? ++ fi + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) +@@ -1197,6 +1209,10 @@ if ___eapi_has_repository_path; then + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" get_repo_path "${EROOT}" "${repository}") + fi + retval=$? ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' repository_path '${READONLY_EPREFIX%:*}' '${repository}'") ++ retval=$? ++ fi + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) +@@ -1227,6 +1243,10 @@ if ___eapi_has_available_eclasses; then + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" available_eclasses "${EROOT}" "${repository}") + fi + retval=$? ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' available_eclasses '${READONLY_EPREFIX%:*}' '${repository}'") ++ retval=$? ++ fi + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) +@@ -1257,6 +1277,10 @@ if ___eapi_has_eclass_path; then + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" eclass_path "${EROOT}" "${repository}" "${eclass}") + fi ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ output=$(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' eclass_path '${READONLY_EPREFIX%:*}' '${repository}' '${eclass}'") ++ retval=$? ++ fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in +@@ -1288,6 +1312,10 @@ if ___eapi_has_license_path; then + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" license_path "${EROOT}" "${repository}" "${license}") + fi ++ if [[ ${retval} -eq 1 && -n ${READONLY_EPREFIX} ]]; then ++ output=(${SHELL} -c "EPREFIX='${READONLY_EPREFIX%:*}' EPYTHON= '${PORTAGE_BIN_PATH}/ebuild-helpers/portageq' license_path '${READONLY_EPREFIX%:*}' '${repository}' '${license}'") ++ retval=$? ++ fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in +diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py +index 1d37d0e..2b185ef 100644 +--- a/pym/_emerge/actions.py ++++ b/pym/_emerge/actions.py +@@ -39,7 +39,7 @@ from portage import os + from portage import shutil + from portage import eapi_is_supported, _encodings, _unicode_decode + from portage.cache.cache_errors import CacheError +-from portage.const import EPREFIX ++from portage.const import EPREFIX, BPREFIX + from portage.const import GLOBAL_CONFIG_PATH, VCS_DIRS, _DEPCLEAN_LIB_CHECK_DEFAULT + from portage.const import SUPPORTED_BINPKG_FORMATS, TIMESTAMP_FORMAT + from portage.dbapi.dep_expand import dep_expand +@@ -65,6 +65,7 @@ from portage.util.SlotObject import SlotObject + from portage.util._async.run_main_scheduler import run_main_scheduler + from portage.util._async.SchedulerInterface import SchedulerInterface + from portage.util._eventloop.global_event_loop import global_event_loop ++from portage.util._path import exists_raise_eaccess + from portage._global_updates import _global_updates + from portage.sync.old_tree_timestamp import old_tree_timestamp_warn + from portage.localization import _ +@@ -2659,6 +2660,9 @@ def missing_sets_warning(root_config, missing_sets): + if portage.const.EPREFIX: + global_config_path = os.path.join(portage.const.EPREFIX, + portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX: ++ global_config_path = os.path.join(portage.const.BPREFIX, ++ portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep)) + msg.append(" This usually means that '%s'" % \ + (os.path.join(global_config_path, "sets/portage.conf"),)) + msg.append(" is missing or corrupt.") +diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py +index 76f1370..8f681d6 100644 +--- a/pym/_emerge/depgraph.py ++++ b/pym/_emerge/depgraph.py +@@ -3180,23 +3180,24 @@ class depgraph(object): + edepend["HDEPEND"] = "" + + deps = ( +- (depend_root, edepend["DEPEND"], ++ (depend_root, "DEPEND", + self._priority(buildtime=True, + optional=(pkg.built or ignore_depend_deps), + ignored=ignore_depend_deps)), +- (self._frozen_config._running_root.root, edepend["HDEPEND"], ++ (self._frozen_config._running_root.root, "HDEPEND", + self._priority(buildtime=True, + optional=(pkg.built or ignore_hdepend_deps), + ignored=ignore_hdepend_deps)), +- (myroot, edepend["RDEPEND"], ++ (myroot, "RDEPEND", + self._priority(runtime=True)), +- (myroot, edepend["PDEPEND"], ++ (myroot, "PDEPEND", + self._priority(runtime_post=True)) + ) + + debug = "--debug" in self._frozen_config.myopts + +- for dep_root, dep_string, dep_priority in deps: ++ for dep_root, dep_type, dep_priority in deps: ++ dep_string = edepend[dep_type] + if not dep_string: + continue + if debug: +@@ -3234,7 +3235,7 @@ class depgraph(object): + + try: + dep_string = list(self._queue_disjunctive_deps( +- pkg, dep_root, dep_priority, dep_string)) ++ pkg, dep_root, dep_priority, dep_string, dep_type)) + except portage.exception.InvalidDependString as e: + if pkg.installed: + self._dynamic_config._masked_installed.add(pkg) +@@ -3249,14 +3250,14 @@ class depgraph(object): + + if not self._add_pkg_dep_string( + pkg, dep_root, dep_priority, dep_string, +- allow_unsatisfied): ++ allow_unsatisfied, dep_type): + return 0 + + self._dynamic_config._traversed_pkg_deps.add(pkg) + return 1 + + def _add_pkg_dep_string(self, pkg, dep_root, dep_priority, dep_string, +- allow_unsatisfied): ++ allow_unsatisfied, dep_type=None): + _autounmask_backup = self._dynamic_config._autounmask + if dep_priority.optional or dep_priority.ignored: + # Temporarily disable autounmask for deps that +@@ -3265,7 +3266,7 @@ class depgraph(object): + try: + return self._wrapped_add_pkg_dep_string( + pkg, dep_root, dep_priority, dep_string, +- allow_unsatisfied) ++ allow_unsatisfied, dep_type) + finally: + self._dynamic_config._autounmask = _autounmask_backup + +@@ -3301,7 +3302,7 @@ class depgraph(object): + not slot_operator_rebuild + + def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority, +- dep_string, allow_unsatisfied): ++ dep_string, allow_unsatisfied, dep_type=None): + if isinstance(pkg.depth, int): + depth = pkg.depth + 1 + else: +@@ -3325,7 +3326,7 @@ class depgraph(object): + try: + selected_atoms = self._select_atoms(dep_root, + dep_string, myuse=self._pkg_use_enabled(pkg), parent=pkg, +- strict=strict, priority=dep_priority) ++ strict=strict, priority=dep_priority, dep_type=dep_type) + except portage.exception.InvalidDependString: + if pkg.installed: + self._dynamic_config._masked_installed.add(pkg) +@@ -3623,7 +3624,7 @@ class depgraph(object): + child_pkgs.sort() + yield (atom, child_pkgs[-1]) + +- def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct): ++ def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct, dep_type=None): + """ + Queue disjunctive (virtual and ||) deps in self._dynamic_config._dep_disjunctive_stack. + Yields non-disjunctive deps. Raises InvalidDependString when +@@ -3632,33 +3633,33 @@ class depgraph(object): + for x in dep_struct: + if isinstance(x, list): + if x and x[0] == "||": +- self._queue_disjunction(pkg, dep_root, dep_priority, [x]) ++ self._queue_disjunction(pkg, dep_root, dep_priority, [x], dep_type) + else: + for y in self._queue_disjunctive_deps( +- pkg, dep_root, dep_priority, x): ++ pkg, dep_root, dep_priority, x, dep_type): + yield y + else: + # Note: Eventually this will check for PROPERTIES=virtual + # or whatever other metadata gets implemented for this + # purpose. + if x.cp.startswith('virtual/'): +- self._queue_disjunction(pkg, dep_root, dep_priority, [x]) ++ self._queue_disjunction(pkg, dep_root, dep_priority, [x], dep_type) + else: + yield x + +- def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct): ++ def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct, dep_type=None): + self._dynamic_config._dep_disjunctive_stack.append( +- (pkg, dep_root, dep_priority, dep_struct)) ++ (pkg, dep_root, dep_priority, dep_struct, dep_type)) + + def _pop_disjunction(self, allow_unsatisfied): + """ + Pop one disjunctive dep from self._dynamic_config._dep_disjunctive_stack, and use it to + populate self._dynamic_config._dep_stack. + """ +- pkg, dep_root, dep_priority, dep_struct = \ ++ pkg, dep_root, dep_priority, dep_struct, dep_type = \ + self._dynamic_config._dep_disjunctive_stack.pop() + if not self._add_pkg_dep_string( +- pkg, dep_root, dep_priority, dep_struct, allow_unsatisfied): ++ pkg, dep_root, dep_priority, dep_struct, allow_unsatisfied, dep_type): + return 0 + return 1 + +@@ -4511,7 +4512,7 @@ class depgraph(object): + return self._select_atoms_highest_available(*pargs, **kwargs) + + def _select_atoms_highest_available(self, root, depstring, +- myuse=None, parent=None, strict=True, trees=None, priority=None): ++ myuse=None, parent=None, strict=True, trees=None, priority=None, dep_type=None): + """This will raise InvalidDependString if necessary. If trees is + None then self._dynamic_config._filtered_trees is used.""" + +@@ -4534,6 +4535,13 @@ class depgraph(object): + pkgsettings = self._frozen_config.pkgsettings[root] + if trees is None: + trees = self._dynamic_config._filtered_trees ++ ++ # this one is needed to guarantee good readonly root ++ # resolution display in the merge list. required since ++ # parent (below) can be None ++ trees[root]["disp_parent"] = parent ++ ++ + mytrees = trees[root] + atom_graph = digraph() + if True: +@@ -4565,7 +4573,7 @@ class depgraph(object): + + mycheck = portage.dep_check(depstring, None, + pkgsettings, myuse=myuse, +- myroot=root, trees=trees) ++ myroot=root, trees=trees, dep_type=dep_type) + finally: + # restore state + self._dynamic_config._autounmask = _autounmask_backup +@@ -4641,6 +4649,7 @@ class depgraph(object): + continue + node_stack.append((child_node, node, child_atom)) + ++ trees[root].pop("disp_parent") + return selected_atoms + + def _expand_virt_from_graph(self, root, atom): +diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py +index e993ce1..32a942c 100644 +--- a/pym/_emerge/resolver/output.py ++++ b/pym/_emerge/resolver/output.py +@@ -22,11 +22,12 @@ from portage.localization import localized_size + from portage.package.ebuild.config import _get_feature_flags + from portage.package.ebuild._spawn_nofetch import spawn_nofetch + from portage.output import ( blue, colorize, create_color_func, +- darkblue, darkgreen, green, nc_len, teal) ++ darkblue, darkgreen, green, nc_len, teal, yellow, turquoise) + bad = create_color_func("BAD") + from portage._sets.base import InternalPackageSet + from portage.util import writemsg_stdout + from portage.versions import best, cpv_getversion ++from portage.dep.dep_check import ro_selected + + from _emerge.Blocker import Blocker + from _emerge.create_world_atom import create_world_atom +@@ -563,6 +564,42 @@ class Display(object): + writemsg_stdout("%s\n" % (pkg,), noiselevel=-1) + return + ++ def print_readonly_prefix(self): ++ """Performs the actual output printing for the readonly prefix ++ information stuff ++ """ ++ out = sys.stdout ++ ++ # print readonly selected packages ++ if len(ro_selected) > 0: ++ out.write("\n%s\n\n" % (darkgreen("Packages resolved from readonly installations:"))) ++ ++ ro_mismatch_warning = False ++ ro_dupcheck = [] ++ for x in ro_selected: ++ tmp_type = x["type"].replace("END","") ++ while len(tmp_type) < 4: ++ tmp_type += " " ++ if x["parent"] and str(x["atom"]) not in ro_dupcheck: ++ out.write("[%s %s] %s %s %s (%s by %s)" % (teal("readonly"), ++ green(tmp_type), green(str(x["matches"][0])), yellow("from"), ++ blue(x["ro_root"]), turquoise(str(x["atom"])), green(x["parent"].cpv))) ++ ++ ro_dupcheck.append(str(x["atom"])) ++ ++ if x["host_mismatch"]: ++ ro_mismatch_warning = True ++ out.write(" %s\n" % (red("**"))) ++ else: ++ out.write("\n") ++ ++ if ro_mismatch_warning: ++ out.write("\n%s:" % (red("**"))) ++ out.write(yellow(" WARNING: packages marked with ** have been resolved as a\n")) ++ out.write(yellow(" runtime dependency, but the CHOST variable for the parent\n")) ++ out.write(yellow(" and dependency package don't match. This could cause link\n")) ++ out.write(yellow(" errors. It is recommended to use RDEPEND READONLY_EPREFIX's\n")) ++ out.write(yellow(" only with matching CHOST portage instances.\n")) + + def print_verbose(self, show_repos): + """Prints the verbose output to std_out +@@ -913,6 +950,7 @@ class Display(object): + show_repos = self.quiet_repo_display and repoadd_set and repoadd_set != set(["0"]) + + # now finally print out the messages ++ self.print_readonly_prefix() + self.print_messages(show_repos) + self.print_blockers() + if self.conf.verbosity == 3: +diff --git a/pym/portage/_sets/__init__.py b/pym/portage/_sets/__init__.py +index 2c9bf97..6a27842 100644 +--- a/pym/portage/_sets/__init__.py ++++ b/pym/portage/_sets/__init__.py +@@ -21,6 +21,7 @@ from portage.const import _ENABLE_SET_CONFIG + from portage.exception import PackageSetNotFound + from portage.localization import _ + from portage.util import writemsg_level ++from portage.util._path import exists_raise_eaccess + from portage.util.configparser import (SafeConfigParser, + NoOptionError, ParsingError, read_configs) + +@@ -281,6 +282,10 @@ def load_default_config(settings, trees): + if portage.const.EPREFIX: + global_config_path = os.path.join(portage.const.EPREFIX, + GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX: ++ global_config_path = os.path.join(portage.const.BPREFIX, ++ GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ + vcs_dirs = [_unicode_encode(x, encoding=_encodings['fs']) for x in VCS_DIRS] + def _getfiles(): + for path, dirs, files in os.walk(os.path.join(global_config_path, "sets")): +diff --git a/pym/portage/const.py b/pym/portage/const.py +index a0ad1f9..4f14bc8 100644 +--- a/pym/portage/const.py ++++ b/pym/portage/const.py +@@ -189,6 +189,7 @@ SUPPORTED_FEATURES = frozenset([ + "notitles", + "parallel-fetch", + "parallel-install", ++ "prefix-chaining", + "prelink-checksums", + "preserve-libs", + "protect-owned", +@@ -266,6 +267,11 @@ MANIFEST2_IDENTIFIERS = ("AUX", "MISC", "DIST", "EBUILD") + #EPREFIX = "" + # END PREFIX LOCAL + ++BPREFIX = EPREFIX ++ ++# --prefix commandline arg always rules, ends up in os.environ["EPREFIX"] ++if "EPREFIX" in os.environ: ++ os.environ["PORTAGE_OVERRIDE_EPREFIX"] = os.environ["EPREFIX"] + # pick up EPREFIX from the environment if set + if "PORTAGE_OVERRIDE_EPREFIX" in os.environ: + EPREFIX = os.environ["PORTAGE_OVERRIDE_EPREFIX"] +diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py +index 0006bc5..2ea5f16 100644 +--- a/pym/portage/dbapi/vartree.py ++++ b/pym/portage/dbapi/vartree.py +@@ -194,8 +194,19 @@ class vardbapi(dbapi): + self._counter_path = os.path.join(self._eroot, + CACHE_PATH, "counter") + +- self._plib_registry = PreservedLibsRegistry(settings["ROOT"], +- os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry")) ++ plibreg_path = os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry") ++ ++ if vartree: ++ self._kill_eprefix = vartree._kill_eprefix ++ else: ++ self._kill_eprefix = False ++ ++ if self._kill_eprefix: ++ self._aux_cache_filename = self._aux_cache_filename.replace(EPREFIX, "") ++ self._counter_path = self._counter_path.replace(EPREFIX, "") ++ plibreg_path = plibreg_path.replace(EPREFIX, "") ++ ++ self._plib_registry = PreservedLibsRegistry(settings["ROOT"], plibreg_path) + self._linkmap = LinkageMap(self) + chost = self.settings.get('CHOST') + if not chost: +@@ -236,6 +247,9 @@ class vardbapi(dbapi): + # This is an optimized hotspot, so don't use unicode-wrapped + # os module and don't use os.path.join(). + rValue = self._eroot + VDB_PATH + _os.sep + mykey ++ if self._kill_eprefix: ++ rValue = rValue.replace(EPREFIX, "") ++ + if filename is not None: + # If filename is always relative, we can do just + # rValue += _os.sep + filename +@@ -499,6 +513,9 @@ class vardbapi(dbapi): + returnme = [] + basepath = os.path.join(self._eroot, VDB_PATH) + os.path.sep + ++ if self._kill_eprefix: ++ basepath = os.path.join(self.root, basepath.replace(EPREFIX, "")) ++ + if use_cache: + from portage import listdir + else: +@@ -595,11 +612,17 @@ class vardbapi(dbapi): + del self.matchcache[mycat] + return list(self._iter_match(mydep, + self.cp_list(mydep.cp, use_cache=use_cache))) ++ ++ _tmp_path = os.path.join(self._eroot, VDB_PATH, mycat) ++ ++ if self._kill_eprefix: ++ _tmp_path = _tmp_path.replace(EPREFIX, "") ++ + try: + if sys.hexversion >= 0x3030000: +- curmtime = os.stat(os.path.join(self._eroot, VDB_PATH, mycat)).st_mtime_ns ++ curmtime = os.stat(_tmp_path).st_mtime_ns + else: +- curmtime = os.stat(os.path.join(self._eroot, VDB_PATH, mycat)).st_mtime ++ curmtime = os.stat(_tmp_path).st_mtime + except (IOError, OSError): + curmtime=0 + +@@ -1410,7 +1433,7 @@ class vardbapi(dbapi): + class vartree(object): + "this tree will scan a var/db/pkg database located at root (passed to init)" + def __init__(self, root=None, virtual=DeprecationWarning, categories=None, +- settings=None): ++ settings=None, kill_eprefix=None): + + if settings is None: + settings = portage.settings +@@ -1428,6 +1451,7 @@ class vartree(object): + " constructor is unused", + DeprecationWarning, stacklevel=2) + ++ self._kill_eprefix = kill_eprefix + self.settings = settings + self.dbapi = vardbapi(settings=settings, vartree=self) + self.populated = 1 +diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py +index 35caecc..f0dca8a 100644 +--- a/pym/portage/dep/dep_check.py ++++ b/pym/portage/dep/dep_check.py +@@ -255,6 +255,95 @@ class _dep_choice(SlotObject): + __slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available', + 'all_installed_slots') + ++ro_trees={} ++ro_vartrees={} ++ro_selected=[] ++ ++def dep_match_readonly_roots(settings, atom, dep_type, parent=None): ++ if len(ro_trees) < len(settings.readonly_prefixes): ++ # MDUFT: create additional vartrees for every readonly root here. ++ # the ro_vartrees instances are created below as they are needed to ++ # avoid reading vartrees of portage instances which aren't required ++ # while resolving this dependencies. ++ for type in ("DEPEND","RDEPEND", "PDEPEND"): ++ ro_trees[type] = [] ++ ++ for ro_root, ro_dep_types in settings.readonly_prefixes.items(): ++ if type in ro_dep_types: ++ ro_trees[type].append(ro_root) ++ ++ if len(ro_trees) == 0: ++ return [] ++ ++ matches = [] ++ ++ for ro_root in ro_trees[dep_type]: ++ if not ro_root in ro_vartrees: ++ # target_root=ro_root ok? or should it be the real target_root? ++ _tmp_settings = portage.config(config_root=ro_root, target_root=ro_root, ++ config_incrementals=portage.const.INCREMENTALS) ++ ++ ro_vartrees[ro_root] = portage.vartree(root=ro_root, ++ categories=_tmp_settings.categories, ++ settings=_tmp_settings, kill_eprefix=True) ++ ++ ro_matches = ro_vartrees[ro_root].dbapi.match(atom) ++ ++ if ro_matches: ++ ro_host_mismatch = False ++ if dep_type is "RDEPEND": ++ # we need to assure binary compatability, so it needs to be ++ # the same CHOST! But how? for now i cannot do anything... ++ if parent and parent.metadata["CHOST"] != ro_vartrees[ro_root].settings.get("CHOST", ""): ++ # provocate a big fat warning in the list of external packages. ++ ro_host_mismatch = True ++ pass ++ ++ matches.append({ "ro_root": ro_root, "atom": atom, "matches": ro_matches, ++ "type": dep_type, "parent": parent, "host_mismatch": ro_host_mismatch }) ++ ++ return matches ++ ++def dep_wordreduce_readonly(reduced, unreduced, settings, dep_type, parent): ++ for mypos, token in enumerate(unreduced): ++ # recurse if it's a list. ++ if isinstance(reduced[mypos], list): ++ reduced[mypos] = dep_wordreduce_readonly(reduced[mypos], ++ unreduced[mypos], settings, dep_type, parent) ++ ++ # do nothing if it's satisfied already. ++ elif not reduced[mypos]: ++ ro_matches = dep_match_readonly_roots(settings, unreduced[mypos], dep_type, parent) ++ ++ if ro_matches: ++ # TODO: select a match if there are more than one? ++ # for now, the first match is taken... ++ ro_selected.append(ro_matches[0]) ++ reduced[mypos] = True ++ ++ return reduced ++ ++# this may be better placed somewhere else, but i put it here for now, to ++# keep all functions in the patch on one big heap. ++def readonly_pathmatch_any(settings, path): ++ path = path.lstrip('/') ++ # first try locally, and match that if it exists. ++ if os.path.exists(os.path.join(EPREFIX,path)): ++ return os.path.join(EPREFIX,path) ++ ++ # after that try all readonly roots where DEPEND is allowed. this makes ++ # sure that executing binaries is possible from there. ++ for ro_root, ro_deps in settings.readonly_roots.items(): ++ if "DEPEND" in ro_deps: ++ print(" --- checking %s --- " % (os.path.join(ro_root,path))) ++ if os.path.exists(os.path.join(ro_root,path)): ++ return os.path.join(ro_root,path) ++ break ++ ++ # as a fallback make the string the same as it was originally. ++ # even though this path doesn't exist. ++ return os.path.join(EPREFIX,path) ++ + def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): + """ + Takes an unreduced and reduced deplist and removes satisfied dependencies. +@@ -643,7 +732,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): + assert(False) # This point should not be reachable + + def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None, +- use_cache=1, use_binaries=0, myroot=None, trees=None): ++ use_cache=1, use_binaries=0, myroot=None, trees=None, dep_type=None): + """ + Takes a depend string, parses it, and selects atoms. + The myroot parameter is unused (use mysettings['EROOT'] instead). +@@ -741,6 +830,14 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None, + writemsg("mysplit: %s\n" % (mysplit), 1) + writemsg("mysplit2: %s\n" % (mysplit2), 1) + ++ if dep_type is not None: ++ mysplit2=dep_wordreduce_readonly(unreduced=mysplit[:], ++ reduced=mysplit2, settings=mysettings, ++ dep_type=dep_type, parent=trees[myroot].get("disp_parent")) ++ ++ writemsg("\n", 1) ++ writemsg("mysplit2 after readonly reduce: %s\n" % (mysplit2), 1) ++ + selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot, + use_binaries=use_binaries, trees=trees) + +diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py b/pym/portage/package/ebuild/_config/LocationsManager.py +index 55b8c08..32e969e 100644 +--- a/pym/portage/package/ebuild/_config/LocationsManager.py ++++ b/pym/portage/package/ebuild/_config/LocationsManager.py +@@ -307,6 +307,9 @@ class LocationsManager(object): + if portage.const.EPREFIX: + self.global_config_path = os.path.join(portage.const.EPREFIX, + GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ if not exists_raise_eaccess(self.global_config_path) and portage.const.BPREFIX: ++ self.global_config_path = os.path.join(portage.const.BPREFIX, ++ GLOBAL_CONFIG_PATH.lstrip(os.sep)) + + def set_port_dirs(self, portdir, portdir_overlay): + self.portdir = portdir +diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py +index b5fb42e..8cac700 100644 +--- a/pym/portage/package/ebuild/config.py ++++ b/pym/portage/package/ebuild/config.py +@@ -306,6 +306,7 @@ class config(object): + self.features = features_set(self) + self.features._features = copy.deepcopy(clone.features._features) + self._features_overrides = copy.deepcopy(clone._features_overrides) ++ self.readonly_prefixes = copy.deepcopy(clone.readonly_prefixes) + + #Strictly speaking _license_manager is not immutable. Users need to ensure that + #extract_global_changes() is called right after __init__ (if at all). +@@ -945,6 +946,63 @@ class config(object): + + self._validate_commands() + ++ # expand READONLY_EPREFIX to a list of all readonly portage instances ++ # all the way down to the last one. beware that ATM a deeper instance ++ # in the chain can provide more than the toplevel! this means that ++ # if you only inherit DEPENDS from one instance, that instance may ++ # inherit RDEPENDs from another one, making the top-level instance ++ # inherit RDEPENDs from there too - even if the intermediate prefix ++ # does not do this. ++ self.readonly_prefixes = {} ++ ro_cfg_root = config_root ++ ro_widest_depset = set(['DEPEND', 'RDEPEND', 'PDEPEND']) ++ ++ while ro_cfg_root: ++ ro_make_conf_paths = [ ++ os.path.join(ro_cfg_root, 'etc', 'make.conf'), ++ os.path.join(ro_cfg_root, MAKE_CONF_FILE) ++ ] ++ try: ++ if os.path.samefile(*ro_make_conf_paths): ++ ro_make_conf_paths.pop() ++ except OSError: ++ pass ++ ++ ro_cfg_root = None ++ for ro_make_conf in ro_make_conf_paths: ++ if not os.path.exists(ro_make_conf): ++ continue ++ ++ ro_cfg = getconfig(ro_make_conf, tolerant=True, allow_sourcing=True) ++ if not "READONLY_EPREFIX" in ro_cfg: ++ continue ++ ++ if not ro_cfg["READONLY_EPREFIX"].find(":"): ++ raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s" % (ro_make_conf)) ++ ++ if ro_cfg_root is not None: ++ raise portage.exception.InvalidReadonlyERoot("ERROR: duplicate READONLY_EPREFIX in %s and %s" % tuple(ro_make_conf_paths)) ++ ++ (ro_cfg_root,ro_cfg_root_deps) = ro_cfg["READONLY_EPREFIX"].rsplit(":",1) ++ ++ if not os.path.exists(ro_cfg_root): ++ raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s: %s does not exist!" % (ro_make_conf, ro_cfg_root)) ++ ++ if os.path.samefile(ro_cfg_root, config_root): ++ raise portage.exception.InvalidReadonlyERoot("ERROR: cannot add this instance (%s) as READONLY_EPREFIX in %s." % (ro_cfg_root, ro_make_conf)) ++ ++ if ro_cfg_root in self.readonly_prefixes: ++ raise portage.exception.InvalidReadonlyERoot("ERROR: circular READONLY_EPREFIX's in %s. %s already checked for %s" % (ro_make_conf, ro_cfg_root, self.readonly_prefixes[ro_cfg_root])) ++ ++ # intersect the widest depset with the current one to strip down ++ # the allowed dependency resolution to not be wider than the ++ # next higher one. this way we can prevent for a given prefix ++ # to resolve RDEPENDs from a prefix with a different CHOST that ++ # is a few levels deeper in the chain. ++ ro_widest_depset = set(ro_cfg_root_deps.split(",")) & ro_widest_depset ++ self.readonly_prefixes[ro_cfg_root] = ro_widest_depset ++ pass ++ + for k in self._case_insensitive_vars: + if k in self: + self[k] = self[k].lower() +@@ -2805,6 +2863,10 @@ class config(object): + if not eapi_exports_merge_type(eapi): + mydict.pop("MERGE_TYPE", None) + ++ # populate with PORTAGE_READONLY_EPREFIXES ++ if self.readonly_prefixes and len(self.readonly_prefixes) > 0: ++ mydict["PORTAGE_READONLY_EPREFIXES"] = ':'.join(self.readonly_prefixes) ++ + # Prefix variables are supported beginning with EAPI 3, or when + # force-prefix is in FEATURES, since older EAPIs would otherwise be + # useless with prefix configurations. This brings compatibility with +diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py +index c6d6133..f914091 100644 +--- a/pym/portage/package/ebuild/doebuild.py ++++ b/pym/portage/package/ebuild/doebuild.py +@@ -51,6 +51,7 @@ from portage import bsd_chflags, \ + unmerge, _encodings, _os_merge, \ + _shell_quote, _unicode_decode, _unicode_encode + from portage.const import EBUILD_SH_ENV_FILE, EBUILD_SH_ENV_DIR, \ ++ GLOBAL_CONFIG_PATH, \ + EBUILD_SH_BINARY, INVALID_ENV_FILE, MISC_SH_BINARY, PORTAGE_PYM_PACKAGES, EPREFIX, MACOSSANDBOX_PROFILE + from portage.data import portage_gid, portage_uid, secpass, \ + uid, userpriv_groups +@@ -72,6 +73,7 @@ from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs + from portage.process import find_binary + from portage.util import ( apply_recursive_permissions, + apply_secpass_permissions, ++ getconfig, + noiselimit, + shlex_split, + varexpand, +@@ -79,6 +81,7 @@ from portage.util import ( apply_recursive_permissions, + writemsg_stdout, + write_atomic + ) ++from portage.util._path import exists_raise_eaccess + from portage.util.cpuinfo import get_cpu_count + from portage.util.lafilefixer import rewrite_lafile + from portage.util.compression_probe import _compressors +@@ -241,8 +244,27 @@ def _doebuild_path(settings, eapi=None): + + for x in portage_bin_path: + path.append(os.path.join(x, "ebuild-helpers")) ++ ++ # PREFIX CHAINING: append default path for all prefixes involved ++ pfxs = [ eprefix ] ++ pfxs.extend(settings.readonly_prefixes) ++ for prefix in pfxs: ++ global_config_path = os.path.join(prefix, GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ make_globals_path = os.path.join(global_config_path, "make.globals") ++ if exists_raise_eaccess(make_globals_path): ++ expand_map = { "EPREFIX": prefix } ++ pxcfg = getconfig(make_globals_path, True, expand_map) ++ pxdefp = [x for x in pxcfg.get("DEFAULT_PATH", "").split(":") if x] ++ for x in pxdefp: ++ if x.startswith(prefix) and not x in path: ++ path.append(x) ++ else: ++ pxdefs = [prefix + "/usr/sbin", prefix + "/usr/bin", prefix + "/sbin", prefix + "/bin"] ++ path.extend(pxdefs) ++ # END PREFIX CHAINING ++ + path.extend(prerootpath) +- path.extend(defaultpath) ++ # path.extend(defaultpath) # PREFIX CHAINING appends the default path for involved prefixes above + path.extend(rootpath) + path.extend(extrapath) + # END PREFIX LOCAL +diff --git a/pym/portage/package/ebuild/fetch.py b/pym/portage/package/ebuild/fetch.py +index 265d0c9..2ec6ff4 100644 +--- a/pym/portage/package/ebuild/fetch.py ++++ b/pym/portage/package/ebuild/fetch.py +@@ -43,6 +43,7 @@ from portage.output import colorize, EOutput + from portage.util import apply_recursive_permissions, \ + apply_secpass_permissions, ensure_dirs, grabdict, shlex_split, \ + varexpand, writemsg, writemsg_level, writemsg_stdout ++from portage.util._path import exists_raise_eaccess + from portage.process import spawn + + _userpriv_spawn_kwargs = ( +@@ -874,6 +875,9 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, + global_config_path = GLOBAL_CONFIG_PATH + if portage.const.EPREFIX: + global_config_path = os.path.join(portage.const.EPREFIX, ++ GLOBAL_CONFIG_PATH.lstrip(os.sep)) ++ if not exists_raise_eaccess(global_config_path) and portage.const.BPREFIX: ++ global_config_path = os.path.join(portage.const.BPREFIX, + GLOBAL_CONFIG_PATH.lstrip(os.sep)) + + missing_file_param = False +diff --git a/pym/portage/sync/controller.py b/pym/portage/sync/controller.py +index 3bccf6f..cacd637 100644 +--- a/pym/portage/sync/controller.py ++++ b/pym/portage/sync/controller.py +@@ -94,19 +94,20 @@ class SyncManager(object): + self.module_controller = portage.sync.module_controller + self.module_names = self.module_controller.module_names + self.hooks = {} +- for _dir in ["repo.postsync.d", "postsync.d"]: +- postsync_dir = os.path.join(self.settings["PORTAGE_CONFIGROOT"], +- portage.USER_CONFIG_PATH, _dir) +- hooks = OrderedDict() +- for filepath in util._recursive_file_list(postsync_dir): +- name = filepath.split(postsync_dir)[1].lstrip(os.sep) +- if os.access(filepath, os.X_OK): +- hooks[filepath] = name +- else: +- writemsg_level(" %s %s hook: '%s' is not executable\n" +- % (warn("*"), _dir, _unicode_decode(name),), +- level=logging.WARN, noiselevel=2) +- self.hooks[_dir] = hooks ++ for _confroot in [self.settings["PORTAGE_CONFIGROOT"], portage.const.BPREFIX]: ++ for _dir in ["repo.postsync.d", "postsync.d"]: ++ postsync_dir = os.path.join(_confroot, ++ portage.USER_CONFIG_PATH, _dir) ++ hooks = OrderedDict() ++ for filepath in util._recursive_file_list(postsync_dir): ++ name = filepath.split(postsync_dir)[1].lstrip(os.sep) ++ if os.access(filepath, os.X_OK): ++ hooks[filepath] = name ++ else: ++ writemsg_level(" %s %s hook: '%s' is not executable\n" ++ % (warn("*"), _dir, _unicode_decode(name),), ++ level=logging.WARN, noiselevel=2) ++ self.hooks[_dir] = hooks + + def __getattr__(self, name): + if name == 'async': +diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py +index a063621..968fbd3 100644 +--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py ++++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py +@@ -12,7 +12,7 @@ from portage import _os_merge + from portage import _unicode_decode + from portage import _unicode_encode + from portage.cache.mappings import slot_dict_class +-from portage.const import EPREFIX ++from portage.const import BPREFIX + from portage.dep.soname.multilib_category import compute_multilib_category + from portage.exception import CommandNotFound, InvalidData + from portage.localization import _ +@@ -268,7 +268,7 @@ class LinkageMapELF(object): + continue + plibs.update((x, cpv) for x in items) + if plibs: +- args = [os.path.join(EPREFIX or "/", "usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"] ++ args = [os.path.join(BPREFIX or "/", "usr/bin/scanelf"), "-qF", "%a;%F;%S;%r;%n"] + args.extend(os.path.join(root, x.lstrip("." + os.sep)) \ + for x in plibs) + try: +-- +2.10.2 + diff --git a/sys-apps/portage/portage-2.3.8.ebuild b/sys-apps/portage/portage-2.3.8.ebuild index 60f0e15afe..07490db2e3 100644 --- a/sys-apps/portage/portage-2.3.8.ebuild +++ b/sys-apps/portage/portage-2.3.8.ebuild @@ -91,7 +91,7 @@ python_prepare_all() { epatch "${FILESDIR}"/${PN}-2.2.28-ebuildshell-r1.patch # 155161 use prefix-chaining && - epatch "${FILESDIR}"/${PN}-2.3.5-prefix-chaining.patch + epatch "${FILESDIR}"/${PN}-2.3.8-prefix-chaining.patch if use native-extensions; then printf "[build_ext]\nportage-ext-modules=true\n" >> \
