--- ./prefix-portage-2.2.8/pym/_emerge/depgraph.py.orig 2014-04-04 09:21:13.093484137 +0200 +++ ./prefix-portage-2.2.8/pym/_emerge/depgraph.py 2014-04-04 13:08:28.193677557 +0200 @@ -23,6 +23,7 @@ from portage.dep import Atom, best_match_to_list, extract_affecting_use, \ check_required_use, human_readable_required_use, match_from_list, \ _repo_separator +from portage.dep.dep_check import ro_selected from portage.dep._slot_operator import ignore_built_slot_operator_deps from portage.eapi import eapi_has_strong_blocks, eapi_has_required_use, \ _get_eapi_attrs @@ -2231,7 +2232,7 @@ 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)), @@ -2239,15 +2240,18 @@ 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="" + if dep_type and edepend[dep_type]: + dep_string = edepend[dep_type] if not dep_string: continue if debug: @@ -2285,7 +2289,7 @@ 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) @@ -2300,14 +2304,14 @@ if not self._add_pkg_dep_string( pkg, dep_root, dep_priority, dep_string, - allow_unsatisfied): + allow_unsatisfied, dep_type=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 @@ -2352,7 +2356,7 @@ 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): depth = pkg.depth + 1 deep = self._dynamic_config.myparams.get("deep", 0) recurse_satisfied = deep is True or depth <= deep @@ -2372,7 +2376,7 @@ 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) @@ -2670,7 +2674,7 @@ 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 @@ -2679,33 +2683,33 @@ 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 @@ -3513,7 +3517,7 @@ **portage._native_kwargs(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.""" @@ -3536,6 +3540,12 @@ 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: @@ -3564,7 +3574,7 @@ 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 @@ -3631,6 +3641,7 @@ 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): @@ -3906,6 +3917,37 @@ def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None, check_backtrack=False, check_autounmask_breakage=False, show_req_use=None): + # 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 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")) + """ When check_backtrack=True, no output is produced and the method either returns or raises _backtrack_mask if --- ./prefix-portage-2.2.8/pym/portage/exception.py.orig 2014-04-04 10:55:45.613564604 +0200 +++ ./prefix-portage-2.2.8/pym/portage/exception.py 2014-04-04 11:00:19.483568489 +0200 @@ -197,3 +197,6 @@ class UntrustedSignature(SignatureException): """Signature was not certified to the desired security level""" +class InvalidReadonlyERoot(PortageException): + """Readonly EROOT definition string in make.conf invalid.""" + --- ./prefix-portage-2.2.8/pym/portage/dep/dep_check.py.orig 2014-04-04 10:13:37.033528735 +0200 +++ ./prefix-portage-2.2.8/pym/portage/dep/dep_check.py 2014-04-04 10:55:27.403564346 +0200 @@ -261,6 +261,95 @@ __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_roots): + # 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_roots.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_vartrees.has_key(ro_root): + # 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. @@ -571,7 +660,7 @@ 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). @@ -667,6 +756,14 @@ 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) --- ./prefix-portage-2.2.8/pym/portage/package/ebuild/config.py.orig 2014-04-04 10:57:48.593566349 +0200 +++ ./prefix-portage-2.2.8/pym/portage/package/ebuild/config.py 2014-04-04 10:59:37.163567889 +0200 @@ -296,6 +296,7 @@ self.features = features_set(self) self.features._features = copy.deepcopy(clone.features._features) self._features_overrides = copy.deepcopy(clone._features_overrides) + self.readonly_roots = copy.deepcopy(clone.readonly_roots) #Strictly speaking _license_manager is not immutable. Users need to ensure that #extract_global_changes() is called right after __init__ (if at all). @@ -885,6 +886,51 @@ 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_roots = {} + my_ro_current_instance = config_root + my_ro_widest_depset = set(['DEPEND', 'RDEPEND', 'PDEPEND']) + + while True: + my_ro_current_make_conf_file = os.path.join(my_ro_current_instance,MAKE_CONF_FILE.lstrip(os.path.sep)) + + if os.path.exists(my_ro_current_make_conf_file): + my_ro_cfg = getconfig(my_ro_current_make_conf_file, tolerant=1) + + if my_ro_cfg.has_key("READONLY_EPREFIX"): + if not my_ro_cfg["READONLY_EPREFIX"].find(":"): + raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s" % (my_ro_current_make_conf_file)) + + (my_ro_cfg_root,my_ro_cfg_root_deps) = my_ro_cfg["READONLY_EPREFIX"].rsplit(":",1) + + if not os.path.exists(my_ro_cfg_root): + raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EPREFIX in %s: path does not exist!" % (my_ro_current_instance)) + + if self.readonly_roots.has_key(my_ro_cfg_root): + raise portage.exception.InvalidReadonlyERoot("ERROR: circular READONLY_EPREFIX's in %s. %s already checked for %s" % (my_ro_current_make_conf_file, my_ro_cfg_root, self.readonly_roots[my_ro_cfg_root])) + + if my_ro_cfg_root == config_root: + raise portage.exception.InvalidReadonlyERoot("ERROR: cannot add this instance as READONLY_EPREFIX in %s." % (my_ro_current_make_conf_file)) + + # 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. + my_ro_widest_depset = set(my_ro_cfg_root_deps.split(",")) & my_ro_widest_depset + self.readonly_roots[my_ro_cfg_root] = my_ro_widest_depset + my_ro_current_instance = my_ro_cfg_root + continue + + break + + for k in self._case_insensitive_vars: if k in self: self[k] = self[k].lower() @@ -2658,6 +2704,10 @@ if not eapi_exports_merge_type(eapi): mydict.pop("MERGE_TYPE", None) + # populate with PORTAGE_READONLY_EPREFIXES + if self.readonly_roots and len(self.readonly_roots) > 0: + mydict["PORTAGE_READONLY_EPREFIXES"] = ':'.join(self.readonly_roots) + # 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 --- ./prefix-portage-2.2.8/pym/portage/dbapi/vartree.py.orig 2014-04-04 10:03:08.083519813 +0200 +++ ./prefix-portage-2.2.8/pym/portage/dbapi/vartree.py 2014-04-04 12:56:46.113667598 +0200 @@ -181,8 +181,20 @@ self._counter_path = os.path.join(self._eroot, CACHE_PATH, "counter") + plibreg_path = os.path.join(self.root, 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 = os.path.join(self.root, self._aux_cache_filename.replace(EPREFIX, "")) + self._counter_path = os.path.join(self.root, self._counter_path.replace(EPREFIX, "")) + plibreg_path = os.path.join(self.root, plibreg_path.replace(EPREFIX, "")) + self._plib_registry = PreservedLibsRegistry(settings["ROOT"], - os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry")) + os.path.join(self._eroot, plibreg_path)) self._linkmap = LinkageMap(self) chost = self.settings.get('CHOST') if not chost: @@ -212,6 +224,9 @@ # 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 @@ -528,10 +543,14 @@ return list(self._iter_match(mydep, self.cp_list(mydep.cp, use_cache=use_cache))) try: + _tmp_path = os.path.join(self.root, VDB_PATH, mycat) + + if self._kill_eprefix: + _tmp_path = os.path.join(self.root, _tmp_path.replace(EPREFIX, "")) 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 @@ -1319,7 +1338,7 @@ 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=False): if settings is None: settings = portage.settings @@ -1338,6 +1357,7 @@ DeprecationWarning, stacklevel=2) self.settings = settings + self._kill_eprefix = kill_eprefix self.dbapi = vardbapi(settings=settings, vartree=self) self.populated = 1 @@ -1377,6 +1397,10 @@ raise except Exception as e: mydir = self.dbapi.getpath(mycpv) + + if self._kill_eprefix: + mydir = os.path.join(self.root, mydir.replace(EPREFIX, "")) + writemsg(_("\nParse Error reading PROVIDE and USE in '%s'\n") % mydir, noiselevel=-1) if mylines: --- ./prefix-portage-2.2.8/bin/ebuild.sh.orig 2014-04-04 08:40:57.733449874 +0200 +++ ./prefix-portage-2.2.8/bin/ebuild.sh 2014-04-04 09:20:44.603483733 +0200 @@ -107,6 +107,83 @@ # Unset some variables that break things. unset GZIP BZIP BZIP2 CDPATH GREP_OPTIONS GREP_COLOR GLOBIGNORE +if [[ -n "${PORTAGE_READONLY_EPREFIXES}" ]]; then + new_PATH=${PATH} + + prefixes="${PORTAGE_READONLY_EPREFIXES}:${EPREFIX}" + + # build up a PATH for the current environment. the path has to + # contain all the paths the start with $EPREFIX _first_, and + # after that, all the others. We have to re-order the new_PATH, + # so that EPREFIX paths move to the front. after that, the paths + # of all parent prefixes are added, and finally, after that, the + # paths not containing any prefix are added. + + save_IFS=$IFS + IFS=':' + pth_pfx= + pth_nopfx= + for pth in ${new_PATH}; do + IFS=$save_IFS + if [[ "${pth#${EPREFIX}}" == "${pth}" ]]; then + [[ ":${pth_nopfx}:" == *":${pth}:"* ]] && continue + if [[ -z "${pth_nopfx}" ]]; then + pth_nopfx="${pth}" + else + pth_nopfx="${pth_nopfx}:${pth}" + fi + else + [[ ":${pth_pfx}:" == *":${pth}:"* ]] && continue + if [[ -z "${pth_pfx}" ]]; then + pth_pfx="${pth}" + else + pth_pfx="${pth_pfx}:${pth}" + fi + fi + done + IFS=$save_IFS + + new_PATH= + + save_IFS=$IFS + IFS=':' + for eroot in ${prefixes}; do + IFS=$save_IFS + if [[ -f ${eroot}/usr/share/portage/config/make.globals ]]; then + # ok, there is a portage instance installed in this prefix, + # so we can ask (politely) for the DEFAULT_PATH of it :) + + defpath="$(. ${eroot}/etc/make.globals && echo $DEFAULT_PATH)" + okpath= + save_IFS2=$IFS + IFS=':' + for p in $defpath; do + IFS=$save_IFS2 + # we have that one already... + [[ ":${new_PATH}:" == *":$p:"* ]] && continue + # we skip paths, that are outside our prefix ... + [[ "${p#${eroot}}" == "${p}" ]] && continue + if [[ -z "${okpath}" ]]; then + okpath="${p}" + else + okpath="${okpath}:${p}" + fi + done + IFS=$save_IFS2 + + new_PATH="${okpath}:${new_PATH}" + else + # no portage installed in this prefix. this means we have to + # somehow fiddle together a sane path for that prefix for at + # least the standard things to work. + new_PATH="${eroot}/usr/bin:${eroot}/usr/sbin:${eroot}/bin:${eroot}/sbin:${new_PATH}" + fi + done + IFS=$save_IFS + + export PATH=${pth_pfx}:$new_PATH:${pth_nopfx} +fi + [[ $PORTAGE_QUIET != "" ]] && export PORTAGE_QUIET # sandbox support functions; defined prior to profile.bashrc srcing, since the profile might need to add a default exception (/usr/lib64/conftest fex)