diff options
Diffstat (limited to 'pym/_emerge/__init__.py')
-rw-r--r-- | pym/_emerge/__init__.py | 202 |
1 files changed, 166 insertions, 36 deletions
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index cc236633b..a1933d6c1 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -729,7 +729,6 @@ class search(object): result = "" return result - class RootConfig(object): """This is used internally by depgraph to track information about a particular $ROOT.""" @@ -1273,13 +1272,22 @@ class Package(Task): __slots__ = ("built", "cpv", "depth", "installed", "metadata", "onlydeps", "operation", "root", "type_name", - "cp", "cpv_slot", "pv_split", "slot_atom") + "category", "cp", "cpv_slot", "pf", "pv_split", "slot_atom") + + metadata_keys = [ + "CHOST", "COUNTER", "DEPEND", "EAPI", "IUSE", "KEYWORDS", + "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", + "repository", "RESTRICT", "SLOT", "USE"] + def __init__(self, **kwargs): Task.__init__(self, **kwargs) self.cp = portage.cpv_getkey(self.cpv) self.slot_atom = "%s:%s" % (self.cp, self.metadata["SLOT"]) self.cpv_slot = "%s:%s" % (self.cpv, self.metadata["SLOT"]) - self.pv_split = portage.catpkgsplit(self.cpv)[1:] + cpv_parts = portage.catpkgsplit(self.cpv) + self.category = cpv_parts[0] + self.pv_split = cpv_parts[1:] + self.pf = self.cpv.replace(self.category + "/", "", 1) def _get_hash_key(self): hash_key = getattr(self, "_hash_key", None) @@ -1366,6 +1374,9 @@ class BlockerCache(DictMixin): 2) the old-style virtuals have changed """ class BlockerData(object): + + __slots__ = ("__weakref__", "atoms", "counter") + def __init__(self, counter, atoms): self.counter = counter self.atoms = atoms @@ -1507,6 +1518,84 @@ class BlockerCache(DictMixin): an AttributeError.""" return list(self) +class BlockerDB(object): + + def __init__(self, vartree, portdb): + self._vartree = vartree + self._portdb = portdb + self._blocker_cache = \ + BlockerCache(self._vartree.root, vartree.dbapi) + self._dep_check_trees = { self._vartree.root : { + "porttree" : self._vartree, + "vartree" : self._vartree, + }} + self._installed_pkgs = None + + def findInstalledBlockers(self, new_pkg): + self._update_cache() + blocker_parents = digraph() + blocker_atoms = [] + for pkg in self._installed_pkgs: + for blocker_atom in self._blocker_cache[pkg.cpv].atoms: + blocker_atom = blocker_atom[1:] + blocker_atoms.append(blocker_atom) + blocker_parents.add(blocker_atom, pkg) + + blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) + blocking_pkgs = set() + for atom in blocker_atoms.iterAtomsForPackage(new_pkg): + blocking_pkgs.update(blocker_parents.parent_nodes(atom)) + return blocking_pkgs + + def _update_cache(self): + blocker_cache = self._blocker_cache + dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] + dep_check_trees = self._dep_check_trees + settings = self._vartree.settings + stale_cache = set(blocker_cache) + fake_vartree = \ + FakeVartree(self._vartree, + self._portdb, Package.metadata_keys, {}) + vardb = fake_vartree.dbapi + self._installed_pkgs = list(vardb) + + for inst_pkg in self._installed_pkgs: + stale_cache.discard(inst_pkg.cpv) + cached_blockers = blocker_cache.get(inst_pkg.cpv) + if cached_blockers is not None and \ + cached_blockers.counter != long(inst_pkg.metadata["COUNTER"]): + cached_blockers = None + if cached_blockers is not None: + blocker_atoms = cached_blockers.atoms + else: + myuse = inst_pkg.metadata["USE"].split() + # Use aux_get() to trigger FakeVartree global + # updates on *DEPEND when appropriate. + depstr = " ".join(vardb.aux_get(inst_pkg.cpv, dep_keys)) + try: + portage.dep._dep_check_strict = False + success, atoms = portage.dep_check(depstr, + vardb, settings, myuse=myuse, + trees=dep_check_trees, myroot=inst_pkg.root) + finally: + portage.dep._dep_check_strict = True + if not success: + pkg_location = os.path.join(inst_pkg.root, + portage.VDB_PATH, inst_pkg.category, inst_pkg.pf) + portage.writemsg("!!! %s/*DEPEND: %s\n" % \ + (pkg_location, atoms), noiselevel=-1) + continue + + blocker_atoms = [atom for atom in atoms \ + if atom.startswith("!")] + blocker_atoms.sort() + counter = long(inst_pkg.metadata["COUNTER"]) + blocker_cache[inst_pkg.cpv] = \ + blocker_cache.BlockerData(counter, blocker_atoms) + for cpv in stale_cache: + del blocker_cache[cpv] + blocker_cache.flush() + def show_invalid_depstring_notice(parent_node, depstring, error_msg): from formatter import AbstractFormatter, DumbWriter @@ -1660,10 +1749,7 @@ class depgraph(object): "binary":"bintree", "installed":"vartree"} - _mydbapi_keys = [ - "CHOST", "COUNTER", "DEPEND", "EAPI", "IUSE", "KEYWORDS", - "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", - "repository", "RESTRICT", "SLOT", "USE"] + _mydbapi_keys = Package.metadata_keys _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] @@ -3484,6 +3570,9 @@ class depgraph(object): return -1 myblocker_uninstalls = self._blocker_uninstalls.copy() retlist=[] + # Contains uninstall tasks that have been scheduled to + # occur after overlapping blockers have been installed. + scheduled_uninstalls = set() # Contains any Uninstall tasks that have been ignored # in order to avoid the circular deps code path. These # correspond to blocker conflicts that could not be @@ -3698,10 +3787,16 @@ class depgraph(object): selected_nodes = list(selected_nodes) selected_nodes.sort(cmp_circular_bias) + if not selected_nodes and scheduled_uninstalls: + selected_nodes = set() + for node in scheduled_uninstalls: + if not mygraph.child_nodes(node): + selected_nodes.add(node) + scheduled_uninstalls.difference_update(selected_nodes) + if not selected_nodes and not myblocker_uninstalls.is_empty(): # An Uninstall task needs to be executed in order to # avoid conflict if possible. - min_parent_deps = None uninst_task = None for task in myblocker_uninstalls.leaf_nodes(): @@ -3819,7 +3914,20 @@ class depgraph(object): uninst_task = task if uninst_task is not None: - selected_nodes = [uninst_task] + # The uninstall is performed only after blocking + # packages have been merged on top of it. File + # collisions between blocking packages are detected + # and removed from the list of files to be uninstalled. + scheduled_uninstalls.add(uninst_task) + parent_nodes = mygraph.parent_nodes(uninst_task) + + # Reverse the parent -> uninstall edges since we want + # to do the uninstall after blocking packages have + # been merged on top of it. + mygraph.remove(uninst_task) + for blocked_pkg in parent_nodes: + mygraph.add(blocked_pkg, uninst_task, + priority=BlockerDepPriority.instance) else: # None of the Uninstall tasks are acceptable, so # the corresponding blockers are unresolvable. @@ -3836,12 +3944,12 @@ class depgraph(object): ignored_uninstall_tasks.add(node) break - # After dropping an Uninstall task, reset - # the state variables for leaf node selection and - # continue trying to select leaf nodes. - prefer_asap = True - accept_root_node = False - continue + # After dropping an Uninstall task, reset + # the state variables for leaf node selection and + # continue trying to select leaf nodes. + prefer_asap = True + accept_root_node = False + continue if not selected_nodes: self._circular_deps_for_display = mygraph @@ -4002,6 +4110,8 @@ class depgraph(object): verbosity = ("--quiet" in self.myopts and 1 or \ "--verbose" in self.myopts and 3 or 2) favorites_set = InternalPackageSet(favorites) + oneshot = "--oneshot" in self.myopts or \ + "--onlydeps" in self.myopts changelogs=[] p=[] blockers = [] @@ -4558,7 +4668,8 @@ class depgraph(object): try: pkg_system = system_set.findAtomForPackage(pkg_key, metadata) pkg_world = world_set.findAtomForPackage(pkg_key, metadata) - if not pkg_world and myroot == self.target_root and \ + if not (oneshot or pkg_world) and \ + myroot == self.target_root and \ favorites_set.findAtomForPackage(pkg_key, metadata): # Maybe it will be added to world now. if create_world_atom(pkg_key, metadata, @@ -5368,12 +5479,35 @@ class MergeTask(object): if settings.get("PORTAGE_DEBUG", "") == "1": self.edebug = 1 self.pkgsettings = {} + self._blocker_db = {} for root in trees: self.pkgsettings[root] = portage.config( clone=trees[root]["vartree"].settings) + self._blocker_db[root] = BlockerDB( + trees[root]["vartree"], + trees[root]["porttree"].dbapi) self.curval = 0 self._spawned_pids = [] - self._uninstall_queue = [] + + def _find_blockers(self, new_pkg): + for opt in ("--buildpkgonly", "--nodeps", + "--fetchonly", "--fetch-all-uri", "--pretend"): + if opt in self.myopts: + return None + + blocker_dblinks = [] + for blocking_pkg in self._blocker_db[ + new_pkg.root].findInstalledBlockers(new_pkg): + if new_pkg.slot_atom == blocking_pkg.slot_atom: + continue + if new_pkg.cpv == blocking_pkg.cpv: + continue + blocker_dblinks.append(portage.dblink( + blocking_pkg.category, blocking_pkg.pf, blocking_pkg.root, + self.pkgsettings[blocking_pkg.root], treetype="vartree", + vartree=self.trees[blocking_pkg.root]["vartree"])) + + return blocker_dblinks def merge(self, mylist, favorites, mtimedb): try: @@ -5403,17 +5537,6 @@ class MergeTask(object): pass spawned_pids.remove(pid) - def _dequeue_uninstall_tasks(self, mtimedb): - if not self._uninstall_queue: - return - for uninst_task in self._uninstall_queue: - root_config = self.trees[uninst_task.root]["root_config"] - unmerge(root_config, self.myopts, "unmerge", - [uninst_task.cpv], mtimedb["ldpath"], clean_world=0) - del mtimedb["resume"]["mergelist"][0] - mtimedb.commit() - del self._uninstall_queue[:] - def _merge(self, mylist, favorites, mtimedb): from portage.elog import elog_process from portage.elog.filtering import filter_mergephases @@ -5554,7 +5677,10 @@ class MergeTask(object): metadata = pkg.metadata if pkg.installed: if not (buildpkgonly or fetchonly or pretend): - self._uninstall_queue.append(pkg) + unmerge(root_config, self.myopts, "unmerge", + [pkg.cpv], mtimedb["ldpath"], clean_world=0) + del mtimedb["resume"]["mergelist"][0] + mtimedb.commit() continue if x[0]=="blocks": @@ -5655,20 +5781,22 @@ class MergeTask(object): return retval bintree = self.trees[myroot]["bintree"] bintree.inject(pkg_key, filename=binpkg_tmpfile) - self._dequeue_uninstall_tasks(mtimedb) + if "--buildpkgonly" not in self.myopts: msg = " === (%s of %s) Merging (%s::%s)" % \ (mergecount, len(mymergelist), pkg_key, y) short_msg = "emerge: (%s of %s) %s Merge" % \ (mergecount, len(mymergelist), pkg_key) emergelog(xterm_titles, msg, short_msg=short_msg) + retval = portage.merge(pkgsettings["CATEGORY"], pkgsettings["PF"], pkgsettings["D"], os.path.join(pkgsettings["PORTAGE_BUILDDIR"], "build-info"), myroot, pkgsettings, myebuild=pkgsettings["EBUILD"], mytree="porttree", mydbapi=portdb, - vartree=vartree, prev_mtimes=ldpath_mtimes) + vartree=vartree, prev_mtimes=ldpath_mtimes, + blockers=self._find_blockers(pkg)) if retval != os.EX_OK: return retval elif "noclean" not in pkgsettings.features: @@ -5687,14 +5815,15 @@ class MergeTask(object): prev_mtimes=ldpath_mtimes) if retval != os.EX_OK: return retval - self._dequeue_uninstall_tasks(mtimedb) + retval = portage.merge(pkgsettings["CATEGORY"], pkgsettings["PF"], pkgsettings["D"], os.path.join(pkgsettings["PORTAGE_BUILDDIR"], "build-info"), myroot, pkgsettings, myebuild=pkgsettings["EBUILD"], mytree="porttree", mydbapi=portdb, - vartree=vartree, prev_mtimes=ldpath_mtimes) + vartree=vartree, prev_mtimes=ldpath_mtimes, + blockers=self._find_blockers(pkg)) if retval != os.EX_OK: return retval finally: @@ -5716,7 +5845,6 @@ class MergeTask(object): portage.locks.unlockdir(catdir_lock) elif x[0]=="binary": - self._dequeue_uninstall_tasks(mtimedb) #merge the tbz2 mytbz2 = self.trees[myroot]["bintree"].getname(pkg_key) if "--getbinpkg" in self.myopts: @@ -5772,7 +5900,8 @@ class MergeTask(object): retval = portage.pkgmerge(mytbz2, x[1], pkgsettings, mydbapi=bindb, vartree=self.trees[myroot]["vartree"], - prev_mtimes=ldpath_mtimes) + prev_mtimes=ldpath_mtimes, + blockers=self._find_blockers(pkg)) if retval != os.EX_OK: return retval #need to check for errors @@ -7888,6 +8017,7 @@ def action_build(settings, trees, mtimedb, fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts ask = "--ask" in myopts nodeps = "--nodeps" in myopts + oneshot = "--oneshot" in myopts or "--onlydeps" in myopts tree = "--tree" in myopts if nodeps and tree: tree = False @@ -8059,7 +8189,7 @@ def action_build(settings, trees, mtimedb, mergecount += 1 if mergecount==0: - if "--noreplace" in myopts and favorites: + if "--noreplace" in myopts and not oneshot and favorites: print for x in favorites: print " %s %s" % (good("*"), x) |