]> git.ipfire.org Git - pakfire.git/commitdiff
Some bigger rework of the transaction code and solver.
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 21 May 2011 15:19:26 +0000 (17:19 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 21 May 2011 15:19:26 +0000 (17:19 +0200)
pakfire/base.py
pakfire/builder.py
pakfire/packages/base.py
pakfire/packages/file.py
pakfire/packages/installed.py
pakfire/repository/base.py
pakfire/repository/index.py
pakfire/repository/remote.py
pakfire/repository/solver.py
pakfire/repository/transaction.py
po/pakfire.pot

index b3de456777e6889a1dd4006b6a0a03603f2e4088..ef4ebbfe1143ab927ef86fa0674254a8c1644606 100644 (file)
@@ -133,7 +133,6 @@ class Pakfire(object):
                # try to solve it.
                request = self.solver.create_request()
                for solvable in repo:
-                       print solvable
                        request.install(solvable)
 
                t = self.solver.solve(request)
index 5904d22ea8327544663d5114e6286b36e136fef2..0d61104cf97ff34b5d48b50890f7136b22c9b492 100644 (file)
@@ -278,12 +278,15 @@ class Builder(object):
                        if isinstance(req, packages.BinaryPackage):
                                req = req.friendly_name
 
-                       req = self.solver.create_relation(req)
+                       if "<" in req or ">" in req or "=" in req:
+                               req = self.solver.create_relation(req)
 
                        request.install(req)
 
                # Do the solving.
-               transaction = self.solver.solve(request)
+               transaction = self.solver.solve(request, allow_downgrade=True)
+
+               # XXX check for errors
 
                # Show the user what is going to be done.
                transaction.dump(logger=self.log)
@@ -292,6 +295,9 @@ class Builder(object):
                transaction.run()
 
        def install_test(self):
+               # XXX currently disabled
+               return
+
                pkgs = []
                for dir, subdirs, files in os.walk(self.chrootPath("result")):
                        for file in files:
index bd90428b9ebeba0d99232e25469b09e1e335541a..51dc3b7aba728a82b82078dc72737b6d6213285f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 
 import logging
+import xml.sax.saxutils
 
 import util
 
@@ -122,10 +123,20 @@ class Package(object):
                        "maintainer"  : self.maintainer,
                        "url"         : self.url,
                        "license"     : self.license,
+                       "hash1"       : self.hash1,
+                       "vendor"      : self.vendor,
+                       "build_host"  : self.build_host,
+                       "build_time"  : self.build_time,
+                       "size"        : self.size,
+                       "inst_size"   : self.inst_size,
                }
 
                return info
 
+       @property
+       def hash1(self):
+               return "0"*40
+
        @property
        def size(self):
                """
@@ -136,6 +147,11 @@ class Package(object):
                """
                return 0
 
