ds.add_requires(req)
ds.resolve()
-
- ts = transaction.TransactionSet(self, ds)
- ts.dump()
+ ds.dump()
ret = cli.ask_user(_("Is this okay?"))
if not ret:
return
+ ts = transaction.Transaction(self, ds)
ts.run()
def provides(self, patterns):
ds.add_requires("icecream")
ds.resolve()
+ ds.dump()
# Get build dependencies from source package.
if isinstance(self.pkg, packages.SourcePackage):
for req in self.pkg.requires:
ds.add_requires(req)
- ts = transaction.TransactionSet(self.pakfire, ds)
- ts.dump()
+ ts = transaction.Transaction(self.pakfire, ds)
ts.run()
# Copy the makefile and load source tarballs.
for r in requires:
ds.add_requires(r)
ds.resolve()
+ ds.dump()
- ts = transaction.TransactionSet(self.pakfire, ds)
- ts.dump()
+ ts = transaction.Transaction(self.pakfire, ds)
ts.run()
@property
CACHE_DIR = "/var/cache/pakfire"
CCACHE_CACHE_DIR = os.path.join(CACHE_DIR, "ccache")
+REPO_CACHE_DIR = os.path.join(CACHE_DIR, "repos")
LOCAL_BUILD_REPO_PATH = "/var/lib/pakfire/local"
SOURCE_DOWNLOAD_URL = "http://source.ipfire.org/source-3.x/"
SOURCE_CACHE_DIR = os.path.join(CACHE_DIR, "sources")
+TIME_24H = 60*60*24
+
SOURCE_PACKAGE_META = """\
PKG_NAME="%(PKG_NAME)s"
import packages
import repository
+import transaction
+import util
from errors import *
+from i18n import _
+
+PKG_DUMP_FORMAT = " %-21s %-8s %-21s %-19s %5s "
+
class Requires(object):
- def __init__(self, pkg, requires):
+ def __init__(self, pkg, requires, dep=False):
self.pkg = pkg
self.requires = requires
+ self.dep = dep
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.requires)
self.__requires = []
self.__obsoletes = []
+ # Create a new transaction set.
+ self.ts = transaction.TransactionSet()
+
# Read-in all packages from the database that have
# been installed previously and need to be taken into
# account when resolving dependencies.
for pkg in self.repos.local.packages:
- self.add_package(pkg)
+ self.add_package(pkg, transaction=False)
- def add_requires(self, requires, pkg=None):
+ def add_requires(self, requires, pkg=None, dep=False):
# XXX for now, we skip the virtual perl requires
if requires.startswith("perl(") or requires.startswith("perl>") or requires.startswith("perl="):
return
- requires = Requires(pkg, requires)
+ requires = Requires(pkg, requires, dep)
if requires in self.__requires:
return
self.__obsoletes.append(obsoletes)
- def add_package(self, pkg):
+ def add_package(self, pkg, dep=False, transaction=True):
#print pkg, sorted(self.__packages)
#assert not pkg in self.__packages
if pkg in self.__packages:
logging.debug("Trying to add package which is already in the dependency set: %s" % pkg)
return
- if not isinstance(pkg, packages.DatabasePackage):
- logging.info(" --> Adding package to dependency set: %s" % pkg.friendly_name)
+ if transaction:
+ transaction_mode = "install"
+ for p in self.__packages:
+ if pkg.name == p.name:
+ transaction_mode = "update"
+ break
+
+ # Add package to transaction set
+ func = getattr(self.ts, transaction_mode)
+ func(pkg, dep=dep)
+
+ #if not isinstance(pkg, packages.DatabasePackage):
+ # logging.info(" --> Adding package to dependency set: %s" % pkg.friendly_name)
self.__packages.append(pkg)
# Add the requirements of the newly added package.
for req in pkg.requires:
- self.add_requires(req, pkg)
+ self.add_requires(req, pkg, dep=True)
# Remove all requires that are fulfilled by this package.
# For that we copy the matching requires to _requires and remove them
best = candidates.get_most_recent()
if best:
- self.add_package(best)
+ self.add_package(best, dep=requires.dep)
if unresolveable_reqs:
raise DependencyError, "Cannot resolve %s" % \
" ".join([r.requires for r in unresolveable_reqs])
+ def dump_pkg(self, format, pkg):
+ return format % (
+ pkg.name,
+ pkg.arch,
+ pkg.friendly_version,
+ pkg.repo.name,
+ util.format_size(pkg.size),
+ )
+
+ def dump_pkgs(self, caption, pkgs):
+ if not pkgs:
+ return []
+
+ s = [caption,]
+ for pkg in sorted(pkgs):
+ s.append(self.dump_pkg(PKG_DUMP_FORMAT, pkg))
+ s.append("")
+ return s
+
+ def dump(self):
+ width = 80
+ line = "=" * width
+
+ s = []
+ s.append(line)
+ s.append(PKG_DUMP_FORMAT % (_("Package"), _("Arch"), _("Version"), _("Repository"), _("Size")))
+ s.append(line)
+
+ s += self.dump_pkgs(_("Installing:"), self.ts.installs)
+ s += self.dump_pkgs(_("Installing for dependencies:"), self.ts.install_deps)
+ s += self.dump_pkgs(_("Updating:"), self.ts.updates)
+ s += self.dump_pkgs(_("Updating for dependencies:"), self.ts.update_deps)
+ s += self.dump_pkgs(_("Removing:"), self.ts.removes)
+ s += self.dump_pkgs(_("Removing for dependencies:"), self.ts.remove_deps)
+
+ s.append(_("Transaction Summary"))
+ s.append(line)
+
+ format = "%-20s %-4d %s"
+
+ if self.ts.installs or self.ts.install_deps:
+ s.append(format % (_("Install"),
+ len(self.ts.installs + self.ts.install_deps), _("Package(s)")))
+
+ if self.ts.updates or self.ts.update_deps:
+ s.append(format % (_("Updates"),
+ len(self.ts.updates + self.ts.update_deps), _("Package(s)")))
+
+ if self.ts.removes or self.ts.remove_deps:
+ s.append(format % (_("Remove"),
+ len(self.ts.removes + self.ts.remove_deps), _("Package(s)")))
+
+ download_size = sum([p.size for p in self.ts.installs + \
+ self.ts.install_deps + self.ts.updates + self.ts.update_deps])
+ s.append(_("Total download size: %s") % util.format_size(download_size))
+ s.append("")
+
+ for line in s:
+ logging.info(line)
--- /dev/null
+#!/usr/bin/python
+
+import json
+import logging
+
+from urlgrabber.grabber import URLGrabber, URLGrabError
+from urlgrabber.mirror import MGRandomOrder
+from urlgrabber.progress import TextMultiFileMeter
+
+from constants import *
+
+MIRRORLIST_MAXSIZE = 1024**2
+
+class PakfireGrabber(URLGrabber):
+ """
+ Class to make some modifications on the urlgrabber configuration.
+ """
+ pass
+
+
+class Mirror(object):
+ def __init__(self, mirror, location=None, preferred=False):
+ # Save URL of the mirror in full format
+ self.mirror = mirror
+
+ # Save the location (if given)
+ self.location = location
+
+ # Save preference
+ self.preferred = False
+
+
+class MirrorList(object):
+ def __init__(self, pakfire, repo):
+ self.pakfire = pakfire
+ self.repo = repo
+
+ self.__mirrors = []
+
+ # Save URL to more mirrors.
+ self.mirrorlist = repo.mirrorlist
+
+ self.update(force=False)
+
+ @property
+ def cache(self):
+ """
+ Shortcut to cache from repository.
+ """
+ return self.repo.cache
+
+ def update(self, force=False):
+ # XXX should this be allowed?
+ if not self.mirrorlist:
+ return
+
+ logging.debug("Updating mirrorlist for repository '%s' (force=%s)" % (self.repo.name, force))
+
+ cache_filename = "mirrors/mirrorlist"
+
+ # Force the update if no mirrorlist is available.
+ if not self.cache.exists(cache_filename):
+ force = True
+
+ if not force and self.cache.exists(cache_filename):
+ age = self.cache.age(cache_filename)
+
+ # If the age could be determined and is higher than 24h,
+ # we force an update.
+ if age and age > TIME_24H:
+ force = True
+
+ if force:
+ g = PakfireGrabber()
+
+ try:
+ mirrordata = g.urlread(self.mirrorlist, limit=MIRRORLIST_MAXSIZE)
+ except URLGrabError, e:
+ logging.warning("Could not update the mirrorlist for repo '%s': %s" % (self.repo.name, e))
+ return
+
+ # XXX check for empty files or damaged output
+
+ # Save new mirror data to cache.
+ f = self.cache.open(cache_filename, "w")
+ f.write(mirrordata)
+ f.close()
+
+ # Read mirrorlist from cache and parse it.
+ with self.cache.open(cache_filename) as f:
+ self.parse_mirrordata(f.read())
+
+ def parse_mirrordata(self, data):
+ data = json.loads(data)
+
+ for mirror in data["mirrors"]:
+ self.add_mirror(**mirror)
+
+ def add_mirror(self, *args, **kwargs):
+ mirror = Mirror(*args, **kwargs)
+
+ self.__mirrors.append(mirror)
+
+ @property
+ def preferred(self):
+ """
+ Return a generator for all mirrors that are preferred.
+ """
+ for mirror in self.__mirrors:
+ if mirror.preferred:
+ yield mirror
+
+ @property
+ def all(self):
+ """
+ Return a generator for all mirrors.
+ """
+ for mirror in self.__mirrors:
+ yield mirror
+
def __len__(self):
return len(self.__packages)
+ def get_by_name(self, name):
+ for pkg in self.__packages:
+ if pkg.name == name:
+ yield pkg
+
def get_most_recent(self):
if self.__packages:
return self.__packages[-1]
import fnmatch
import logging
import os
+import stat
+import time
from ConfigParser import ConfigParser
-from urlgrabber.grabber import URLGrabber
-from urlgrabber.mirror import MGRandomOrder
-from urlgrabber.progress import TextMultiFileMeter
-
import base
import database
+import downloader
import index
import packages
"name" : name,
"enabled" : True,
"gpgkey" : None,
+ "mirrorlist" : None,
}
_args.update(args)
return 20000
+class RepositoryCache(object):
+ """
+ An object that is able to cache all data that is loaded from a
+ remote repository.
+ """
+
+ def __init__(self, pakfire, repo):
+ self.pakfire = pakfire
+ self.repo = repo
+
+ self.create()
+
+ @property
+ def path(self):
+ return os.path.join(REPO_CACHE_DIR, self.repo.name, self.repo.arch)
+
+ def create(self):
+ """
+ Create all necessary directories.
+ """
+ for d in ("mirrors", "packages", "metadata"):
+ path = os.path.join(self.path, d)
+
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+ def exists(self, filename):
+ """
+ Returns True if a file exists and False if it doesn't.
+ """
+ return os.path.exists(os.path.join(self.path, filename))
+
+ def age(self, filename):
+ """
+ Returns the age of a downloaded file in minutes.
+ i.e. the time from download until now.
+ """
+ if not self.exists(filename):
+ return None
+
+ filename = os.path.join(self.path, filename)
+
+ # Creation time of the file
+ ctime = os.stat(filename)[stat.ST_CTIME]
+
+ return (time.time() - ctime) / 60
+
+ def open(self, filename, *args, **kwargs):
+ filename = os.path.join(self.path, filename)
+
+ return open(filename, *args, **kwargs)
+
+
class RemoteRepository(RepositoryFactory):
- def __init__(self, pakfire, name, description, url, gpgkey, enabled):
+ def __init__(self, pakfire, name, description, url, mirrorlist, gpgkey, enabled):
RepositoryFactory.__init__(self, pakfire, name, description)
self.url, self.gpgkey = url, gpgkey
+ self.mirrorlist = mirrorlist
if enabled in (True, 1, "1", "yes", "y"):
self.enabled = True
else:
self.enabled = False
+ # Create a cache for the repository where we can keep all temporary data.
+ self.cache = RepositoryCache(self.pakfire, self)
+
+ # Initialize mirror servers.
+ self.mirrors = downloader.MirrorList(self.pakfire, self)
+
if self.local:
self.index = index.DirectoryIndex(self.pakfire, self, self.url)
else:
# Otherwise not.
return False
+ @property
+ def arch(self):
+ return self.pakfire.distro.arch
+
@property
def path(self):
if self.local:
return self.url[7:]
- raise Exception, "XXX find some cache dir"
+ return self.cache.path
@property
def priority(self):
return priority
- @property
- def mirrorlist(self):
- # XXX
- return [
- "http://mirror0.ipfire.org/",
- ]
-
def fetch_file(self, filename):
grabber = URLGrabber(
progress_obj = TextMultiFileMeter(),
import logging
import depsolve
+import packages
import util
from i18n import _
class TransactionSet(object):
- def __init__(self, pakfire, ds):
- self.pakfire = pakfire
- self.ds = ds
+ def __init__(self):
+ self.installs = []
+ self.install_deps = []
- self._actions = []
+ self.updates = []
+ self.update_deps = []
- self._installs = []
- self._removes = []
- self._updates = []
+ self.removes = []
+ self.remove_deps = []
- # Reference to local repository
- self.local = pakfire.repos.local
+ def install(self, pkg, dep=False):
+ logging.info(" --> Marking package for install: %s" % pkg.friendly_name)
- self._packages = self.local.get_all()
+ if dep:
+ self.install_deps.append(pkg)
+ else:
+ self.installs.append(pkg)
- self.populate()
+ def remove(self, pkg, dep=False):
+ logging.info(" --> Marking package for remove: %s" % pkg.friendly_name)
+
+ if dep:
+ self.remove_deps.append(pkg)
+ else:
+ self.removes.append(pkg)
+
+ def update(self, pkg, dep=False):
+ logging.info(" --> Marking package for update: %s" % pkg.friendly_name)
+
+ if dep:
+ self.update_deps.append(pkg)
+ else:
+ self.updates.append(pkg)
+
+ def download(self):
+ pass
+
+
+class Transaction(object):
+ def __init__(self, pakfire, ds):
+ self.pakfire = pakfire
+ self.ds = ds
+
+ self._actions = []
def _install_pkg(self, pkg):
+ assert isinstance(pkg, packages.BinaryPackage)
+
# XXX add dependencies for running the script here
action_prein = ActionScriptPreIn(self.pakfire, pkg)
for action in (action_prein, action_extract, action_postin):
self.add_action(action)
- self._installs.append(pkg)
-
def _update_pkg(self, pkg):
+ assert isinstance(pkg, packages.BinaryPackage)
+
action_extract = ActionExtract(self.pakfire, pkg)
self.add_action(action_extract)
- self._updates.append(pkg)
def _remove_pkg(self, pkg):
# XXX TBD
- self._removes.append(pkg)
+ pass
def populate(self):
- # XXX need to check later, if this really works
-
# Determine which packages we have to add
# and which we have to remove.
- for pkg in self.ds.packages:
- pkgs = self.local.get_by_name(pkg.name)
- pkgs = [p for p in pkgs]
- if not pkgs:
- # Got a new package to install
- self._install_pkg(pkg)
+ # Add all packages that need to be installed.
+ for pkg in self.ds.ts.installs + self.ds.ts.install_deps:
+ self._install_pkg(pkg)
- else:
- # Check for updates
- for _pkg in pkgs:
- if pkg > _pkg:
- self._update_pkg(pkg)
- break
+ # Add all packages that need to be updated.
+ for pkg in self.ds.ts.updates + self.ds.ts.update_deps:
+ self._update_pkg(pkg)
- for pkg in self._packages:
- if not pkg in self.ds.packages:
- self._remove_pkg(pkg)
+ # Add all packages that need to be removed.
+ for pkg in self.ds.ts.removes + self.ds.ts.remove_deps:
+ self._remove_pkg(pkg)
def add_action(self, action):
logging.debug("New action added: %s" % action)
for _action in self.actions:
_action.remove_dep(action)
- @property
- def installs(self):
- return sorted(self._installs)
-
- @property
- def updates(self):
- return sorted(self._updates)
-
- @property
- def removes(self):
- return sorted(self._removes)
-
@property
def actions(self):
for action in self._actions:
logging.error("Action finished with an error: %s - %s" % (action, e))
def run(self):
+ # Download all packages.
+ self.ds.ts.download()
+
+ # Create all the actions that need to be done.
+ self.populate()
+
while True:
if not [a for a in self.actions]:
break
self.run_action(action)
self.remove_action(action)
- def dump_pkg(self, format, pkg):
- return format % (
- pkg.name,
- pkg.arch,
- pkg.friendly_version,
- pkg.repo.name,
- util.format_size(pkg.size),
- )
-
- def dump(self):
- width = 80
- line = "=" * width
- format = " %-21s %-8s %-21s %-19s %5s "
-
- s = []
- s.append(line)
- s.append(format % (_("Package"), _("Arch"), _("Version"), _("Repository"), _("Size")))
- s.append(line)
-
- if self.installs:
- s.append(_("Installing:"))
- for pkg in self.installs:
- s.append(self.dump_pkg(format, pkg))
- s.append("")
-
- if self.updates:
- s.append(_("Updating:"))
- for pkg in self.updates:
- s.append(self.dump_pkg(format, pkg))
- s.append("")
-
- if self.removes:
- s.append(_("Removing:"))
- for pkg in self.removes:
- s.append(self.dump_pkg(format, pkg))
- s.append("")
-
- 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)")))
-
- download_size = sum([p.size for p in self.installs + self.updates])
- s.append(_("Total download size: %s") % util.format_size(download_size))
- s.append("")
-
- print "\n".join(s)
-
pakfire/database.py
pakfire/depsolve.py
pakfire/distro.py
+pakfire/downloader.py
pakfire/errors.py
pakfire/i18n.py
pakfire/index.py
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-02-20 11:01+0100\n"
+"POT-Creation-Date: 2011-02-21 02:10+0100\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"
msgid "Path to input packages."
msgstr ""
-#: ../pakfire/__init__.py:174
-msgid "Is this okay?"
-msgstr ""
-
-#: ../pakfire/packages/base.py:51
-msgid "Name"
+#: ../pakfire/depsolve.py:211
+msgid "Package"
msgstr ""
-#: ../pakfire/packages/base.py:52 ../pakfire/transaction.py:227
+#: ../pakfire/depsolve.py:211 ../pakfire/packages/base.py:51
msgid "Arch"
msgstr ""
-#: ../pakfire/packages/base.py:53 ../pakfire/transaction.py:227
+#: ../pakfire/depsolve.py:211 ../pakfire/packages/base.py:52
msgid "Version"
msgstr ""
-#: ../pakfire/packages/base.py:54
-msgid "Release"
+#: ../pakfire/depsolve.py:211
+msgid "Repository"
msgstr ""
-#: ../pakfire/packages/base.py:55 ../pakfire/transaction.py:227
+#: ../pakfire/depsolve.py:211 ../pakfire/packages/base.py:54
msgid "Size"
msgstr ""
-#: ../pakfire/packages/base.py:56
-msgid "Repo"
+#: ../pakfire/depsolve.py:214
+msgid "Installing:"
msgstr ""
-#: ../pakfire/packages/base.py:57
-msgid "Summary"
+#: ../pakfire/depsolve.py:215
+msgid "Installing for dependencies:"
msgstr ""
-#: ../pakfire/packages/base.py:58
-msgid "URL"
+#: ../pakfire/depsolve.py:216
+msgid "Updating:"
msgstr ""
-#: ../pakfire/packages/base.py:59
-msgid "License"
+#: ../pakfire/depsolve.py:217
+msgid "Updating for dependencies:"
msgstr ""
-#: ../pakfire/packages/base.py:62
-msgid "Description"
+#: ../pakfire/depsolve.py:218
+msgid "Removing:"
msgstr ""
-#: ../pakfire/packages/base.py:68
-msgid "Build ID"
+#: ../pakfire/depsolve.py:219
+msgid "Removing for dependencies:"
msgstr ""
-#: ../pakfire/packages/base.py:69
-msgid "Build date"
+#: ../pakfire/depsolve.py:221
+msgid "Transaction Summary"
msgstr ""
-#: ../pakfire/packages/base.py:70
-msgid "Build host"
+#: ../pakfire/depsolve.py:227
+msgid "Install"
msgstr ""
-#: ../pakfire/packages/packager.py:70
-msgid "Extracting"
+#: ../pakfire/depsolve.py:228 ../pakfire/depsolve.py:232
+#: ../pakfire/depsolve.py:236
+msgid "Package(s)"
msgstr ""
-#: ../pakfire/packages/packager.py:125
-msgid "Extracting:"
+#: ../pakfire/depsolve.py:231
+msgid "Updates"
msgstr ""
-#: ../pakfire/transaction.py:227
-msgid "Package"
+#: ../pakfire/depsolve.py:235
+msgid "Remove"
msgstr ""
-#: ../pakfire/transaction.py:227
-msgid "Repository"
+#: ../pakfire/depsolve.py:240
+#, python-format
+msgid "Total download size: %s"
msgstr ""
-#: ../pakfire/transaction.py:231
-msgid "Installing:"
+#: ../pakfire/__init__.py:172
+msgid "Is this okay?"
msgstr ""
-#: ../pakfire/transaction.py:237
-msgid "Updating:"
+#: ../pakfire/packages/base.py:50
+msgid "Name"
msgstr ""
-#: ../pakfire/transaction.py:243
-msgid "Removing:"
+#: ../pakfire/packages/base.py:53
+msgid "Release"
msgstr ""
-#: ../pakfire/transaction.py:248
-msgid "Transaction Summary"
+#: ../pakfire/packages/base.py:55
+msgid "Repo"
msgstr ""
-#: ../pakfire/transaction.py:254
-msgid "Install"
+#: ../pakfire/packages/base.py:56
+msgid "Summary"
msgstr ""
-#: ../pakfire/transaction.py:254 ../pakfire/transaction.py:257
-#: ../pakfire/transaction.py:260
-msgid "Package(s)"
+#: ../pakfire/packages/base.py:57
+msgid "URL"
msgstr ""
-#: ../pakfire/transaction.py:257
-msgid "Updates"
+#: ../pakfire/packages/base.py:58
+msgid "License"
msgstr ""
-#: ../pakfire/transaction.py:260
-msgid "Remove"
+#: ../pakfire/packages/base.py:61
+msgid "Description"
msgstr ""
-#: ../pakfire/transaction.py:263
-#, python-format
-msgid "Total download size: %s"
+#: ../pakfire/packages/base.py:67
+msgid "Build ID"
+msgstr ""
+
+#: ../pakfire/packages/base.py:68
+msgid "Build date"
+msgstr ""
+
+#: ../pakfire/packages/base.py:69
+msgid "Build host"
+msgstr ""
+
+#: ../pakfire/packages/packager.py:70
+msgid "Extracting"
+msgstr ""
+
+#: ../pakfire/packages/packager.py:125
+msgid "Extracting:"
msgstr ""