Add async execution support for pkg_preinst and pkg_postinst. This allows the
authorZac Medico <zmedico@gentoo.org>
Mon, 14 Jul 2008 03:47:47 +0000 (03:47 -0000)
committerZac Medico <zmedico@gentoo.org>
Mon, 14 Jul 2008 03:47:47 +0000 (03:47 -0000)
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
pym/portage/__init__.py
pym/portage/dbapi/vartree.py

index e4e184ad2181558c5eefd2a6cf0d2dbfe6378a80..a68248a77c669eb095c5e454103338d0cdf90d55 100644 (file)
@@ -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.
index 4031f64ccf675a11744fbaf55e192cadb85e6296..fd4d14b2f1aa8b2828cde8fd3f675fce4c5f7186 100644 (file)
@@ -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)
 
index 50aadc89857018ca5ef82bd0611183f36c2f61ac..fdb16a225eccc5a1f6d939333f0f7fe93012a892 100644 (file)
@@ -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):