+       @property
+       def inst_size(self):
+               # XXX to be done
+               return 0
+
        @property
        def local(self):
                """
@@ -267,7 +283,13 @@ class Package(object):
 
        @property
        def build_host(self):
-               return self.metadata.get("BUILD_HOST")
+               host = self.metadata.get("BUILD_HOST")
+
+               # XXX Workaround tripple X as hostname.
+               if host == "X"*3:
+                       host = ""
+
+               return host
 
        @property
        def build_id(self):
@@ -291,6 +313,10 @@ class Package(object):
        def vendor(self):
                return self.metadata.get("PKG_VENDOR", "")
 
+       @property
+       def pre_requires(self):
+               return set() # XXX to be done
+
        @property
        def requires(self):
                ret = ""
@@ -315,6 +341,12 @@ class Package(object):
 
                return set(provides)
 
+       @property
+       def conflicts(self):
+               conflicts = self.metadata.get("PKG_CONFLICTS", "").split()
+
+               return set(conflicts)
+
        @property
        def obsoletes(self):
                obsoletes = self.metadata.get("PKG_OBSOLETES", "").split()
@@ -322,5 +354,69 @@ class Package(object):
                return set(obsoletes)
 
        def extract(self, path, prefix=None):
-               raise NotImplementedError, "%s" % type(self)
+               raise NotImplementedError, "%s" % repr(self)
+
+       def export_xml_string(self):
+               info = self.info
+               info["groups"] = " ".join(info["groups"])
+
+               # Escape everything to conform to XML.
+               for key, value in info.items():
+                       if not type(value) in (type(a) for a in ("a", u"a")):
+                               continue
+
+                       info[key] = xml.sax.saxutils.escape(value, {'"': "&quot;"})
+
+               s = """\
+                       <package type="rpm">
+                               <name>%(name)s</name>
+                               <arch>%(arch)s</arch>
+                               <version epoch="%(epoch)s" ver="%(version)s" rel="%(release)s"/>
+                               <checksum type="sha" pkgid="YES">%(hash1)s</checksum>
+                               <summary>%(summary)s</summary>
+                               <description>%(description)s</description>
+                               <packager>%(maintainer)s</packager>
+                               <url>%(url)s</url>
+                               <time file="0" build="%(build_time)s"/>
+                               <size package="%(size)s" installed="%(inst_size)s" />
+                               <format>
+                                       <rpm:license>%(license)s</rpm:license>
+                                       <rpm:vendor>%(vendor)s</rpm:vendor>
+                                       <rpm:group>%(groups)s</rpm:group>
+                                       <rpm:buildhost>%(build_host)s</rpm:buildhost>\n""" \
+                       % info
+
+               if self.provides:
+                       s += "<rpm:provides>"
+                       for provides in self.provides:
+                               s += "<rpm:entry name=\"%s\" />" % provides
+                       s += "</rpm:provides>"
+
+               if self.requires or self.pre_requires:
+                       s += "<rpm:requires>"
+                       for requires in self.requires:
+                               s += "<rpm:entry name=\"%s\" />" % requires
+
+                       for requires in self.pre_requires:
+                               s += "<rpm:entry name=\"%s\" pre=\"1\" />" % requires
+                       s += "</rpm:requires>"
+
+               if self.conflicts:
+                       s += "<rpm:conflicts>"
+                       for conflict in self.conflicts:
+                               s += "<rpm:entry name=\"%s\" />" % conflict
+                       s += "</rpm:conflicts>"
+
+               if self.obsoletes:
+                       s += "<rpm:obsoletes>"
+                       for obsolete in self.obsoletes:
+                               s += "<rpm:entry name=\"%s\" />" % obsolete
+                       s += "</rpm:obsoletes>"
+
+               for file in self.filelist:
+                       # XXX what about type="dir"?
+                       s += "<file>%s</file>" % file
+
+               s += "</format></package>"
 
+               return s
index cb70bb45fdba5fa85049c47d297073e9979818b7..f692c3a2ca53b2fbd6ebb6c4ce91589f06f18799 100644 (file)
@@ -313,9 +313,9 @@ class FilePackage(Package):
 
                # Remove triple X placeholder that was used some time.
                if comp == "X"*3:
-                       comp = None
+                       return None
 
-               return comp
+               return comp or "xz"
 
        @property
        def signature(self):
index 9d66dec4bdd8d72bdd3ddf1675970606a1480b63..f31c8f21322b0582daefc1c863a91c459daaad66 100644 (file)
@@ -176,6 +176,11 @@ class DatabasePackage(Package):
                        Downloads the package from repository and returns a new instance
                        of BinaryPackage.
                """
+
+               # XXX a bit hacky, but InstalledRepository has no cache.
+               if self.repo.name == "installed":
+                       return self
+
                # Marker, if we need to download the package.
                download = True
 
index d80448cbb7c55f3ffc8e1f318874367e31fe382d..50f06ca0e300bfb0fb3632da92fea0ac8c16fafd 100644 (file)
@@ -2,6 +2,8 @@
 
 import fnmatch
 import glob
+import logging
+import re
 
 class RepositoryFactory(object):
        def __init__(self, pakfire, name, description):
