diff --git a/pisi/__init__.py b/pisi/__init__.py index c165ea8e..cc3a101e 100644 --- a/pisi/__init__.py +++ b/pisi/__init__.py @@ -12,9 +12,9 @@ # PiSi version -__version__ = "1.0.3" +__version__ = "1.0.4" -__dbversion__ = "1.0.2" +__dbversion__ = "1.0.4" __all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ] diff --git a/pisi/api.py b/pisi/api.py index 117a3971..37470edf 100644 --- a/pisi/api.py +++ b/pisi/api.py @@ -37,9 +37,9 @@ import pisi.sourcedb import pisi.component as component from pisi.index import Index import pisi.cli -from pisi.operations import install, remove, upgrade -from pisi.build import build, build_until -from pisi.atomicoperations import resurrect_package +from pisi.operations import install, remove, upgrade, emerge +from pisi.build import build_until +from pisi.atomicoperations import resurrect_package, build from pisi.metadata import MetaData from pisi.files import Files import pisi.search @@ -73,13 +73,14 @@ def init(database = True, options = None, ui = None, comar = True): ctx.filesdb = pisi.files.FilesDB() ctx.componentdb = pisi.component.ComponentDB() packagedb.init_db() - pisi.sourcedb.init() + ctx.sourcedb = pisi.sourcedb.init() pisi.search.init(['summary', 'description'], ['en', 'tr']) else: ctx.repodb = None ctx.installdb = None ctx.filesdb = None ctx.componentdb = None + ctx.sourcedb = None ctx.ui.debug('PISI API initialized') ctx.initialized = True diff --git a/pisi/atomicoperations.py b/pisi/atomicoperations.py index 2ec61ad7..def644bd 100644 --- a/pisi/atomicoperations.py +++ b/pisi/atomicoperations.py @@ -429,6 +429,14 @@ class Remove(AtomicOperation): def remove_single(package_name): Remove(package_name).run() +def build(package, authinfo=None): + # wrapper for build op` + if package.endswith('.xml'): + import pisi.build + pisi.build.build(package, authinfo) + #else: + # ... + def __is_virtual_upgrade(metadata): pkg = metadata.package diff --git a/pisi/build.py b/pisi/build.py index bae179cb..ca840f16 100644 --- a/pisi/build.py +++ b/pisi/build.py @@ -43,7 +43,7 @@ import pisi.component as component class Error(pisi.Error): pass - + # Helper Functions def get_file_type(path, pinfo_list, install_dir): """Return the file type of a path according to the given PathInfo diff --git a/pisi/cli/commands.py b/pisi/cli/commands.py index 2fc1010b..5978350f 100644 --- a/pisi/cli/commands.py +++ b/pisi/cli/commands.py @@ -320,10 +320,14 @@ def ignoredep_opt(self): class Build(Command): """Build a PISI package using a pspec.xml file -Usage: build +Usage: build [ | ] ... You can give a URI of the pspec.xml file. PISI will fetch all necessary files and build the package for you. + +Alternatively, you can give the name of a source package +to be downloaded from a repository containing sources. + """ __metaclass__ = autocommand @@ -350,8 +354,8 @@ fetch all necessary files and build the package for you. self.init(database = True) ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir) - for arg in self.args: - pisi.api.build(arg, self.authInfo) + for x in self.args: + pisi.api.build(x, self.authInfo) self.finalize() @@ -935,6 +939,42 @@ Gives a brief list of PiSi components published in the repositories. self.finalize() +class ListSources(Command): + """List available sources + +Usage: list-sources + +Gives a brief list of sources published in the repositories. +""" + __metaclass__ = autocommand + + def __init__(self): + super(ListSources, self).__init__() + + name = ("list-sources", "ls") + + def options(self): + self.parser.add_option("-l", "--long", action="store_true", + default=False, help=_("show in long format")) + + def run(self): + + self.init(True) + + list = ctx.sourcedb.list() + list.sort() + for p in list: + source = ctx.sourcedb.get_source(p) + if self.options.long: + ctx.ui.info(unicode(component)) + else: + lenp = len(p) + #if p in installed_list: + # p = colorize(p, 'cyan') + p = p + ' ' * max(0, 15 - lenp) + ctx.ui.info('%s - %s ' % (source.name, unicode(source.summary))) + self.finalize() + class ListUpgrades(Command): """List packages to be upgraded diff --git a/pisi/index.py b/pisi/index.py index 3b0edc02..be56d9cf 100644 --- a/pisi/index.py +++ b/pisi/index.py @@ -26,6 +26,7 @@ import pisi.context as ctx import pisi.specfile as specfile import pisi.metadata as metadata import pisi.packagedb as packagedb +import pisi.sourcedb as sourcedb import pisi.util as util from pisi.package import Package from pisi.pxml.xmlfile import XmlFile @@ -85,6 +86,8 @@ class Index(XmlFile): ctx.componentdb.update_component(comp) for pkg in self.packages: pkgdb.add_package(pkg) + for src in self.sources: + ctx.sourcedb.add_source(src, repo) def add_package(self, path, repo_uri): package = Package(path, 'r') diff --git a/pisi/operations.py b/pisi/operations.py index b93ce086..4505b1ab 100644 --- a/pisi/operations.py +++ b/pisi/operations.py @@ -41,6 +41,7 @@ class Error(pisi.Error): pass # high level operations + def install(packages, reinstall = False): """install a list of packages (either files/urls, or names)""" @@ -495,3 +496,147 @@ def plan_remove(A): G_f.write_graphviz(sys.stdout) order = G_f.topological_sort() return G_f, order + +def expand_src_components(A): + Ap = set() + for x in A: + if ctx.componentdb.has_component(x): + Ap = Ap.union(ctx.componentdb.get_component(x).sources) + else: + Ap.add(x) + return Ap + +##def build_names(A, rebuild = true): +## +## # A was a list, remove duplicates and expand components +## A_0 = A = expand_src_components(set(A)) +## ctx.ui.debug('A = %s' % str(A)) +## +## # filter packages that are already installed +## if not rebuild: +## Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A)) +## d = A - Ap +## if len(d) > 0: +## ctx.ui.warning(_('Not re-building the following packages: ') + +## util.strlist(d)) +## A = Ap +## +## if len(A)==0: +## ctx.ui.info(_('No packages to build.')) +## return +## +## if not ctx.config.get_option('ignore_dependency'): +## G_f, order_inst = plan_build_pkg_names(A) +## else: +## G_f = None +## order_inst = [] +## +## ctx.ui.info(_("""The following minimal list of packages will be installed +##in the respective order to satisfy dependencies: +##""") + util.strlist(order)) +## +## if ctx.get_option('dry_run'): +## return +## +## if len(order) > len(A_0): +## if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')): +## return False +## +## ctx.ui.notify(ui.packagestogo, order = order) +## +## for x in order: +## atomicoperations.install_single_name(x) +## +##def plan_build_names(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 +## +## while len(B) > 0: +## Bp = set() +## for x in B: +## pkg = packagedb.get_package(x) +## for dep in pkg.runtimeDependencies(): +## 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() +## check_conflicts(order) +## return G_f, order + +def emerge(A): + + # A was a list, remove duplicates and expand components + A_0 = A = expand_src_components(set(A)) + ctx.ui.debug('A = %s' % str(A)) + + if len(A)==0: + ctx.ui.info(_('No packages to build.')) + return + + if not ctx.config.get_option('ignore_dependency'): + G_f, order = plan_emerge(A) + else: + G_f = None + order_inst = [] + + ctx.ui.info(_("""The following minimal list of packages will be built and +installed in the respective order to satisfy dependencies: +""") + util.strlist(order)) + + if ctx.get_option('dry_run'): + return + + if len(order) > len(A_0): + if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')): + return False + + ctx.ui.notify(ui.packagestogo, order = order) + + for x in order: + atomicoperations.install_single_name(x) + +def plan_emerge(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 + + while len(B) > 0: + Bp = set() + for x in B: + pkg = packagedb.get_package(x) + for dep in pkg.runtimeDependencies(): + 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() + check_conflicts(order) + return G_f, order diff --git a/pisi/repodb.py b/pisi/repodb.py index d477320f..4033a111 100644 --- a/pisi/repodb.py +++ b/pisi/repodb.py @@ -51,7 +51,7 @@ class RepoDB(object): # initialize package/source dbs for x in self.list(): packagedb.add_db(x) - + def close(self): self.d.close() diff --git a/pisi/sourcedb.py b/pisi/sourcedb.py index d9a85e6d..91d2bda0 100644 --- a/pisi/sourcedb.py +++ b/pisi/sourcedb.py @@ -16,6 +16,9 @@ package source database interface for update/query to local package repository we basically store everything in sourceinfo class yes, we are cheap +to handle multiple repositories, for sources, we +store a set of repositories in which the source appears. +the actual guy to take is determined from the repo order. """ import os @@ -28,31 +31,60 @@ _ = __trans.ugettext import pisi.util as util import pisi.context as ctx import pisi.lockeddbshelve as shelve +import pisi.repodb class SourceDB(object): - def __init__(self, id = 'repo'): - self.d = shelve.LockedDBShelf('source-%s' % id) + def __init__(self): + self.d = shelve.LockedDBShelf('source') def close(self): self.d.close() + def list(self): + list = [] + for (pkg, x) in self.d.items(): + list.append(pkg) # for some reason we couldn't return self.d.items()!! --exa + return list + def has_source(self, name): name = str(name) return self.d.has_key(name) def get_source(self, name): name = str(name) - return self.d[name] + s = self.d[name] + order = ctx.repodb.list() + for repo in order: + if s.has_key(repo): + return s[repo] + return None - def add_source(self, source_info): + def add_source(self, source_info, repo, txn = None): assert not source_info.errors() name = str(source_info.name) - self.d[name] = source_info - - def remove_source(self, name): + repo = str(repo) + def proc(txn): + if not self.d.has_key(name): + s = dict() + else: + s = self.d[name] + s[repo] = source_info + self.d[name] = s + self.d.txn_proc(proc, txn) + + def remove_source(self, name, repo): name = str(name) - del self.d[name] + repo = str(repo) + def proc(txn): + #assert self.has_source(name) + s = self.d[name] + s.remove(repo) + if (len(s)==0): + del self.d[name] + else: + self.d[name] = s + self.d.txn_proc(proc, txn) sourcedb = None