From 698bbf0e9c210c978d7e953306a59c1a25a00ac5 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Mon, 14 Jul 2008 03:47:47 +0000 Subject: Add async execution support for pkg_preinst and pkg_postinst. This allows the scheduler's poll loop to run so that other parallel tasks aren't starved for output handling while pkg_preinst and pkg_postinst are executing. svn path=/main/trunk/; revision=11044 --- pym/_emerge/__init__.py | 77 +++++++++++++++++++++++++++++++++++++++----- pym/portage/__init__.py | 5 +-- pym/portage/dbapi/vartree.py | 36 ++++++++++++++------- 3 files changed, 97 insertions(+), 21 deletions(-) diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index e4e184ad2..a68248a77 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -739,6 +739,17 @@ class search(object): class RootConfig(object): """This is used internally by depgraph to track information about a particular $ROOT.""" + + pkg_tree_map = { + "ebuild" : "porttree", + "binary" : "bintree", + "installed" : "vartree" + } + + tree_pkg_map = {} + for k, v in pkg_tree_map.iteritems(): + tree_pkg_map[v] = k + def __init__(self, settings, trees, setconfig): self.trees = trees self.settings = settings @@ -2409,6 +2420,7 @@ class EbuildBuild(CompositeTask): merge = EbuildMerge(find_blockers=self.find_blockers, ldpath_mtimes=ldpath_mtimes, logger=logger, pkg=pkg, pkg_count=pkg_count, pkg_path=ebuild_path, + scheduler=self.scheduler, settings=settings, tree=tree, world_atom=world_atom) msg = " === (%s of %s) Merging (%s::%s)" % \ @@ -2767,7 +2779,7 @@ class EbuildMerge(SlotObject): __slots__ = ("find_blockers", "logger", "ldpath_mtimes", "pkg", "pkg_count", "pkg_path", "pretend", - "settings", "tree", "world_atom") + "scheduler", "settings", "tree", "world_atom") def execute(self): root_config = self.pkg.root_config @@ -2780,6 +2792,7 @@ class EbuildMerge(SlotObject): mytree=self.tree, mydbapi=root_config.trees[self.tree].dbapi, vartree=root_config.trees["vartree"], prev_mtimes=self.ldpath_mtimes, + scheduler=self.scheduler, blockers=self.find_blockers) if retval == os.EX_OK: @@ -3072,8 +3085,8 @@ class Binpkg(CompositeTask): merge = EbuildMerge(find_blockers=self.find_blockers, ldpath_mtimes=self.ldpath_mtimes, logger=self.logger, pkg=self.pkg, pkg_count=self.pkg_count, - pkg_path=self._pkg_path, settings=settings, - tree=self._tree, world_atom=self.world_atom) + pkg_path=self._pkg_path, scheduler=self.scheduler, + settings=settings, tree=self._tree, world_atom=self.world_atom) try: retval = merge.execute() @@ -3843,10 +3856,7 @@ class PackageVirtualDbapi(portage.dbapi): class depgraph(object): - pkg_tree_map = { - "ebuild":"porttree", - "binary":"bintree", - "installed":"vartree"} + pkg_tree_map = RootConfig.pkg_tree_map _mydbapi_keys = Package.metadata_keys @@ -8244,7 +8254,8 @@ class Scheduler(PollScheduler): _fetch_log = "/var/log/emerge-fetch.log" class _iface_class(SlotObject): - __slots__ = ("fetch", "register", "schedule", "unregister") + __slots__ = ("dblinkEbuildPhase", "fetch", + "register", "schedule", "unregister") class _fetch_iface_class(SlotObject): __slots__ = ("log_file", "schedule") @@ -8307,6 +8318,7 @@ class Scheduler(PollScheduler): fetch_iface = self._fetch_iface_class(log_file=self._fetch_log, schedule=self._schedule_fetch) self._sched_iface = self._iface_class( + dblinkEbuildPhase=self._dblink_ebuild_phase, fetch=fetch_iface, register=self._register, schedule=self._schedule_wait, unregister=self._unregister) @@ -8480,6 +8492,55 @@ class Scheduler(PollScheduler): return blocker_dblinks + def _dblink_pkg(self, pkg_dblink): + cpv = pkg_dblink.mycpv + type_name = RootConfig.tree_pkg_map[pkg_dblink.treetype] + root_config = self.trees[pkg_dblink.myroot]["root_config"] + installed = type_name == "installed" + return self._pkg(cpv, type_name, root_config, installed=installed) + + def _append_to_log_path(self, log_path, msg): + f = open(log_path, 'a') + try: + f.write(msg) + finally: + f.close() + + def _dblink_ebuild_phase(self, + pkg_dblink, pkg_dbapi, ebuild_path, phase): + """ + Using this callback for merge phases allows the scheduler + to run while these phases execute asynchronously, and allows + the scheduler control output handling. + """ + + scheduler = self._sched_iface + settings = pkg_dblink.settings + pkg = self._dblink_pkg(pkg_dblink) + background = self._max_jobs > 1 + log_path = settings.get("PORTAGE_LOG_FILE") + + if phase == "preinst": + msg = ">>> Merging %s to %s\n" % (pkg.cpv, pkg.root) + portage.writemsg_stdout(msg) + if log_path is not None: + self._append_to_log_path(log_path, msg) + + ebuild_phase = EbuildPhase(background=background, + pkg=pkg, phase=phase, scheduler=scheduler, + settings=settings, tree=pkg_dblink.treetype) + ebuild_phase.start() + ebuild_phase.wait() + + if phase == "postinst" and \ + ebuild_phase.returncode == os.EX_OK: + msg = ">>> %s %s\n" % (pkg.cpv, "merged.") + portage.writemsg_stdout(msg) + if log_path is not None: + self._append_to_log_path(log_path, msg) + + return ebuild_phase.returncode + def _check_manifests(self): # Verify all the manifests now so that the user is notified of failure # as soon as possible. diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 4031f64cc..fd4d14b2f 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -5727,13 +5727,14 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None): return newmtime def merge(mycat, mypkg, pkgloc, infloc, myroot, mysettings, myebuild=None, - mytree=None, mydbapi=None, vartree=None, prev_mtimes=None, blockers=None): + mytree=None, mydbapi=None, vartree=None, prev_mtimes=None, blockers=None, + scheduler=None): if not os.access(myroot, os.W_OK): writemsg("Permission denied: access('%s', W_OK)\n" % myroot, noiselevel=-1) return errno.EACCES mylink = dblink(mycat, mypkg, myroot, mysettings, treetype=mytree, - vartree=vartree, blockers=blockers) + vartree=vartree, blockers=blockers, scheduler=scheduler) return mylink.merge(pkgloc, infloc, myroot, myebuild, mydbapi=mydbapi, prev_mtimes=prev_mtimes) diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 50aadc898..fdb16a225 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1282,7 +1282,7 @@ class dblink(object): } def __init__(self, cat, pkg, myroot, mysettings, treetype=None, - vartree=None, blockers=None): + vartree=None, blockers=None, scheduler=None): """ Creates a DBlink object for a given CPV. The given CPV may not be present in the database already. @@ -1312,6 +1312,7 @@ class dblink(object): vartree = db[myroot]["vartree"] self.vartree = vartree self._blockers = blockers + self._scheduler = scheduler self.dbroot = normalize_path(os.path.join(myroot, VDB_PATH)) self.dbcatdir = self.dbroot+"/"+cat @@ -2379,7 +2380,7 @@ class dblink(object): # we need it to have private ${T} etc... for things like elog. others_in_slot.append(dblink(self.cat, catsplit(cur_cpv)[1], self.vartree.root, config(clone=self.settings), - vartree=self.vartree)) + vartree=self.vartree, scheduler=self._scheduler)) retval = self._security_check(others_in_slot) if retval: return retval @@ -2550,8 +2551,6 @@ class dblink(object): if collision_protect: return 1 - writemsg_stdout(">>> Merging %s to %s\n" % (self.mycpv, destroot)) - # The merge process may move files out of the image directory, # which causes invalidation of the .installed flag. try: @@ -2566,10 +2565,17 @@ class dblink(object): self.delete() ensure_dirs(self.dbtmpdir) + scheduler = self._scheduler + # run preinst script - a = doebuild(myebuild, "preinst", destroot, self.settings, - use_cache=0, tree=self.treetype, mydbapi=mydbapi, - vartree=self.vartree) + if scheduler is None: + writemsg_stdout(">>> Merging %s to %s\n" % (self.mycpv, destroot)) + a = doebuild(myebuild, "preinst", destroot, self.settings, + use_cache=0, tree=self.treetype, mydbapi=mydbapi, + vartree=self.vartree) + else: + a = scheduler.dblinkEbuildPhase( + self, mydbapi, myebuild, "preinst") # XXX: Decide how to handle failures here. if a != os.EX_OK: @@ -2722,9 +2728,18 @@ class dblink(object): self.settings["PORTAGE_UPDATE_ENV"] = \ os.path.join(self.dbpkgdir, "environment.bz2") self.settings.backup_changes("PORTAGE_UPDATE_ENV") - a = doebuild(myebuild, "postinst", destroot, self.settings, use_cache=0, - tree=self.treetype, mydbapi=mydbapi, vartree=self.vartree) - self.settings.pop("PORTAGE_UPDATE_ENV", None) + try: + if scheduler is None: + a = doebuild(myebuild, "postinst", destroot, self.settings, + use_cache=0, tree=self.treetype, mydbapi=mydbapi, + vartree=self.vartree) + if a == os.EX_OK: + writemsg_stdout(">>> %s %s\n" % (self.mycpv, "merged.")) + else: + a = scheduler.dblinkEbuildPhase( + self, mydbapi, myebuild, "postinst") + finally: + self.settings.pop("PORTAGE_UPDATE_ENV", None) # XXX: Decide how to handle failures here. if a != os.EX_OK: @@ -2741,7 +2756,6 @@ class dblink(object): target_root=self.settings["ROOT"], prev_mtimes=prev_mtimes, contents=contents, env=self.settings.environ()) - writemsg_stdout(">>> %s %s\n" % (self.mycpv,"merged.")) return os.EX_OK def mergeme(self, srcroot, destroot, outfile, secondhand, stufftomerge, cfgfiledict, thismtime): -- cgit v1.2.3-1-g7c22