@@ -12,6 +14,9 @@ class RepositoryFactory(object):
                # All repositories are enabled by default
                self.enabled = True
 
+               # Reference to corresponding Repo object in the solver.
+               self.solver_repo = None
+
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.name)
 
@@ -59,6 +64,26 @@ class RepositoryFactory(object):
                        if pkg.name == name:
                                yield pkg
 
+       def get_by_evr(self, name, evr):
+               m = re.match(r"([0-9]+\:)?([0-9A-Za-z\.\-]+)-([0-9]+\.?[a-z0-9]+|[0-9]+)", evr)
+
+               if not m:
+                       raise Exception, "Invalid input: %s" % evr
+
+               (epoch, version, release) = m.groups()
+               if epoch and epoch.endswith(":"):
+                       epoch = epoch[:-1]
+
+               pkgs = [p for p in self.index.get_by_evr(name, epoch, version, release)]
+
+               if not pkgs:
+                       return
+
+               if not len(pkgs) == 1:
+                       raise Exception
+
+               return pkgs[0]
+
        def get_by_glob(self, pattern):
                """
                        Returns a list of all packages that names match the glob pattern
@@ -142,3 +167,9 @@ class RepositoryFactory(object):
                        return self.index.filelist
 
                return {}
+
+       def import_to_solver(self, solver, repo):
+               if hasattr(self, "index"):
+                       self.solver_repo = repo
+
+                       self.index.import_to_solver(solver, repo)
index 854e0a3a9f807f868a33d9b7aa9cfd9fbb74e9a1..79c412c22012eb589b37ebbfce84fb5b5a297d5e 100644 (file)
@@ -6,6 +6,7 @@ import logging
 import os
 import random
 import shutil
+import subprocess
 import time
 
 import database
@@ -46,6 +47,20 @@ class Index(object):
                        if match:
                                yield pkg
 
+       def get_by_evr(self, name, epoch, version, release):
+               try:
+                       epoch = int(epoch)
+               except TypeError:
+                       epoch = 0
+
+               for pkg in self.packages:
+                       if pkg.type == "source":
+                               continue
+
+                       if pkg.name == name and pkg.epoch == epoch \
+                                       and pkg.version == version and pkg.release == release:
+                               yield pkg
+
        def get_by_id(self, id):
                raise NotImplementedError
 
@@ -78,6 +93,52 @@ class Index(object):
        def add_package(self, pkg):
                raise NotImplementedError
 
+       @property
+       def cachefile(self):
+               return None
+
+       def import_to_solver(self, solver, repo):
+               if self.cachefile:
+                       if not os.path.exists(self.cachefile):
+                               self.create_solver_cache()
+
+                       logging.debug("Importing repository cache data from %s" % self.cachefile)
+                       repo.add_solv(self.cachefile)
+
+               else:
+                       for pkg in self.packages:
+                               solver.add_package(pkg, repo.name())
+
+               logging.debug("Initialized new repo '%s' with %s packages." % \
+                       (repo.name(), repo.size()))
+
+       def create_solver_cache(self):
+               cachedir = os.path.dirname(self.cachefile)
+               if not os.path.exists(cachedir):
+                       os.makedirs(cachedir)
+
+               f = open(self.cachefile, "w")
+
+               # Write metadata header.
+               xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+               xml += "<metadata xmlns=\"http://linux.duke.edu/metadata/common\""
+               xml += " xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\">\n"
+
+               # We dump an XML string for every package in this repository and
+               # write it to the XML file.
+               for pkg in self.packages:
+                       xml += pkg.export_xml_string()
+
+               # Write footer.
+               xml += "</metadata>"
+
+               p = subprocess.Popen("rpmmd2solv", stdin=subprocess.PIPE,
+                       stdout=subprocess.PIPE)
+               stdout, stderr = p.communicate(xml)
+
+               f.write(stdout)
+               f.close()
+
 
 class DirectoryIndex(Index):
        def __init__(self, pakfire, repo, path):
@@ -206,6 +267,7 @@ class DatabaseIndexFactory(Index):
 
                return files
 
+
 class InstalledIndex(DatabaseIndexFactory):
        def open_database(self):
                # Open the local package database.
@@ -380,3 +442,7 @@ class RemoteIndex(DatabaseIndexFactory):
 
                # XXX this code needs lots of work:
                # XXX   * check the metadata content
+
+       @property
+       def cachefile(self):
+               return "%s.cache" % self.db.filename
index c2ffcff1afa542e1a71d543e49a87d636f1b587c..52c348472aaae14ed173bba4a0c265595f29f09e 100644 (file)
@@ -11,6 +11,8 @@ import pakfire.downloader as downloader
 from base import RepositoryFactory
 
 class RemoteRepository(RepositoryFactory):
+       cacheable = True
+
        def __init__(self, pakfire, name, description, url, mirrorlist, gpgkey, enabled):
                RepositoryFactory.__init__(self, pakfire, name, description)
 
@@ -76,9 +78,9 @@ class RemoteRepository(RepositoryFactory):
 
                return priority
 
-       def update(self, force=False):
-               if self.index:
-                       self.index.update(force=force)
+       #def update(self, force=False):
+       #       if self.index:
+       #               self.index.update(force=force)
 
        def _replace_from_cache(self, pkg):
                for _pkg in self.cache.packages:
index cf9bf610ed5cc7ad82e4f95a6c7735ee93684ecd..07d0f26138de822c4d31443ac7a83fe04454dd8c 100644 (file)
@@ -35,10 +35,18 @@ class Solver(object):
                self.pool.set_arch(arch)
 
                # Initialize all repositories.
-               self.repos = self.init_repos()
+               for repo in self.repos.enabled:
+                       self.init_repo(repo)
+
+#              self.init_repos()
 
                self.pool.prepare()
 
+               logging.debug("Solver pool has %s solvables." % self.pool.size())
+
+       def create_repo(self, *args, **kwargs):
+               return self.pool.create_repo(*args, **kwargs)
+
        def create_relation(self, s):
                s = str(s)
 
@@ -55,17 +63,30 @@ class Solver(object):
 
                return satsolver.Relation(self.pool, s)
 
-       def init_repos(self):
-               repos = []
+       def init_repo(self, repo):
+               solvrepo = self.pool.create_repo(repo.name)
+               if repo.name == "installed":
+                       self.pool.set_installed(solvrepo)
 
-               for repo in self.repos.enabled:
-                       solvrepo = self.pool.create_repo(repo.name)
-                       if repo.name == "installed":
-                               self.pool.set_installed(solvrepo)
+               repo.import_to_solver(self, solvrepo)
+               return
 
-                       pb = util.make_progress(_("Loading %s") % repo.name, repo.size)
-                       i = 0
+               # XXX dead code
 
+               solvrepo = self.pool.create_repo(repo.name)
+               if repo.name == "installed":
+                       self.pool.set_installed(solvrepo)
+
+               pb = util.make_progress(_("Loading %s") % repo.name, repo.size)
+               i = 0
+
+               # Let's see if this repository has a cache and use it if possible.
+               cachefile = repo.metadata_cachefile
+               print cachefile
+               if cachefile and os.path.exists(cachefile):
+                       solvrepo.add_solv(cachefile)
+
+               else:
                        for pkg in repo.get_all():
                                if pb:
                                        i += 1
@@ -73,36 +94,20 @@ class Solver(object):
 
                                self.add_package(pkg)
 
-                       logging.debug("Initialized new repo '%s' with %s packages." % \
-                               (solvrepo.name(), solvrepo.size()))
+               logging.debug("Initialized new repo '%s' with %s packages." % \
+                       (solvrepo.name(), solvrepo.size()))
 
-                       if pb:
-                               pb.finish()
+               if pb:
+                       pb.finish()
 
-                       repos.append(solvrepo)
+               repos.append(solvrepo)
 
-               return repos
-
-       def get_repo(self, name):
-               for repo in self.pool.repos():
-                       if not repo.name() == name:
-                               continue
+       def add_package(self, pkg, reponame):
+               repo = self.repos.get_repo_by_name(reponame)
 
-                       return repo
-
-       def add_package(self, pkg, repo_name=None):
-               if not repo_name:
-                       repo_name = pkg.repo.name
-
-               solvrepo = self.get_repo(repo_name)
-               assert solvrepo
-
-               solvable = satsolver.Solvable(solvrepo, str(pkg.name),
+               solvable = satsolver.Solvable(repo.solver_repo, str(pkg.name),
                        str(pkg.friendly_version), str(pkg.arch))
 
-               # Store the solver's ID.
-               self.id2pkg[solvable.id()] = pkg
-
                # Set vendor.
                solvable.set_vendor(pkg.vendor)
 
@@ -136,10 +141,12 @@ class Solver(object):
        def create_request(self):
                return self.pool.create_request()
 
-       def solve(self, request, update=False, interactive=False):
+       def solve(self, request, update=False, allow_downgrade=False, interactive=False):
                solver = self.pool.create_solver()
                #solver.set_allow_uninstall(True)
 
+               solver.set_allow_downgrade(allow_downgrade)
+
                # Configure the solver for an update.
                if update:
                        solver.set_update_system(True)
@@ -164,10 +171,6 @@ class Solver(object):
                        # Solver had an error and we now see what we can do:
                        logging.info("The solver returned %s problems." % solver.problems_count())
 
-                       # XXX everything after this line is totally broken and does not do its
-                       # job correctly.
-                       return
-
                        jobactions = {
                                satsolver.INSTALL_SOLVABLE : "install",
                                satsolver.UPDATE_SOLVABLE  : "update",
@@ -194,6 +197,8 @@ class Solver(object):
                                if not interactive:
                                        continue
 
+                               continue # XXX
+
                                logging.info("  - %s -" % _("Empty to abort."))
 
                                while True:
@@ -225,17 +230,12 @@ class Solver(object):
                                                                raise Exception, "Unknown action called."
                                                break
 
-       def solvables2packages(self, solvables):
-               pkgs = []
+       def solv2pkg(self, solv):
+               repo = self.repos.get_repo_by_name(solv.repo().name())
 
-               for solv in solvables:
-                       pkg = self.id2pkg[solv.id()]
-                       pkgs.append(pkg)
-
-               return pkgs
+               return repo.get_by_evr(solv.name(), solv.evr())
 
        def get_by_provides(self, provides):
-               print provides
                provides = self.create_relation(provides)
 
                pkgs = self.solvables2packages(self.pool.providers(provides))
index 01600f17e53b9b99fd58fb9db8ca0e82e9cd3c1c..dfc7f7a0ac1661cbfa624d312c8b44172663b8db 100644 (file)
@@ -38,6 +38,17 @@ class Action(object):
                        logging.debug("Removing dep %s from %s" % (dep, self))
                        self.deps.remove(dep)
 
+       @property
+       def needs_download(self):
+               return self.type in ("install", "reinstall", "update", "downgrade",) \
+                       and not isinstance(self.pkg, packages.BinaryPackage)
+
+       def download(self, text):
+               if not self.needs_download:
+                       return
+
+               self.pkg = self.pkg.download(text)
+
        def run(self):
                raise NotImplementedError
 
@@ -92,7 +103,18 @@ class ActionInstall(Action):
                self.local.index.add_package(self.pkg)
 
        def run(self):
-               self.extract(_("Installing: %s") % self.pkg.name)
+               msg = _("Extracting: %s")
+
+               if self.type == "install":
+                       msg = _("Installing: %s")
+               elif self.type == "reinstall":
+                       msg = _("Reinstalling: %s")
+               elif self.type == "update":
+                       msg = _("Updating: %s")
+               elif self.type == "downgrade":
+                       msg = _("Downgrading: %s")
+
+               self.extract(msg % self.pkg.name)
 
                self.pakfire.solver.add_package(self.pkg, "installed")
 
@@ -100,10 +122,6 @@ class ActionInstall(Action):
 class ActionUpdate(ActionInstall):
        type = "upgrade"
 
-       def run(self):
-               self.extract(_("Updating: %s") % self.pkg.name)
-
-
 class ActionRemove(ActionCleanup):
        type = "erase"
 
@@ -116,24 +134,28 @@ class ActionRemove(ActionCleanup):
                self.remove_files(_("Removing: %s") % self.pkg.name, files)
 
 
+class ActionReinstall(ActionInstall):
+       type = "reinstall"
+
+
+class ActionDowngrade(ActionInstall):
+       type = "downgrade"
+
+
 class Transaction(object):
        action_classes = [
                ActionInstall,
                ActionUpdate,
                ActionRemove,
                ActionCleanup,
+               ActionReinstall,
+               ActionDowngrade,
        ]
 
        def __init__(self, pakfire):
                self.pakfire = pakfire
                self.actions = []
 
-               self.downloads = []
-
-               self.installs = []
-               self.updates = []
-               self.removes = []
-
        @classmethod
        def from_solver(cls, pakfire, solver1, solver2):
                # Grab the original transaction object from the solver.
@@ -146,17 +168,12 @@ class Transaction(object):
                # Create a new instance of our own transaction class.
                transaction = cls(pakfire)
 
-               # Copy all information.
-               transaction.installs = solver1.solvables2packages(solver2.installs())
-               transaction.updates = \
-                       [p for p in solver1.solvables2packages(solver2.updates()) if not p.repo.name == "installed"]
-               transaction.removes = solver1.solvables2packages(solver2.removes())
-
                for step in _transaction.steps():
                        action = step.type_s(satsolver.TRANSACTION_MODE_ACTIVE)
-                       pkg = solver1.id2pkg[step.solvable().id()]
+                       pkg = solver1.solv2pkg(step.solvable())
 
-                       if action in ("install", "upgrade") and not isinstance(pkg, packages.BinaryPackage):
+                       if action in ("install", "reinstall", "upgrade") and \
+                                       not isinstance(pkg, packages.BinaryPackage):
                                transaction.downloads.append(pkg)
 
                        for action_cls in cls.action_classes:
@@ -168,28 +185,41 @@ class Transaction(object):
 
                        transaction.add_action(action)
 
+               print transaction.actions
                return transaction
 
-       def download(self):
-               if not self.downloads:
-                       return
+       @property
+       def installs(self):
+               return [a.pkg for a in self.actions if isinstance(a, ActionInstall)]
 
-               i = 0
-               for pkg in self.downloads:
-                       i += 1
+       @property
+       def reinstalls(self):
+               return [a.pkg for a in self.actions if isinstance(a, ActionReinstall)]
 
-                       # Actually download the package.
-                       pkg_bin = pkg.download(text="(%2d/%02d): " % (i, len(self.downloads)))
+       @property
+       def removes(self):
+               return [a.pkg for a in self.actions if isinstance(a, ActionRemove)]
 
-                       # Replace the package in all actions where it matches.
-                       actions = [a for a in self.actions if a.pkg == pkg]
+       @property
+       def updates(self):
+               return [a.pkg for a in self.actions if isinstance(a, ActionUpdate)]
 
-                       for action in actions:
-                               action.pkg = pkg_bin
+       @property
+       def downgrades(self):
+               return [a.pkg for a in self.actions if isinstance(a, ActionDowngrade)]
 
-               # Reset packages to be downloaded.
-               self.downloads = []
-               print
+       @property
+       def downloads(self):
+               return [a for a in self.actions if a.needs_download]
+
+       def download(self):
+               downloads = self.downloads
+
+               i = 0
+               for action in self.actions:
+                       i += 1
+
+                       action.download(text="(%02d/%02d): " % (i, len(downloads)))
 
        def dump_pkg(self, pkg):
                ret = []
@@ -221,32 +251,35 @@ class Transaction(object):
                width = 80
                line = "=" * width
 
-               s = []
+               s = [""]
                s.append(line)
                s.append(PKG_DUMP_FORMAT % (_("Package"), _("Arch"), _("Version"), _("Repository"), _("Size")))
                s.append(line)
 
-               s += self.dump_pkgs(_("Installing:"), self.installs)
-               s += self.dump_pkgs(_("Updating:"), self.updates)
-               s += self.dump_pkgs(_("Removing:"), self.removes)
+               actions = (
+                       (_("Installing:"),              self.installs),
+                       (_("Reinstalling:"),    self.reinstalls),
+                       (_("Updating:"),                self.updates),
+                       (_("Downgrading:"),             self.downgrades),
+                       (_("Removing:"),                self.removes),
+               )
+
+               for caption, pkgs in actions:
+                       s += self.dump_pkgs(caption, pkgs)
 
                s.append(_("Transaction Summary"))
                s.append(line)
 
                format = "%-20s %-4d %s"
 
-               if self.installs:
-                       s.append(format % (_("Install"), len(self.installs), _("Package(s)")))
-               
-               if self.updates:
-                       s.append(format % (_("Updates"), len(self.updates), _("Package(s)")))
-               
-               if self.removes:
-                       s.append(format % (_("Remove"), len(self.removes), _("Package(s)")))
+               for caption, pkgs in actions:
+                       if not len(pkgs):
+                               continue
+                       s.append(format % (caption, len(pkgs), _("package", "packages", len(pkgs))))
 
                # Calculate the size of all files that need to be downloaded this this
                # transaction.
-               download_size = sum([p.size for p in self.downloads])
+               download_size = sum([a.pkg.size for a in self.downloads])
                if download_size:
                        s.append(_("Total download size: %s") % util.format_size(download_size))
                s.append("")
index 846d4d484515b4dd321a60f643a69142ef44d527..565fe514eb2456f883e112919e588a8b91bbe48f 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-05-19 12:48+0200\n"
+"POT-Creation-Date: 2011-05-21 17:16+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,158 +22,168 @@ msgstr ""
 msgid "%s [y/N]"
 msgstr ""
 
-#: ../pakfire/packages/base.py:67
+#: ../pakfire/packages/base.py:68
 msgid "Name"
 msgstr ""
 
-#: ../pakfire/packages/base.py:68 ../pakfire/repository/transaction.py:226
+#: ../pakfire/packages/base.py:69 ../pakfire/repository/transaction.py:256
 msgid "Arch"
 msgstr ""
 
-#: ../pakfire/packages/base.py:69 ../pakfire/repository/transaction.py:226
+#: ../pakfire/packages/base.py:70 ../pakfire/repository/transaction.py:256
 msgid "Version"
 msgstr ""
 
-#: ../pakfire/packages/base.py:70
+#: ../pakfire/packages/base.py:71
 msgid "Release"
 msgstr ""
 
-#: ../pakfire/packages/base.py:71 ../pakfire/repository/transaction.py:226
+#: ../pakfire/packages/base.py:72 ../pakfire/repository/transaction.py:256
 msgid "Size"
 msgstr ""
 
-#: ../pakfire/packages/base.py:72
+#: ../pakfire/packages/base.py:73
 msgid "Repo"
 msgstr ""
 
-#: ../pakfire/packages/base.py:73
+#: ../pakfire/packages/base.py:74
 msgid "Summary"
 msgstr ""
 
-#: ../pakfire/packages/base.py:74
+#: ../pakfire/packages/base.py:75
 msgid "Groups"
 msgstr ""
 
-#: ../pakfire/packages/base.py:75
+#: ../pakfire/packages/base.py:76
 msgid "URL"
 msgstr ""
 
-#: ../pakfire/packages/base.py:76
+#: ../pakfire/packages/base.py:77
 msgid "License"
 msgstr ""
 
-#: ../pakfire/packages/base.py:79
+#: ../pakfire/packages/base.py:80
 msgid "Description"
 msgstr ""
 
-#: ../pakfire/packages/base.py:85
+#: ../pakfire/packages/base.py:86
 msgid "UUID"
 msgstr ""
 
-#: ../pakfire/packages/base.py:86
+#: ../pakfire/packages/base.py:87
 msgid "Build ID"
 msgstr ""
 
-#: ../pakfire/packages/base.py:87
+#: ../pakfire/packages/base.py:88
 msgid "Build date"
 msgstr ""
 
-#: ../pakfire/packages/base.py:88
+#: ../pakfire/packages/base.py:89
 msgid "Build host"
 msgstr ""
 
-#: ../pakfire/packages/base.py:90
+#: ../pakfire/packages/base.py:91
 msgid "Provides"
 msgstr ""
 
-#: ../pakfire/packages/base.py:95
+#: ../pakfire/packages/base.py:96
 msgid "Requires"
 msgstr ""
 
-#: ../pakfire/repository/solver.py:66
+#: ../pakfire/repository/solver.py:80
 #, python-format
 msgid "Loading %s"
 msgstr ""
 
-#: ../pakfire/repository/solver.py:197
+#. XXX
+#: ../pakfire/repository/solver.py:202
 msgid "Empty to abort."
 msgstr ""
 
-#: ../pakfire/repository/solver.py:200
+#: ../pakfire/repository/solver.py:205
 msgid "Choose a solution:"
 msgstr ""
 
-#: ../pakfire/repository/solver.py:216
+#: ../pakfire/repository/solver.py:221
 msgid "You have entered an invalid solution. Try again."
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:95
+#: ../pakfire/repository/transaction.py:106
+#, python-format
+msgid "Extracting: %s"
+msgstr ""
+
+#: ../pakfire/repository/transaction.py:109
 #, python-format
 msgid "Installing: %s"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:104
+#: ../pakfire/repository/transaction.py:111
+#, python-format
+msgid "Reinstalling: %s"
+msgstr ""
+
+#: ../pakfire/repository/transaction.py:113
 #, python-format
 msgid "Updating: %s"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:116
+#: ../pakfire/repository/transaction.py:115
+#, python-format
+msgid "Downgrading: %s"
+msgstr ""
+
+#: ../pakfire/repository/transaction.py:134
 #, python-format
 msgid "Removing: %s"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:226
+#: ../pakfire/repository/transaction.py:256
 msgid "Package"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:226 ../pakfire/cli.py:223
+#: ../pakfire/repository/transaction.py:256 ../pakfire/cli.py:223
 msgid "Repository"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:229
+#: ../pakfire/repository/transaction.py:260
 msgid "Installing:"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:230
-msgid "Updating:"
-msgstr ""
-
-#: ../pakfire/repository/transaction.py:231
-msgid "Removing:"
+#: ../pakfire/repository/transaction.py:261
+msgid "Reinstalling:"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:233
-msgid "Transaction Summary"
+#: ../pakfire/repository/transaction.py:262
+msgid "Updating:"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:239
-msgid "Install"
+#: ../pakfire/repository/transaction.py:263
+msgid "Downgrading:"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:239
-#: ../pakfire/repository/transaction.py:242
-#: ../pakfire/repository/transaction.py:245
-msgid "Package(s)"
+#: ../pakfire/repository/transaction.py:264
+msgid "Removing:"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:242
-msgid "Updates"
+#: ../pakfire/repository/transaction.py:270
+msgid "Transaction Summary"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:245
-msgid "Remove"
+#: ../pakfire/repository/transaction.py:278
+msgid "package"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:251
+#: ../pakfire/repository/transaction.py:284
 #, python-format
 msgid "Total download size: %s"
 msgstr ""
 
-#: ../pakfire/repository/transaction.py:260
+#: ../pakfire/repository/transaction.py:293
 msgid "Is this okay?"
 msgstr ""
 
-#: ../pakfire/repository/index.py:335
+#: ../pakfire/repository/index.py:397
 #, python-format
 msgid "%s: package database"
 msgstr ""