diff -ru portage.orig/bin/ebuild.sh portage/bin/ebuild.sh --- portage.orig/bin/ebuild.sh 2009-03-24 16:44:15.000000000 +0100 +++ portage/bin/ebuild.sh 2009-03-25 07:32:07.000000000 +0100 @@ -69,7 +69,38 @@ # Unset some variables that break things. unset GZIP BZIP BZIP2 CDPATH GREP_OPTIONS GREP_COLOR GLOBIGNORE -export PATH="${DEFAULT_PATH}:$PORTAGE_BIN_PATH/ebuild-helpers:${ROOTPATH}" +if [[ -n "${PORTAGE_READONLY_EROOTS}" ]]; then + new_PATH="$PORTAGE_BIN_PATH/ebuild-helpers:${ROOTPATH}" + + save_IFS=$IFS + IFS=':' + + for eroot in ${PORTAGE_READONLY_EROOTS}:${EPREFIX}; do + IFS=$save_IFS + [[ -f ${eroot}/usr/share/portage/config/make.globals ]] || continue + defpath="$(. ${eroot}/etc/make.globals && echo $DEFAULT_PATH)" + okpath= + save_IFS2=$IFS + IFS=':' + for p in $defpath; do + IFS=$save_IFS2 + [[ ":${new_PATH}:" == *":$p:"* ]] && continue + if [[ -z "${okpath}" ]]; then + okpath="${p}" + else + okpath="${okpath}:${p}" + fi + done + IFS=$save_IFS2 + + new_PATH="${okpath}:${new_PATH}" + done + IFS=$save_IFS + + export PATH=$new_PATH +else + export PATH="${DEFAULT_PATH}:$PORTAGE_BIN_PATH/ebuild-helpers:${ROOTPATH}" +fi [ ! -z "$PREROOTPATH" ] && export PATH="${PREROOTPATH%%:}:$PATH" source "${PORTAGE_BIN_PATH}/isolated-functions.sh" &>/dev/null diff -ru portage.orig/pym/_emerge/__init__.py portage/pym/_emerge/__init__.py --- portage.orig/pym/_emerge/__init__.py 2009-03-24 16:43:02.000000000 +0100 +++ portage/pym/_emerge/__init__.py 2009-03-24 16:42:51.000000000 +0100 @@ -5269,17 +5269,18 @@ edepend["DEPEND"] = "" deps = ( - ("/", edepend["DEPEND"], + ("/", "DEPEND", self._priority(buildtime=(not bdeps_optional), optional=bdeps_optional)), - (myroot, edepend["RDEPEND"], self._priority(runtime=True)), - (myroot, edepend["PDEPEND"], self._priority(runtime_post=True)) + (myroot, "RDEPEND", self._priority(runtime=True)), + (myroot, "PDEPEND", self._priority(runtime_post=True)) ) debug = "--debug" in self.myopts strict = mytype != "installed" try: - 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: @@ -5289,9 +5290,11 @@ print "Priority:", dep_priority vardb = self.roots[dep_root].trees["vartree"].dbapi try: + # MDUFT: selected_atoms will not contain anything + # that can be resolved from a readonly root! selected_atoms = self._select_atoms(dep_root, dep_string, myuse=myuse, parent=pkg, strict=strict, - priority=dep_priority) + priority=dep_priority, dep_type=dep_type) except portage.exception.InvalidDependString, e: show_invalid_depstring_notice(jbigkey, dep_string, str(e)) return 0 @@ -5916,12 +5919,18 @@ 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._filtered_trees is used.""" pkgsettings = self.pkgsettings[root] if trees is None: trees = self._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 + if not getattr(priority, "buildtime", False): # The parent should only be passed to dep_check() for buildtime # dependencies since that's the only case when it's appropriate @@ -5938,7 +5947,7 @@ portage.dep._dep_check_strict = False mycheck = portage.dep_check(depstring, None, pkgsettings, myuse=myuse, - myroot=root, trees=trees) + myroot=root, trees=trees, dep_type=dep_type) finally: if parent is not None: trees[root].pop("parent") @@ -5946,6 +5955,8 @@ if not mycheck[0]: raise portage.exception.InvalidDependString(mycheck[1]) selected_atoms = mycheck[1] + + trees[root].pop("disp_parent") return selected_atoms def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None): @@ -8455,6 +8466,37 @@ for x in blockers: print x + # print readonly selected packages + if len(portage.ro_selected) > 0: + out.write("\n%s\n\n" % (darkgreen("Packages resolved from readonly installations:"))) + + ro_mismatch_warning = False + ro_dupcheck = [] + for x in portage.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_EROOT's\n")) + out.write(yellow(" only with matching CHOST portage instances.\n")) + if verbosity == 3: print print counters @@ -11823,6 +11865,7 @@ for x in candidate_catpkgs: # cycle through all our candidate deps and determine # what will and will not get unmerged + try: mymatch = vartree.dbapi.match(x) except portage.exception.AmbiguousPackageName, errpkgs: diff -ru portage.orig/pym/portage/__init__.py portage/pym/portage/__init__.py --- portage.orig/pym/portage/__init__.py 2009-03-24 16:43:49.000000000 +0100 +++ portage/pym/portage/__init__.py 2009-03-25 13:40:59.000000000 +0100 @@ -1240,6 +1240,7 @@ self._accept_license = copy.deepcopy(clone._accept_license) self._plicensedict = copy.deepcopy(clone._plicensedict) + self.readonly_roots = copy.deepcopy(clone.readonly_roots) else: def check_var_directory(varname, var): @@ -1807,6 +1808,50 @@ # but needs to be available using portageq self["EPREFIX"] = EPREFIX + # expand READONLY_EROOT 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_EROOT"): + if not my_ro_cfg["READONLY_EROOT"].find(":"): + raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EROOT in %s" % (my_ro_current_make_conf_file)) + + (my_ro_cfg_root,my_ro_cfg_root_deps) = my_ro_cfg["READONLY_EROOT"].rsplit(":",1) + + if not os.path.exists(my_ro_cfg_root): + raise portage.exception.InvalidReadonlyERoot("ERROR: malformed READONLY_EROOT 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_EROOT'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_EROOT 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 + self._init_dirs() if mycpv: @@ -3056,6 +3101,10 @@ if rootpath: mydict["PORTAGE_ROOTPATH"] = rootpath + # populate with PORTAGE_READONLY_EROOTS + if self.readonly_roots and len(self.readonly_roots) > 0: + mydict["PORTAGE_READONLY_EROOTS"] = ':'.join(self.readonly_roots) + return mydict def thirdpartymirrors(self): @@ -6740,6 +6789,73 @@ return 0 return 1 +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 = config(config_root=ro_root, target_root=ro_root, + config_incrementals=portage.const.INCREMENTALS) + + ro_vartrees[ro_root] = 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 + def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): """Takes an unreduced and reduced deplist and removes satisfied dependencies. Returned deplist contains steps that must be taken to satisfy dependencies.""" @@ -6775,7 +6891,7 @@ preferred_any_slot = [] possible_upgrades = [] other = [] - + # Alias the trees we'll be checking availability against parent = trees[myroot].get("parent") graph_db = trees[myroot].get("graph_db") @@ -6958,7 +7074,7 @@ return portage.dep.Atom("=" + prefix + expanded + postfix) def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None, - use_cache=1, use_binaries=0, myroot="/", trees=None): + use_cache=1, use_binaries=0, myroot="/", trees=None, dep_type=None): """Takes a depend string and parses the condition.""" edebug = mysettings.get("PORTAGE_DEBUG", None) == "1" #check_config_instance(mysettings) @@ -7036,6 +7152,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) + try: myzaps = dep_zapdeps(mysplit, mysplit2, myroot, use_binaries=use_binaries, trees=trees) diff -ru portage.orig/pym/portage/dbapi/vartree.py portage/pym/portage/dbapi/vartree.py --- portage.orig/pym/portage/dbapi/vartree.py 2009-03-24 16:43:02.000000000 +0100 +++ portage/pym/portage/dbapi/vartree.py 2009-03-24 16:42:51.000000000 +0100 @@ -1276,9 +1276,20 @@ self._counter_path = os.path.join(root, CACHE_PATH.lstrip(os.path.sep), "counter") + plibreg_path = os.path.join(self.root, EPREFIX_LSTRIP, 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, "")) + try: - self.plib_registry = PreservedLibsRegistry( - os.path.join(self.root, EPREFIX_LSTRIP, PRIVATE_PATH, "preserved_libs_registry")) + self.plib_registry = PreservedLibsRegistry(plibreg_path) except PermissionDenied: # apparently this user isn't allowed to access PRIVATE_PATH self.plib_registry = None @@ -1291,6 +1302,10 @@ def getpath(self, mykey, filename=None): rValue = os.path.join(self.root, VDB_PATH, mykey) + + if self._kill_eprefix: + rValue = os.path.join(self.root, rValue.replace(EPREFIX, "")) + if filename != None: rValue = os.path.join(rValue, filename) return rValue @@ -1439,6 +1454,9 @@ returnme = [] basepath = os.path.join(self.root, 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: @@ -1528,7 +1546,12 @@ return list(self._iter_match(mydep, self.cp_list(mydep.cp, use_cache=use_cache))) try: - curmtime = os.stat(self.root+VDB_PATH+"/"+mycat).st_mtime + _tmp_path = self.root+VDB_PATH+"/"+mycat + + if self._kill_eprefix: + _tmp_path = os.path.join(self.root, _tmp_path.replace(EPREFIX, "")) + + curmtime = os.stat(_tmp_path).st_mtime except (IOError, OSError): curmtime=0 @@ -2073,7 +2096,7 @@ class vartree(object): "this tree will scan a var/db/pkg database located at root (passed to init)" def __init__(self, root="/", virtual=None, clone=None, categories=None, - settings=None): + settings=None, kill_eprefix=False): if clone: writemsg("vartree.__init__(): deprecated " + \ "use of clone parameter\n", noiselevel=-1) @@ -2082,6 +2105,7 @@ self.populated = 1 from portage import config self.settings = config(clone=clone.settings) + self._kill_eprefix = clone._kill_eprefix else: self.root = root[:] if settings is None: @@ -2089,6 +2113,7 @@ self.settings = settings # for key_expand calls if categories is None: categories = settings.categories + self._kill_eprefix=kill_eprefix self.dbapi = vardbapi(self.root, categories=categories, settings=settings, vartree=self) self.populated = 1 @@ -2120,6 +2145,10 @@ raise except Exception, e: mydir = os.path.join(self.root, VDB_PATH, 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: diff -ru portage.orig/pym/portage/exception.py portage/pym/portage/exception.py --- portage.orig/pym/portage/exception.py 2009-03-24 16:43:02.000000000 +0100 +++ portage/pym/portage/exception.py 2009-03-24 16:42:52.000000000 +0100 @@ -118,3 +118,6 @@ class UntrustedSignature(SignatureException): """Signature was not certified to the desired security level""" +class InvalidReadonlyERoot(PortageException): + """Readonly EROOT definition string in make.conf invalid.""" +