* refactor in preparation of fixing several bugs and making many
not-so minor improvements!
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
|
||||
<!-- Project file for project pisi -->
|
||||
<!-- Saved: 2005-10-18, 01:54:10 -->
|
||||
<!-- Saved: 2005-10-19, 20:42:26 -->
|
||||
<!-- Copyright (C) 2005 PiSi Development Team, -->
|
||||
<Project version="3.8">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
@@ -402,9 +402,9 @@
|
||||
</Vcs>
|
||||
<FiletypeAssociations>
|
||||
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.ui" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
|
||||
<FiletypeAssociation pattern="*.py" type="SOURCES" />
|
||||
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
|
||||
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
|
||||
<FiletypeAssociation pattern="*.ui" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.py" type="SOURCES" />
|
||||
</FiletypeAssociations>
|
||||
</Project>
|
||||
|
||||
+20
-351
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
+345
-102
@@ -9,11 +9,15 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
"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 :)
|
||||
|
||||
Reference in New Issue
Block a user