From c3bb82858f470da3a8744253a40d374c325f7e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20=C3=96zkural?= Date: Thu, 20 Oct 2005 03:08:27 +0000 Subject: [PATCH] * refactor in preparation of fixing several bugs and making many not-so minor improvements! --- pisi.e3p | 8 +- pisi/api.py | 371 +------------------ pisi/{install.py => atomicoperations.py} | 117 +++++- pisi/operations.py | 447 +++++++++++++++++------ 4 files changed, 481 insertions(+), 462 deletions(-) rename pisi/{install.py => atomicoperations.py} (67%) diff --git a/pisi.e3p b/pisi.e3p index 170541f6..e9a02d52 100644 --- a/pisi.e3p +++ b/pisi.e3p @@ -1,7 +1,7 @@ - + Python @@ -402,9 +402,9 @@ - - - + + + diff --git a/pisi/api.py b/pisi/api.py index 0f06d497..72bb6f3e 100644 --- a/pisi/api.py +++ b/pisi/api.py @@ -37,11 +37,11 @@ import pisi.repodb import pisi.installdb from pisi.index import Index import pisi.cli +from pisi.operations import install, remove, upgrade class Error(pisi.Error): pass - def init(database = True, options = None, ui = None, comar = True): """Initialize PiSi subsystem""" @@ -71,179 +71,27 @@ def init(database = True, options = None, ui = None, comar = True): # import pisi.sourcedb # pisi.sourcedb.init() -def install(packages): - """install a list of packages (either files/urls, or names)""" +def list_upgradable(): + ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - # FIXME: this function name "install" makes impossible to import - # and use install module directly. - from install import Error as InstallError - - try: - # determine if this is a list of files/urls or names - if packages[0].endswith('.pisi'): # they all have to! - return install_pkg_files(packages) - else: - return install_pkg_names(packages) - - except packagedb.Error, e: - ctx.ui.error(_("Package is not installable.")) - raise e - - -def install_pkg_files(package_URIs): - """install a number of pisi package files""" - from package import Package - - ctx.ui.debug('A = %s' % str(package_URIs)) - - for x in package_URIs: - if not x.endswith(ctx.const.package_prefix): - ctx.ui.error(_('Mixing file names and package names not supported YET.')) - return False - - if ctx.config.get_option('ignore_dependency'): - # simple code path then - for x in package_URIs: - operations.install_single_file(x) - return True - - # read the package information into memory first - # regardless of which distribution they come from - d_t = {} - dfn = {} - for x in package_URIs: - package = Package(x) - package.read() - name = str(package.metadata.package.name) - d_t[name] = package.metadata.package - dfn[name] = x - - def satisfiesDep(dep): - return dependency.installed_satisfies_dep(dep) \ - or dependency.dict_satisfies_dep(d_t, dep) - - # for this case, we have to determine the dependencies - # that aren't already satisfied and try to install them - # from the repository - dep_unsatis = [] - for name in d_t.keys(): - pkg = d_t[name] - deps = pkg.runtimeDeps - for dep in deps: - if not satisfiesDep(dep): - dep_unsatis.append(dep) - - # now determine if these unsatisfied dependencies could - # be satisfied by installing packages from the repo - - # if so, then invoke install_pkg_names - extra_packages = [x.package for x in dep_unsatis] - if (extra_packages and install_pkg_names(extra_packages)) or \ - (not extra_packages): - - class PackageDB: - def __init__(self): - self.d = d_t - - def get_package(self, key): - return d_t[str(key)] - - packagedb = PackageDB() - - A = d_t.keys() - - if len(A)==0: - ctx.ui.info(_('No packages to install.')) - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - #print A - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - #print pkg - for dep in pkg.runtimeDeps: - #print 'checking ', dep - if dependency.dict_satisfies_dep(d_t, dep): - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - if ctx.config.get_option('debug'): - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - ctx.ui.info(_('Installation order: ') + util.strlist(order) ) - for x in order: - operations.install_single_file(dfn[x]) - else: - raise Error(_('External dependencies not satisfied')) - - return True # everything went OK. - -def install_pkg_names(A): - """This is the real thing. It installs packages from - the repository, trying to perform a minimum number of - installs""" - - A_0 = A = set(A) # A was a list, remove duplicates - - ctx.ui.debug('A = %s' % str(A)) - - if len(A)==0: - ctx.ui.info(_('No packages to install.')) - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - #print A + A = ctx.installdb.list_installed() + # filter packages that are not upgradable + Ap = [] for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - #print pkg - for dep in pkg.runtimeDeps: - ctx.ui.debug('checking %s' % str(dep)) - # we don't deal with already *satisfied* dependencies - if not dependency.installed_satisfies_dep(dep): - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - if ctx.config.get_option('debug'): - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - ctx.ui.info(_("""The following minimal list of packages will be installed -in the respective order to satisfy dependencies: -""") + util.strlist(order)) - if len(order) > len(A_0): - if not ctx.ui.confirm('Do you want to continue?'): - return False - for x in order: - operations.install_single_name(x) - - return True # everything went OK :) + if not ctx.installdb.is_installed(x): + continue + (version, release, build) = ctx.installdb.get_version(x) + pkg = packagedb.get_package(x) + if ignore_build or (not build): + if release < pkg.release: + Ap.append(x) + elif build < pkg.build: + Ap.append(x) + else: + pass + #ctx.ui.info('Package %s cannot be upgraded. ' % x) + return Ap + def package_graph(A, ignore_installed = False): """Construct a package relations graph, containing @@ -279,185 +127,6 @@ def package_graph(A, ignore_installed = False): B = Bp return G_f -def upgrade(A): - upgrade_pkg_names(A) - -def upgrade_pkg_names(A = []): - """Re-installs packages from the repository, trying to perform - a maximum number of upgrades.""" - - ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - - if not A: - # if A is empty, then upgrade all packages - A = ctx.installdb.list_installed() - - # filter packages that are not upgradable - A_0 = A = set(A) - Ap = [] - for x in A: - if x.endswith('.pisi'): - ctx.ui.debug(_("Warning: package *name* ends with '.pisi'")) - if not ctx.installdb.is_installed(x): - ctx.ui.info(_('Package %s is not installed.') % x) - continue - (version, release, build) = ctx.installdb.get_version(x) - pkg = packagedb.get_package(x) - - # First check version. If they are same, check release. Again - # if releases are same and checking buildno is premitted, - # check build number. - if version < pkg.version: - Ap.append(x) - elif version == pkg.version: - if release < pkg.release: - Ap.append(x) - if release == pkg.release and build and not ignore_build: - if build < pkg.build: - Ap.append(x) - else: - #ctx.ui.info('Package %s cannot be upgraded. ' % x) - ctx.ui.info(_('Package %s is already at its latest \ -version %s, release %s, build %s.') - % (x, pkg.version, pkg.release, pkg.build)) - A = set(Ap) - - if len(A)==0: - ctx.ui.info(_('No packages to upgrade.')) - return True - - ctx.ui.debug('A = %s' % str(A)) - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - #print pkg - for dep in pkg.runtimeDeps: - #print 'checking ', dep - # add packages that can be upgraded - if dependency.repo_satisfies_dep(dep): - if ctx.installdb.is_installed(dep.package): - (v,r,b) = ctx.installdb.get_version(dep.package) - rep_pkg = packagedb.get_package(dep.package) - (vp,rp,bp) = (rep_pkg.version, rep_pkg.release, - rep_pkg.build) - if ignore_build or (not b) or (not bp): - # if we can't look at build - if r >= rp: # installed already new - continue - elif b and bp and b >= bp: - continue - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - if ctx.config.get_option('debug'): - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - ctx.ui.info(_("""The following packages will be upgraded:\n""") + - util.strlist(order)) - if len(order) > len(A_0): - if not ctx.ui.confirm('Do you want to continue?'): - return False - for x in order: - operations.install_single_name(x, True) - - return True # everything went OK :) - -def list_upgradable(): - ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - - A = ctx.installdb.list_installed() - # filter packages that are not upgradable - Ap = [] - for x in A: - if not ctx.installdb.is_installed(x): - continue - (version, release, build) = ctx.installdb.get_version(x) - pkg = packagedb.get_package(x) - if ignore_build or (not build): - if release < pkg.release: - Ap.append(x) - elif build < pkg.build: - Ap.append(x) - else: - pass - #ctx.ui.info('Package %s cannot be upgraded. ' % x) - return Ap - -def remove(A): - """remove set A of packages from system (A is a list of package names)""" - - # filter packages that are not installed - A_0 = A = set(A) - Ap = [] - for x in A: - if ctx.installdb.is_installed(x): - Ap.append(x) - else: - ctx.ui.info(_('Package %s does not exist. Cannot remove.') % x) - A = set(Ap) - - if len(A)==0: - ctx.ui.info(_('No packages to remove.')) - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the (install closure) graph of G_f by package - # set A using packagedb - #print A - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - #print 'processing', pkg.name - rev_deps = packagedb.get_rev_deps(x) - for (rev_dep, depinfo) in rev_deps: - #print 'checking ', rev_dep - # we don't deal with unsatisfied dependencies - if packagedb.has_package(rev_dep) and \ - dependency.installed_satisfies_dep(depinfo): - if not rev_dep in G_f.vertices(): - Bp.add(rev_dep) - G_f.add_plain_dep(rev_dep, x) - B = Bp - if ctx.config.get_option('debug'): - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - ctx.ui.info(_("""The following minimal list of packages will be removed -in the respective order to satisfy dependencies: -""") + util.strlist(order)) - if len(order) > len(A_0): - if not ctx.ui.confirm('Do you want to continue?'): - return False - for x in order: - if ctx.installdb.is_installed(x): - operations.remove_single(x) - else: - ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x) - - return True # everything went OK :) def configure_pending(): # start with pending packages diff --git a/pisi/install.py b/pisi/atomicoperations.py similarity index 67% rename from pisi/install.py rename to pisi/atomicoperations.py index f30a5f73..e3428309 100644 --- a/pisi/install.py +++ b/pisi/atomicoperations.py @@ -32,18 +32,31 @@ import pisi import pisi.context as ctx import pisi.packagedb as packagedb import pisi.dependency as dependency -import pisi.operations as operations import pisi.util as util from pisi.specfile import * from pisi.package import Package from pisi.metadata import MetaData +from pisi.uri import URI #import conflicts class Error(pisi.Error): pass -class Installer: - "Installer class, provides install routines for pisi packages" +class AtomicOperation(object): + + def __init__(self, package, ignore_dep = None): + self.package = package + if not ignore_dep: + self.ignore_dep = ctx.config.get_option('ignore_dependency') + else: + self.ignore_dep = ignore_dep + + def run(self, package): + "perform an atomic package operation" + pass + +class Install(AtomicOperation): + "Install class, provides install routines for pisi packages" def __init__(self, package_fname): "initialize from a file name" @@ -72,10 +85,8 @@ class Installer: ctx.ui.info("Regenerating /etc/ld.so.cache...") util.env_update() - def check_requirements(self): """check system requirements""" - #TODO: IS THERE ENOUGH SPACE? # what to do if / is split into /usr, /var, etc. pass @@ -215,3 +226,99 @@ class Installer: # installed packages packagedb.inst_packagedb.add_package(self.pkginfo) + +# single package operations + +# remove stuff + +def Remove(AtomicOperation): + pass + +def remove_file(fileinfo): + fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path) + # TODO: We have to store configuration files for futher + # usage. Currently we'are doing it like rpm does, saving + # with a prefix and leaving the user to edit it. In the future + # we'll have a plan for these configuration files. + if fileinfo.type == ctx.const.conf: + if os.path.isfile(fpath): + os.rename(fpath, fpath + ".pisi") + else: + # check if file is removed manually. + # And we don't remove directories! + # TODO: remove directory if there is nothing under it? + if os.path.isfile(fpath) or os.path.islink(fpath): + os.unlink(fpath) + else: + ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath) + +def run_preremove(package_name): + if ctx.comar: + import pisi.comariface as comariface + comariface.run_preremove(package_name) + else: + # TODO: store this somewhere + pass + +def remove_db(package_name): + ctx.installdb.remove(package_name) + packagedb.remove_package(package_name) #FIXME: this looks like a mistake! + +def remove_single(package_name): + """Remove a single package""" + inst_packagedb = packagedb.inst_packagedb + + #TODO: check dependencies + + ctx.ui.info(_('Removing package %s') % package_name) + if not ctx.installdb.is_installed(package_name): + raise Exception(_('Trying to remove nonexistent package ') + + package_name) + + run_preremove(package_name) + + for fileinfo in ctx.installdb.files(package_name).list: + remove_file(fileinfo) + + remove_db(package_name) + +def install_single(pkg, upgrade = False): + """install a single package from URI or ID""" + url = URI(pkg) + # Check if we are dealing with a remote file or a real path of + # package filename. Otherwise we'll try installing a package from + # the package repository. + if url.is_remote_file() or os.path.exists(url.uri): + install_single_file(pkg, upgrade) + else: + install_single_name(pkg, upgrade) + +# FIXME: Here and elsewhere pkg_location must be a URI +def install_single_file(pkg_location, upgrade = False): + """install a package file""" + from pisi.atomicoperations import Install + Install(pkg_location).install(not upgrade) + +def install_single_name(name, upgrade = False): + """install a single package from ID""" + # find package in repository + repo = packagedb.which_repo(name) + if repo: + repo = ctx.repodb.get_repo(repo) + pkg = packagedb.get_package(name) + + # FIXME: let pkg.packageURI be stored as URI type rather than string + pkg_uri = URI(pkg.packageURI) + if pkg_uri.is_absolute_path(): + pkg_path = str(pkg.packageURI) + else: + pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()), + str(pkg_uri.path())) + + ctx.ui.debug(_("Package URI: %s") % pkg_path) + + # Package will handle remote file for us! + install_single_file(pkg_path, upgrade) + else: + ctx.ui.error(_("Package %s not found in any active repository.") % pkg) + diff --git a/pisi/operations.py b/pisi/operations.py index dd707765..c156a0d3 100644 --- a/pisi/operations.py +++ b/pisi/operations.py @@ -9,11 +9,15 @@ # # Please read the COPYING file. # -# +# Author: Eray Ozkural "Package Operations: install/remove/upgrade" import os +import sys +ver = sys.version_info +if ver[0] <= 2 and ver[1] < 4: + from sets import Set as set import gettext __trans = gettext.translation('pisi', fallback=True) @@ -21,112 +25,351 @@ _ = __trans.ugettext import pisi import pisi.context as ctx -import pisi.util as util -import pisi.packagedb as packagedb from pisi.uri import URI +import pisi.util as util +import pisi.dependency as dependency +import pisi.pgraph as pgraph +import pisi.packagedb as packagedb +import pisi.repodb +import pisi.installdb +from pisi.index import Index +import pisi.cli +import pisi.atomicoperations as atomicoperations -# single package operations - -# remove stuff - -def remove_file(fileinfo): - fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path) - # TODO: We have to store configuration files for futher - # usage. Currently we'are doing it like rpm does, saving - # with a prefix and leaving the user to edit it. In the future - # we'll have a plan for these configuration files. - if fileinfo.type == ctx.const.conf: - if os.path.isfile(fpath): - os.rename(fpath, fpath + ".pisi") - else: - # check if file is removed manually. - # And we don't remove directories! - # TODO: remove directory if there is nothing under it? - if os.path.isfile(fpath) or os.path.islink(fpath): - os.unlink(fpath) - else: - ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath) - -def run_preremove(package_name): - if ctx.comar: - import pisi.comariface as comariface - comariface.run_preremove(package_name) - else: - # TODO: store this somewhere - pass - -def remove_db(package_name): - ctx.installdb.remove(package_name) - packagedb.remove_package(package_name) #FIXME: this looks like a mistake! - -def remove_single(package_name): - """Remove a single package""" - inst_packagedb = packagedb.inst_packagedb - - #TODO: check dependencies - - ctx.ui.info(_('Removing package %s') % package_name) - if not ctx.installdb.is_installed(package_name): - raise Exception(_('Trying to remove nonexistent package ') - + package_name) - - run_preremove(package_name) - - for fileinfo in ctx.installdb.files(package_name).list: - remove_file(fileinfo) - - remove_db(package_name) - -def install_single(pkg, upgrade = False): - """install a single package from URI or ID""" - url = URI(pkg) - # Check if we are dealing with a remote file or a real path of - # package filename. Otherwise we'll try installing a package from - # the package repository. - if url.is_remote_file() or os.path.exists(url.uri): - install_single_file(pkg, upgrade) - else: - install_single_name(pkg, upgrade) - -# FIXME: Here and elsewhere pkg_location must be a URI -def install_single_file(pkg_location, upgrade = False): - """install a package file""" - from install import Installer - Installer(pkg_location).install(not upgrade) - -def install_single_name(name, upgrade = False): - """install a single package from ID""" - # find package in repository - repo = packagedb.which_repo(name) - if repo: - repo = ctx.repodb.get_repo(repo) - pkg = packagedb.get_package(name) - - # FIXME: let pkg.packageURI be stored as URI type rather than string - pkg_uri = URI(pkg.packageURI) - if pkg_uri.is_absolute_path(): - pkg_path = str(pkg.packageURI) - else: - pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()), - str(pkg_uri.path())) - - ctx.ui.debug(_("Package URI: %s") % pkg_path) - - # Package will handle remote file for us! - install_single_file(pkg_path, upgrade) - else: - ctx.ui.error(_("Package %s not found in any active repository.") % pkg) +class Error(pisi.Error): + pass # high level operations +def install(packages): + """install a list of packages (either files/urls, or names)""" + + # FIXME: this function name "install" makes impossible to import + # and use install module directly. + from pisi.atomicoperations import Error as InstallError + + try: + # determine if this is a list of files/urls or names + if packages[0].endswith('.pisi'): # they all have to! + return install_pkg_files(packages) + else: + return install_pkg_names(packages) + + except packagedb.Error, e: + ctx.ui.error(_("Package is not installable.")) + raise e +def install_pkg_files(package_URIs): + """install a number of pisi package files""" + from package import Package -# deneme, don't remove ulan -class AtomicOperation(object): - def __init__(self, package, ignore_dep = False): - self.package = package - self.ignore_dep = ignore_dep + ctx.ui.debug('A = %s' % str(package_URIs)) - def run(self, package): - "perform an atomic package operation" - pass + for x in package_URIs: + if not x.endswith(ctx.const.package_prefix): + ctx.ui.error(_('Mixing file names and package names not supported YET.')) + return False + + if ctx.config.get_option('ignore_dependency'): + # simple code path then + for x in package_URIs: + atomicoperations.install_single_file(x) + return True + + # read the package information into memory first + # regardless of which distribution they come from + d_t = {} + dfn = {} + for x in package_URIs: + package = Package(x) + package.read() + name = str(package.metadata.package.name) + d_t[name] = package.metadata.package + dfn[name] = x + + def satisfiesDep(dep): + return dependency.installed_satisfies_dep(dep) \ + or dependency.dict_satisfies_dep(d_t, dep) + + # for this case, we have to determine the dependencies + # that aren't already satisfied and try to install them + # from the repository + dep_unsatis = [] + for name in d_t.keys(): + pkg = d_t[name] + deps = pkg.runtimeDeps + for dep in deps: + if not satisfiesDep(dep): + dep_unsatis.append(dep) + + # now determine if these unsatisfied dependencies could + # be satisfied by installing packages from the repo + + # if so, then invoke install_pkg_names + extra_packages = [x.package for x in dep_unsatis] + if (extra_packages and install_pkg_names(extra_packages)) or \ + (not extra_packages): + + class PackageDB: + def __init__(self): + self.d = d_t + + def get_package(self, key): + return d_t[str(key)] + + packagedb = PackageDB() + + A = d_t.keys() + + if len(A)==0: + ctx.ui.info(_('No packages to install.')) + return True + + # try to construct a pisi graph of packages to + # install / reinstall + + G_f = pgraph.PGraph(packagedb) # construct G_f + + # find the "install closure" graph of G_f by package + # set A using packagedb + #print A + for x in A: + G_f.add_package(x) + B = A + #state = {} + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + #print pkg + for dep in pkg.runtimeDeps: + #print 'checking ', dep + if dependency.dict_satisfies_dep(d_t, dep): + if not dep.package in G_f.vertices(): + Bp.add(str(dep.package)) + G_f.add_dep(x, dep) + B = Bp + if ctx.config.get_option('debug'): + G_f.write_graphviz(sys.stdout) + order = G_f.topological_sort() + order.reverse() + ctx.ui.info(_('Installation order: ') + util.strlist(order) ) + for x in order: + atomicoperations.install_single_file(dfn[x]) + else: + raise Error(_('External dependencies not satisfied')) + + return True # everything went OK. + +def install_pkg_names(A): + """This is the real thing. It installs packages from + the repository, trying to perform a minimum number of + installs""" + + A_0 = A = set(A) # A was a list, remove duplicates + + ctx.ui.debug('A = %s' % str(A)) + + if len(A)==0: + ctx.ui.info(_('No packages to install.')) + return True + + # try to construct a pisi graph of packages to + # install / reinstall + + G_f = pgraph.PGraph(packagedb) # construct G_f + + # find the "install closure" graph of G_f by package + # set A using packagedb + #print A + for x in A: + G_f.add_package(x) + B = A + #state = {} + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + #print pkg + for dep in pkg.runtimeDeps: + ctx.ui.debug('checking %s' % str(dep)) + # we don't deal with already *satisfied* dependencies + if not dependency.installed_satisfies_dep(dep): + if not dep.package in G_f.vertices(): + Bp.add(str(dep.package)) + G_f.add_dep(x, dep) + B = Bp + if ctx.config.get_option('debug'): + G_f.write_graphviz(sys.stdout) + order = G_f.topological_sort() + order.reverse() + ctx.ui.info(_("""The following minimal list of packages will be installed +in the respective order to satisfy dependencies: +""") + util.strlist(order)) + if len(order) > len(A_0): + if not ctx.ui.confirm('Do you want to continue?'): + return False + for x in order: + atomicoperations.install_single_name(x) + + return True # everything went OK :) + +def upgrade(A): + upgrade_pkg_names(A) + +def upgrade_pkg_names(A = []): + """Re-installs packages from the repository, trying to perform + a maximum number of upgrades.""" + + ignore_build = ctx.config.options and ctx.config.options.ignore_build_no + + if not A: + # if A is empty, then upgrade all packages + A = ctx.installdb.list_installed() + + # filter packages that are not upgradable + A_0 = A = set(A) + Ap = [] + for x in A: + if x.endswith('.pisi'): + ctx.ui.debug(_("Warning: package *name* ends with '.pisi'")) + if not ctx.installdb.is_installed(x): + ctx.ui.info(_('Package %s is not installed.') % x) + continue + (version, release, build) = ctx.installdb.get_version(x) + pkg = packagedb.get_package(x) + + # First check version. If they are same, check release. Again + # if releases are same and checking buildno is premitted, + # check build number. + if version < pkg.version: + Ap.append(x) + elif version == pkg.version: + if release < pkg.release: + Ap.append(x) + if release == pkg.release and build and not ignore_build: + if build < pkg.build: + Ap.append(x) + else: + #ctx.ui.info('Package %s cannot be upgraded. ' % x) + ctx.ui.info(_('Package %s is already at its latest \ +version %s, release %s, build %s.') + % (x, pkg.version, pkg.release, pkg.build)) + A = set(Ap) + + if len(A)==0: + ctx.ui.info(_('No packages to upgrade.')) + return True + + ctx.ui.debug('A = %s' % str(A)) + + # try to construct a pisi graph of packages to + # install / reinstall + + G_f = pgraph.PGraph(packagedb) # construct G_f + + # find the "install closure" graph of G_f by package + # set A using packagedb + for x in A: + G_f.add_package(x) + B = A + #state = {} + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + #print pkg + for dep in pkg.runtimeDeps: + #print 'checking ', dep + # add packages that can be upgraded + if dependency.repo_satisfies_dep(dep): + if ctx.installdb.is_installed(dep.package): + (v,r,b) = ctx.installdb.get_version(dep.package) + rep_pkg = packagedb.get_package(dep.package) + (vp,rp,bp) = (rep_pkg.version, rep_pkg.release, + rep_pkg.build) + if ignore_build or (not b) or (not bp): + # if we can't look at build + if r >= rp: # installed already new + continue + elif b and bp and b >= bp: + continue + if not dep.package in G_f.vertices(): + Bp.add(str(dep.package)) + G_f.add_dep(x, dep) + B = Bp + if ctx.config.get_option('debug'): + G_f.write_graphviz(sys.stdout) + order = G_f.topological_sort() + order.reverse() + ctx.ui.info(_("""The following packages will be upgraded:\n""") + + util.strlist(order)) + if len(order) > len(A_0): + if not ctx.ui.confirm('Do you want to continue?'): + return False + for x in order: + atomicoperations.install_single_name(x, True) + + return True # everything went OK :) + + +def remove(A): + """remove set A of packages from system (A is a list of package names)""" + + # filter packages that are not installed + A_0 = A = set(A) + Ap = [] + for x in A: + if ctx.installdb.is_installed(x): + Ap.append(x) + else: + ctx.ui.info(_('Package %s does not exist. Cannot remove.') % x) + A = set(Ap) + + if len(A)==0: + ctx.ui.info(_('No packages to remove.')) + return True + + # try to construct a pisi graph of packages to + # install / reinstall + + G_f = pgraph.PGraph(packagedb) # construct G_f + + # find the (install closure) graph of G_f by package + # set A using packagedb + #print A + for x in A: + G_f.add_package(x) + B = A + #state = {} + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + #print 'processing', pkg.name + rev_deps = packagedb.get_rev_deps(x) + for (rev_dep, depinfo) in rev_deps: + #print 'checking ', rev_dep + # we don't deal with unsatisfied dependencies + if packagedb.has_package(rev_dep) and \ + dependency.installed_satisfies_dep(depinfo): + if not rev_dep in G_f.vertices(): + Bp.add(rev_dep) + G_f.add_plain_dep(rev_dep, x) + B = Bp + if ctx.config.get_option('debug'): + G_f.write_graphviz(sys.stdout) + order = G_f.topological_sort() + ctx.ui.info(_("""The following minimal list of packages will be removed +in the respective order to satisfy dependencies: +""") + util.strlist(order)) + if len(order) > len(A_0): + if not ctx.ui.confirm('Do you want to continue?'): + return False + for x in order: + if ctx.installdb.is_installed(x): + atomicoperations.remove_single(x) + else: + ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x) + + return True # everything went OK :)