Merge branch 'pisi-db'

This commit is contained in:
Faik Uygur
2007-11-23 12:34:47 +00:00
parent 489153317a
commit 4792d55dc8
198 changed files with 5386 additions and 10482 deletions
+115
View File
@@ -0,0 +1,115 @@
API Plan
========
Pisi does not have a usable api. All the projects use its internal modules to do their
jobs. This file holds a list of these usages by project as a guide for a new competent
pisi api.
PiSi
====
Below are pisi's internal calls that they may help to figure out common api calls.
* packagedb.remove_repo
* sourcedb.remove_repo
* packagedb.which_repo
* repodb.get_repo
* packagedb.get_package
* installdb.is_installed
* packagedb.get_rev_deps
* installdb.get_version
* packagedb.has_package
* packagedb.add_package
* componentdb.add_package
* filesdb.has_file
* filesdb.get_file
* installdb.get_info
* installdb.files
* installdb.pkg_dir
* installdb.install
* filesdb.add_files
* componentdb.add_spec
* sourcedb.add_spec
* componentdb.get_union_comp
* componentdb.remove_spec
* installdb.get_version
* componentdb.remove_repo
* componentdb.remove_package
* componentdb.update_component
* componentdb.add_package
* installdb.remove
* filesdb.remove_files
* sourcedb.pkgtosrc
* sourcedb.get_spec
* sourcedb.get_source
* sourcedb.get_spec_repo
* repodb.get_repo
* componentdb.has_component
* componentdb.get_component
* installdb.is_installed
* packagedb.list_packages
* installdb.list_installed
* componentdb.get_union_packages
* componentdb.list_components
* repodb.list
* sourcedb.list
* installdb.list_pending
* filesdb.match_files
Package Manager
===============
Below are the pisi modules used internally by package-manager. Package-manager should use pisi
api.
* repodb.get_repo
* packagedb.get_package
pm still uses the old packagedb with pisi.itemsbyrepo.installed or pisi.itemsbyrepo.repos params
* componentdb.list_components
* repodb.list
* componentdb.get_union_comp
* componentdb.get_union_packages
Yali
====
Below are the pisi modules used internally by Yali. Yali should use pisi api.
* api.add_repo
* api.update_repo
* api.remove_repo
* api.install
* packagedb.list_packages
* installdb.list_pending
* api.configure_pending
* packagedb.get_package
Buildfarm
=========
Below are the pisi modules used internally by Buildfarm.
* api.create_delta_package
PackageKit
==========
Below are the pisi modules used internally by PackageKit.
* installdb.has_package
* installdb.get_package
* packagedb.has_package
* packagedb.get_package
* installdb.get_rev_deps
* packagedb.get_rev_deps
* installdb.get_package.runtimeDependencies
* packagedb.get_package.runtimeDependencies
* api.install
* api.upgrade
* api.remove
* api.list_upgradable
* api.update_repo
* api.list_repos
* repodb.get_repo.indexuri.get_uri
* version.Version
* util.any
-4
View File
@@ -96,10 +96,6 @@ used. (see remove_unused_attributes.patch)
ones named tmpDir/tmp_dir/target_dir) are redundant now. See
http://liste.uludag.org.tr/uludag-commits/2007-February/010070.html
* conflict.py
conflicts method takes a pkg_name parameter but doesn't use it at all
==> Fixes (that need API breakage)
* http://liste.uludag.org.tr/uludag-commits/2007-February/010117.html
+30
View File
@@ -0,0 +1,30 @@
* Create meaningful Exception classes. Remove "Error" exceptions
* Humanized error messages after Exception work done
* Pisi command outputs overhaul
* If possible remove context from pisi
* autoxml is hairy and not maintainable. If possible replace it with a simpler and
faster xml objectifier implementation.
* We need to update some state file during the pisi operation to implement a transaction
like system. And take some actions after last failure of pisi for some reason.
* Messages in log file are not very helpful. Log messages overhaul needed. We need detailed
logging.
* Tidy pisi.api to satisfy necessary functions
* Write a new unit test suite
* Refactor code after unit tests are finished. Divide long functions. Rename
necessary function and variable names (like A, B_C, D_, C) to understandable
ones
* Add documentation to all module functions
* Performance and memory usage optimizations
* Version validator
+1 -5
View File
@@ -32,10 +32,6 @@ def sig_handler(sig, frame):
exit()
def exit():
try:
pisi.api.finalize()
except KeyboardInterrupt: # raised pending interrupt
pass
sys.exit(1)
def handle_exception(exception, value, tb):
@@ -73,7 +69,7 @@ Please file a bug report. (http://bugs.uludag.org.tr)"""))
ui.info(_("Traceback:"))
traceback.print_tb(tb)
else:
if not exception is pisicli.Error:
if not exception is pisi.Error:
ui.info(_("Use --debug to see a traceback."))
exit()
+1
View File
@@ -0,0 +1 @@
*.pyc
+38 -5
View File
@@ -12,10 +12,11 @@
# PiSi version
__version__ = "1.1.5"
import os
import atexit
import logging
__dbversion__ = "1.1.5"
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
__version__ = "1.1.5"
__all__ = [ 'api', 'configfile', 'db']
@@ -35,6 +36,38 @@ class Error(Exception):
pass
import pisi.api
import pisi.config
import pisi.context as ctx
# FIXME: can't do this due to name clashes in config and other singletons booo
#pisi.api import *
def init_logging():
log_dir = os.path.join(ctx.config.dest_dir(), ctx.config.log_dir())
if os.access(log_dir, os.W_OK):
handler = logging.handlers.RotatingFileHandler('%s/pisi.log' % log_dir)
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
ctx.log = logging.getLogger('pisi')
ctx.log.addHandler(handler)
ctx.loghandler = handler
ctx.log.setLevel(logging.DEBUG)
def _cleanup():
"""Close the database cleanly and do other cleanup."""
ctx.disable_keyboard_interrupts()
if ctx.log:
ctx.loghandler.flush()
ctx.log.removeHandler(ctx.loghandler)
filesdb = pisi.db.filesdb.FilesDB()
if filesdb.is_initialized():
filesdb.close()
if ctx.build_leftover and os.path.exists(ctx.build_leftover):
os.unlink(ctx.build_leftover)
ctx.ui.close()
ctx.enable_keyboard_interrupts()
atexit.register(_cleanup)
ctx.config = pisi.config.Config(pisi.config.Options())
init_logging()
+277 -305
View File
@@ -9,10 +9,7 @@
#
# Please read the COPYING file.
"""Top level PiSi interfaces. a facade to the entire PiSi system"""
import os
import sys
import logging
import logging.handlers
@@ -32,140 +29,237 @@ import pisi.db.filesdb
import pisi.db.installdb
import pisi.db.sourcedb
import pisi.db.componentdb
import pisi.db.lockeddbshelve as shelve
import pisi.index
import pisi.config
import pisi.metadata
import pisi.file
import pisi.version
import pisi.operations
import pisi.build
import pisi.atomicoperations
import pisi.delta
import pisi.operations.delta
import pisi.operations.remove
import pisi.operations.upgrade
import pisi.operations.install
import pisi.operations.helper
import pisi.operations.emerge
import pisi.operations.build
import pisi.comariface
import pisi.signalhandler
class Error(pisi.Error):
pass
def init(database = True, write = True,
options = pisi.config.Options(), ui = None, comar = True,
stdout = None, stderr = None,
comar_sockname = None,
signal_handling = True):
"""Initialize PiSi subsystem.
You should call finalize() when your work is finished. Otherwise
you can left the database in a bad state.
def set_userinterface(ui):
"""
Set the user interface where the status information will be send
@param ui: User interface
"""
ctx.ui = ui
# UI comes first
if ui is None:
# FIXME: api importing and using pisi.cli ????
import pisi.cli
if options:
ctx.ui = pisi.cli.CLI(options.debug, options.verbose)
else:
ctx.ui = pisi.cli.CLI()
else:
ctx.ui = ui
# FIXME: something is wrong here... see __init__.py also. Why do we import pisi.api in __init__.py
import pisi.config
ctx.config = pisi.config.Config(options)
if os.access('%s/var/log' % ctx.config.log_dir(), os.W_OK):
handler = logging.handlers.RotatingFileHandler('%s/var/log/pisi.log' % ctx.config.log_dir())
#handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
ctx.log = logging.getLogger('pisi')
ctx.log.addHandler(handler)
ctx.loghandler = handler
ctx.log.setLevel(logging.DEBUG)
else:
ctx.log = None
# If given define stdout and stderr. Needed by buildfarm currently
# but others can benefit from this too.
def set_io_streams(stdout=None, stderr=None):
"""
Set standart i/o streams
Used by Buildfarm
@param stdout: Standart output
@param stderr: Standart input
"""
if stdout:
ctx.stdout = stdout
if stderr:
ctx.stderr = stderr
if signal_handling:
ctx.sig = pisi.signalhandler.SignalHandler()
def set_comar(enable):
"""
Set comar usage
False means no preremove and postinstall scripts will be run
@param enable: Flag indicating comar usage
"""
ctx.comar = enable
# TODO: this is definitely not dynamic beyond this point!
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
# This is for YALI, used in comariface.py
ctx.comar_sockname = comar_sockname
def set_comar_sockname(sockname):
"""
Set comar socket file
Used by YALI
@param sockname: Path to comar socket file
"""
ctx.comar_sockname = sockname
# initialize repository databases
ctx.database = database
if database:
shelve.init_dbenv(write=write)
ctx.repodb = pisi.db.repodb.init()
ctx.installdb = pisi.db.installdb.init()
ctx.filesdb = pisi.db.filesdb.init()
ctx.componentdb = pisi.db.componentdb.init()
ctx.packagedb = pisi.db.packagedb.init()
ctx.sourcedb = pisi.db.sourcedb.init()
else:
ctx.repodb = None
ctx.installdb = None
ctx.filesdb = None
ctx.componentdb = None
ctx.packagedb = None
ctx.sourcedb = None
ctx.ui.debug('PiSi API initialized')
ctx.initialized = True
def set_options(options):
"""
Set various options of pisi
@param options: option set
>>> options = pisi.config.Options()
options.destdir # pisi destination directory where operations will take effect
options.username # username that for reaching remote repository
options.password # password that for reaching remote repository
options.debug # flag controlling debug output
options.verbose # flag controlling verbosity of the output messages
"""
ctx.config = pisi.config.Config(options)
def finalize():
"""Close the database cleanly and do other cleanup."""
if ctx.initialized:
ctx.disable_keyboard_interrupts()
if ctx.log:
ctx.loghandler.flush()
ctx.log.removeHandler(ctx.loghandler)
pisi.db.repodb.finalize()
pisi.db.installdb.finalize()
pisi.db.filesdb.finalize()
pisi.db.componentdb.finalize()
pisi.db.packagedb.finalize()
pisi.db.sourcedb.finalize()
if ctx.dbenv:
ctx.dbenv.close()
ctx.dbenv_lock.close()
if ctx.build_leftover and os.path.exists(ctx.build_leftover):
os.unlink(ctx.build_leftover)
ctx.ui.debug('PiSi API finalized')
ctx.ui.close()
ctx.initialized = False
ctx.enable_keyboard_interrupts()
def list_pending():
"""
Return a list of configuration pending packages -> list_of_strings
"""
return pisi.db.installdb.InstallDB().list_pending()
def list_installed():
"""Return a set of installed package names."""
return set(ctx.installdb.list_installed())
"""
Return a list of installed packages -> list_of_strings
"""
return pisi.db.installdb.InstallDB().list_installed()
def list_replaces(repo = None):
"""Returns a dict of the replaced packages."""
return ctx.packagedb.get_replaces(repo = repo)
def list_replaces(repo=None):
"""
Return a dictionary of the replaced packages in the given repository
@param repo: Repository of the replaced packages. If repo is None than returns
a dictionary of all the replaced packages in all the repositories
{'gaim':'pidgin, 'actioncube':'assaultcube'}
gaim replaced by pidgin and actioncube replaced by assaultcube
"""
return pisi.db.packagedb.PackageDB().get_replaces(repo)
def list_available(repo = None):
"""Return a set of available package names."""
return set(ctx.packagedb.list_packages(repo = repo))
def list_available(repo=None):
"""
Return a list of available packages in the given repository -> list_of_strings
@param repo: Repository of the packages. If repo is None than returns
a list of all the available packages in all the repositories
"""
return pisi.db.packagedb.PackageDB().list_packages(repo)
def list_upgradable():
return filter(pisi.operations.is_upgradable, ctx.installdb.list_installed()) + ctx.packagedb.get_replaces().keys()
"""
Return a list of packages that are upgraded in the repository -> list_of_strings
"""
installdb = pisi.db.installdb.InstallDB()
def package_graph(A, repo = pisi.db.itembyrepodb.installed, ignore_installed = False):
upgradable = filter(pisi.operations.upgrade.is_upgradable, installdb.list_installed())
# replaced packages can not pass is_upgradable test, so we add them manually
upgradable.extend(list_replaces())
return upgradable
def list_repos():
"""
Return a list of the repositories -> list_of_strings
"""
return pisi.db.repodb.RepoDB().list_repos()
def get_install_order(packages):
"""
Return a list of packages in the installation order with extra needed
dependencies -> list_of_strings
@param packages: list of package names -> list_of_strings
"""
install_order = pisi.operations.install.plan_install_pkg_names
i_graph, order = install_order(packages, ignore_package_conflicts=True)
return order
def get_remove_order(packages):
"""
Return a list of packages in the remove order -> list_of_strings
@param packages: list of package names -> list_of_strings
"""
remove_order = pisi.operations.remove.plan_remove
i_graph, order = remove_order(packages)
return order
def get_upgrade_order(packages):
"""
Return a list of packages in the upgrade order with extra needed
dependencies -> list_of_strings
@param packages: list of package names -> list_of_strings
"""
upgrade_order = pisi.operations.upgrade.plan_upgrade
i_graph, order = upgrade_order(packages)
return order
def get_base_upgrade_order(packages):
"""
Return a list of packages of the system.base component that needs to be upgraded
or installed in install order -> list_of_strings
All the packages of the system.base component must be installed on the system
@param packages: list of package names -> list_of_strings
"""
upgrade_order = pisi.operations.upgrade.upgrade_base
order = upgrade_order(packages, ignore_package_conflicts=True)
return list(order)
def get_conflicts(packages):
"""
Return a tuple of the conflicting packages information -> tuple
@param packages: list of package names -> list_of_strings
>>> (pkgs, within, pairs) = pisi.api.get_conflicts(packages)
>>>
>>> pkgs # list of packages that are installed and conflicts with the
# given packages list -> list_of_strings
>>> [...]
>>> within # list of packages that already conflict with each other
# in the given packages list -> list_of_strings
>>> [...]
>>> pairs # dictionary of conflict information that contains which package in the
# given packages list conflicts with which of the installed packages
>>> {'imlib2': <class pisi.conflict.Conflict>, 'valgrind': <class pisi.conflict.Conflict>,
'libmp4v2':'<class pisi.conflict.Conflict>}
>>> print map(lambda c:str(pairs[c]), pairs)
>>> ['imblib', 'callgrind', 'faad2 release >= 3']
"""
return pisi.conflict.calculate_conflicts(packages, pisi.db.packagedb.PackageDB())
def search_package(terms, lang=None, repo=None):
"""
Return a list of packages that contains all the given terms either in its name, summary or
description -> list_of_strings
@param terms: a list of terms used to search package -> list_of_strings
@param lang: language of the summary and description
@param repo: Repository of the packages. If repo is None than returns a list of all the packages
in all the repositories that meets the search
"""
packagedb = pisi.db.packagedb.PackageDB()
return packagedb.search_package(terms, lang, repo)
def search_source(terms, lang=None, repo=None):
"""
Return a list of source packages that contains all the given terms either in its name, summary or
description -> list_of_strings
@param terms: a list of terms used to search source package -> list_of_strings
@param lang: language of the summary and description
@param repo: Repository of the source packages. If repo is None than returns a list of all the source
packages in all the repositories that meets the search
"""
sourcedb = pisi.db.sourcedb.SourceDB()
return sourcedb.search_spec(terms, lang, repo)
def search_component(terms, lang=None, repo=None):
"""
Return a list of components that contains all the given terms either in its name, summary or
description -> list_of_strings
@param terms: a list of terms used to search components -> list_of_strings
@param lang: language of the summary and description
@param repo: Repository of the components. If repo is None than returns a list of all the components
in all the repositories that meets the search
"""
componentdb = pisi.db.componentdb.ComponentDB()
return componentdb.search_component(terms, lang, repo)
def search_file(term):
"""
Returns a tuple of package and matched files list that matches the files of the installed
packages -> list_of_tuples
@param term: used to search file -> list_of_strings
>>> files = pisi.api.search_file("kvm-")
>>> print files
>>> [("kvm", (["lib/modules/2.6.18.8-86/extra/kvm-amd.ko","lib/modules/2.6.18.8-86/extra/kvm-intel.ko"])),]
"""
filesdb = pisi.db.filesdb.FilesDB()
if term.startswith("/"): # FIXME: why? why?
term = term[1:]
return filesdb.search_file(term)
def package_graph(A, packagedb, ignore_installed = False):
"""Construct a package relations graph.
Graph will contain all dependencies of packages A, if ignore_installed
@@ -178,7 +272,7 @@ def package_graph(A, repo = pisi.db.itembyrepodb.installed, ignore_installed = F
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb, repo) # construct G_f
G_f = pgraph.PGraph(packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
@@ -189,7 +283,7 @@ def package_graph(A, repo = pisi.db.itembyrepodb.installed, ignore_installed = F
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x, repo)
pkg = packagedb.get_package(x)
#print pkg
for dep in pkg.runtimeDependencies():
if ignore_installed:
@@ -201,62 +295,30 @@ def package_graph(A, repo = pisi.db.itembyrepodb.installed, ignore_installed = F
B = Bp
return G_f
def generate_install_order(A):
# returns the install order of the given install package list with any extra
# dependency that is also going to be installed
G_f, order = plan_install(A, ignore_package_conflicts = True)
return order
def generate_remove_order(A):
# returns the remove order of the given removal package list with any extra
# reverse dependency that is also going to be removed
G_f, order = plan_remove(A)
return order
def generate_upgrade_order(A):
# returns the upgrade order of the given upgrade package list with any needed extra
# dependency
G_f, order = plan_upgrade(A)
return order
def generate_base_upgrade(A):
# all the packages of the system.base must be installed on the system.
# method returns the currently needed system.base component install and
# upgrade needs
base = upgrade_base(A, ignore_package_conflicts = True)
return list(base)
def generate_conflicts(A):
# returns the conflicting packages list of the to be installed packages.
# @conflicting_pkgs: conflicting and must be removed packages list to proceed
# @conflicts_inorder: list of the conflicting packages _with each other_ in the to be installed list
# @conflicting_pairs: dictionary that contains which package in the to be installed list conflicts
# with which packages
(conflicting_pkgs, conflicts_inorder, conflicting_pairs) = pisi.conflict.calculate_conflicts(A, ctx.packagedb)
return (conflicting_pkgs, conflicts_inorder, conflicting_pairs)
def generate_pending_order(A):
# returns pending package list in reverse topological order of dependency
G_f = pgraph.PGraph(ctx.packagedb, pisi.db.itembyrepodb.installed) # construct G_f
for x in A.keys():
installdb = pisi.db.installdb.InstallDB()
G_f = pgraph.PGraph(installdb) # construct G_f
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B.keys():
pkg = ctx.packagedb.get_package(x, pisi.db.itembyrepodb.installed)
for x in B:
pkg = installdb.get_package(x)
for dep in pkg.runtimeDependencies():
if dep.package in G_f.vertices():
G_f.add_dep(x, dep)
B = Bp
if ctx.get_option('debug'):
import sys
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
componentdb = pisi.db.componentdb.ComponentDB()
# Bug 4211
if ctx.componentdb.has_component('system.base'):
if componentdb.has_component('system.base'):
order = reorder_base_packages(order)
return order
@@ -264,18 +326,18 @@ def generate_pending_order(A):
def configure_pending():
# start with pending packages
# configure them in reverse topological order of dependency
A = ctx.installdb.list_pending()
installdb = pisi.db.installdb.InstallDB()
A = installdb.list_pending()
order = generate_pending_order(A)
try:
for x in order:
if ctx.installdb.is_installed(x):
pkginfo = A[x]
if installdb.has_package(x):
pkginfo = installdb.get_package(x)
pkgname = pisi.util.package_name(x, pkginfo.version,
pkginfo.release,
False,
False)
pkg_path = pisi.util.join_path(ctx.config.lib_dir(),
'package', pkgname)
pkg_path = pisi.util.join_path(ctx.config.packages_dir(), pkgname)
m = pisi.metadata.MetaData()
metadata_path = pisi.util.join_path(pkg_path, ctx.const.metadata_xml)
m.read(metadata_path)
@@ -294,9 +356,9 @@ def configure_pending():
m.package.release
)
ctx.ui.notify(pisi.ui.configured, package = pkginfo, files = None)
ctx.installdb.clear_pending(x)
installdb.clear_pending(x)
except ImportError:
raise Error(_("comar package is not fully installed"))
raise pisi.Error(_("comar package is not fully installed"))
def info(package, installed = False):
if package.endswith(ctx.const.package_suffix):
@@ -308,28 +370,31 @@ def info(package, installed = False):
def info_file(package_fn):
if not os.path.exists(package_fn):
raise Error (_('File %s not found') % package_fn)
raise pisi.Error (_('File %s not found') % package_fn)
package = pisi.package.Package(package_fn)
package.read()
return package.metadata, package.files
def info_name(package_name, installed=False):
def info_name(package_name, useinstalldb=False):
"""Fetch package information for the given package."""
if installed:
package = ctx.packagedb.get_package(package_name, pisi.db.itembyrepodb.installed)
installdb = pisi.db.installdb.InstallDB()
packagedb = pisi.db.packagedb.PackageDB()
if useinstalldb:
package = installdb.get_package(package_name)
repo = None
else:
package, repo = ctx.packagedb.get_package_repo(package_name, pisi.db.itembyrepodb.repos)
package, repo = packagedb.get_package_repo(package_name)
metadata = pisi.metadata.MetaData()
metadata.package = package
#FIXME: get it from sourcedb if available
metadata.source = None
#TODO: fetch the files from server if possible (wow, you maniac -- future exa)
if installed and ctx.installdb.is_installed(package.name):
if useinstalldb and installdb.has_package(package.name):
try:
files = ctx.installdb.files(package.name)
files = installdb.get_files(package.name)
except pisi.Error, e:
ctx.ui.warning(e)
files = None
@@ -337,26 +402,6 @@ def info_name(package_name, installed=False):
files = None
return metadata, files, repo
def search_package_terms(terms, repo = pisi.db.itembyrepodb.all):
return search_in_packages(terms, ctx.packagedb.list_packages(repo), repo)
def search_in_packages(terms, packages, repo = pisi.db.itembyrepodb.all):
def search(package, term):
term = unicode(term).lower()
if term in unicode(package.name).lower() or \
term in unicode(package.summary).lower() or \
term in unicode(package.description).lower():
return True
found = []
for name in packages:
pkg = ctx.packagedb.get_package(name, repo)
if terms == filter(lambda x:search(pkg, x), terms):
found.append(name)
return found
def check(package):
md, files = info(package, True)
corrupt = []
@@ -392,31 +437,30 @@ def index(dirs=None, output='pisi-index.xml', skip_sources=False, skip_signing=F
ctx.ui.info(_('* Index file written'))
def add_repo(name, indexuri, at = None):
if ctx.repodb.has_repo(name):
raise Error(_('Repo %s already present.') % name)
repodb = pisi.db.repodb.RepoDB()
if repodb.has_repo(name):
raise pisi.Error(_('Repo %s already present.') % name)
else:
repo = pisi.db.repodb.Repo(pisi.uri.URI(indexuri))
ctx.repodb.add_repo(name, repo, at = at)
repodb.add_repo(name, repo, at = at)
ctx.ui.info(_('Repo %s added to system.') % name)
def remove_repo(name):
if ctx.repodb.has_repo(name):
ctx.repodb.remove_repo(name)
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
repodb = pisi.db.repodb.RepoDB()
if repodb.has_repo(name):
repodb.remove_repo(name)
ctx.ui.info(_('Repo %s removed from system.') % name)
else:
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
raise pisi.Error(_('Repository %s does not exist. Cannot remove.')
% name)
def list_repos():
return ctx.repodb.list()
def update_repo(repo, force=False):
ctx.ui.info(_('* Updating repository: %s') % repo)
ctx.ui.notify(pisi.ui.updatingrepo, name = repo)
repodb = pisi.db.repodb.RepoDB()
index = pisi.index.Index()
if ctx.repodb.has_repo(repo):
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
if repodb.has_repo(repo):
repouri = repodb.get_repo(repo).indexuri.get_uri()
try:
index.read_uri_of_repo(repouri, repo)
except pisi.file.AlreadyHaveException, e:
@@ -432,21 +476,21 @@ def update_repo(repo, force=False):
except pisi.file.NoSignatureFound, e:
ctx.ui.warning(e)
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
ctx.ui.info(_('* Package database updated.'))
else:
raise Error(_('No repository named %s found.') % repo)
raise pisi.Error(_('No repository named %s found.') % repo)
def delete_cache():
pisi.util.clean_dir(ctx.config.packages_dir())
pisi.util.clean_dir(ctx.config.cached_packages_dir())
pisi.util.clean_dir(ctx.config.archives_dir())
pisi.util.clean_dir(ctx.config.tmp_dir())
def rebuild_repo(repo):
ctx.ui.info(_('* Rebuilding \'%s\' named repo... ') % repo)
if ctx.repodb.has_repo(repo):
repouri = pisi.uri.URI(ctx.repodb.get_repo(repo).indexuri.get_uri())
repodb = pisi.db.repodb.RepoDB()
if repodb.has_repo(repo):
repouri = pisi.uri.URI(repodb.get_repo(repo).indexuri.get_uri())
indexname = repouri.filename()
index = pisi.index.Index()
indexpath = pisi.util.join_path(ctx.config.index_dir(), repo, indexname)
@@ -458,95 +502,38 @@ def rebuild_repo(repo):
except IOError, e:
ctx.ui.warning(_("Input/Output error while reading %s: %s") % (indexpath, unicode(e)))
return
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
else:
raise Error(_('No repository named %s found.') % repo)
raise pisi.Error(_('No repository named %s found.') % repo)
# FIXME: rebuild_db is only here for filesdb and it really is ugly. we should not need any rebuild.
def rebuild_db(files=False):
assert not ctx.database
filesdb = pisi.db.filesdb.FilesDB()
installdb = pisi.db.installdb.InstallDB()
# Bug 2596
# finds and cleans duplicate package directories under '/var/lib/pisi/package'
# deletes the _older_ versioned package directories.
def clean_duplicates():
i_version = {} # installed versions
replica = []
for pkg in os.listdir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package')):
(name, ver) = pisi.util.parse_package_name(pkg)
if i_version.has_key(name):
if pisi.version.Version(ver) > pisi.version.Version(i_version[name]):
# found a greater version, older one is a replica
replica.append(name + '-' + i_version[name])
i_version[name] = ver
else:
# found an older version which is a replica
replica.append(name + '-' + ver)
else:
i_version[name] = ver
for pkg in replica:
pisi.util.clean_dir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package', pkg))
def destroy(files):
#TODO: either don't delete version files here, or remove force flag...
import bsddb3.db
for db in os.listdir(ctx.config.db_dir()):
if db.endswith('.bdb'):# or db.startswith('log'): # delete only db files
if db.startswith('files') or db.startswith('filesdbversion'):
clean = files
else:
clean = True
if clean:
fn = pisi.util.join_path(ctx.config.db_dir(), db)
#NB: there is a parameter bug with python-bsddb3, fixed in pardus
ctx.dbenv.dbremove(file=fn, flags=bsddb3.db.DB_AUTO_COMMIT)
def reload_packages(files, txn):
packages = os.listdir(pisi.util.join_path(ctx.config.lib_dir(), 'package'))
progress = ctx.ui.Progress(len(packages))
processed = 0
for package_fn in packages:
if not package_fn == "scripts":
ctx.ui.debug('Resurrecting %s' % package_fn)
pisi.api.resurrect_package(package_fn, files, txn)
processed += 1
ctx.ui.display_progress(operation = "rebuilding-db",
percent = progress.update(processed),
info = _("Rebuilding package database"))
def reload_indices():
index_dir = ctx.config.index_dir()
if os.path.exists(index_dir): # it may have been erased, or we may be upgrading from a previous version -- exa
for repo in os.listdir(index_dir):
indexuri = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, 'uri')
indexuri = open(indexuri, 'r').readline()
pisi.api.add_repo(repo, indexuri)
pisi.api.rebuild_repo(repo)
# check db schema versions
try:
shelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=False)
except KeyboardInterrupt:
raise
except Exception: #FIXME: what exception could we catch here, replace with that.
files = True # exception means the files db version was wrong
shelve.init_dbenv(write=True, writeversion=True)
destroy(files) # bye bye
def rebuild_filesdb():
for pkg in list_installed():
ctx.ui.info(_('* Adding \'%s\' to db... ') % pkg, noln=True)
files = installdb.get_files(pkg)
filesdb.add_files(pkg, files)
ctx.ui.info(_('OK.'))
# save parameters and shutdown pisi
options = ctx.config.options
ui = ctx.ui
comar = ctx.comar
finalize()
pisi._cleanup()
filesdb.destroy()
filesdb.init()
# reinitialize everything
set_userinterface(ui)
set_options(options)
set_comar(comar)
# construct new database
init(database=True, options=options, ui=ui, comar=comar)
clean_duplicates()
txn = ctx.dbenv.txn_begin()
reload_packages(files, txn)
reload_indices()
txn.commit()
rebuild_filesdb()
############# FIXME: this was a quick fix. ##############################
@@ -560,44 +547,29 @@ def rebuild_db(files=False):
# from pisi.atomicoperations import resurrect_package, build
def install(*args, **kw):
return pisi.operations.install(*args, **kw)
return pisi.operations.install.install(*args, **kw)
def remove(*args, **kw):
return pisi.operations.remove(*args, **kw)
return pisi.operations.remove.remove(*args, **kw)
def upgrade(*args, **kw):
return pisi.operations.upgrade(*args, **kw)
return pisi.operations.upgrade.upgrade(*args, **kw)
def emerge(*args, **kw):
return pisi.operations.emerge(*args, **kw)
def plan_install(*args, **kw):
return pisi.operations.plan_install_pkg_names(*args, **kw)
def plan_remove(*args, **kw):
return pisi.operations.plan_remove(*args, **kw)
def plan_upgrade(*args, **kw):
return pisi.operations.plan_upgrade(*args, **kw)
def upgrade_base(*args, **kw):
return pisi.operations.upgrade_base(*args, **kw)
return pisi.operations.emerge.emerge(*args, **kw)
def calculate_conflicts(*args, **kw):
return pisi.conflict.calculate_conflicts(*args, **kw)
def reorder_base_packages(*args, **kw):
return pisi.operations.reorder_base_packages(*args, **kw)
return pisi.operations.helper.reorder_base_packages(*args, **kw)
def build_until(*args, **kw):
return pisi.build.build_until(*args, **kw)
return pisi.operations.build.build_until(*args, **kw)
def build(*args, **kw):
return pisi.atomicoperations.build(*args, **kw)
def resurrect_package(*args, **kw):
return pisi.atomicoperations.resurrect_package(*args, **kw)
########################################################################
## Deletes the cached pisi packages to keep the package cache dir within cache limits
@@ -655,7 +627,7 @@ def clearCache(all=False):
except exceptions.OSError:
pass
cacheDir = ctx.config.packages_dir()
cacheDir = ctx.config.cached_packages_dir()
pkgList = map(lambda x: os.path.basename(x).split(".pisi")[0], glob.glob("%s/*.pisi" % cacheDir))
if not all:
+7 -8
View File
@@ -29,12 +29,11 @@ import pisi
import pisi.util as util
import pisi.context as ctx
class ArchiveError(pisi.Error):
class UnknownArchiveType(Exception):
pass
class LZMAError(pisi.Error):
def __init__(self, err):
pisi.Error.__init__(self, _("An error has occured while running LZMA:\n%s") % err)
class LzmaRuntimeError(Exception):
pass
class ArchiveBase(object):
"""Base class for Archive classes."""
@@ -118,9 +117,9 @@ class ArchiveTar(ArchiveBase):
ret, out, err = util.run_batch("lzma d %s %s" % (self.file_path + ctx.const.lzma_suffix,
self.file_path))
if ret != 0:
raise LZMAError(err)
raise LzmaRuntimeError(err)
else:
raise ArchiveError(_("Archive type not recognized"))
raise UnknownArchiveType
self.tar = tarfile.open(self.file_path, rmode)
oldwd = os.getcwd()
@@ -180,7 +179,7 @@ class ArchiveTar(ArchiveBase):
wmode = 'w:'
self.file_path = self.file_path.rstrip(ctx.const.lzma_suffix)
else:
raise ArchiveError(_("Archive type not recognized"))
raise UnknownArchiveType
self.tar = tarfile.open(self.file_path, wmode)
self.tar.add(file_name, arc_name)
@@ -197,7 +196,7 @@ class ArchiveTar(ArchiveBase):
ret, out, err = util.run_batch(batch)
if ret != 0:
raise LZMAError(err)
raise LzmaRunTimeError(err)
class MyZipFile(zipfile.ZipFile):
+46 -121
View File
@@ -16,7 +16,6 @@ __trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import os
import bsddb3.db as db
import shutil
import pisi
@@ -28,8 +27,8 @@ import pisi.files
import pisi.uri
import pisi.ui
import pisi.version
import pisi.delta
import pisi.db.packagedb
import pisi.operations.delta
import pisi.db
class Error(pisi.Error):
pass
@@ -58,18 +57,22 @@ class Install(AtomicOperation):
@staticmethod
def from_name(name, ignore_dep = None):
packagedb = pisi.db.packagedb.PackageDB()
# download package and return an installer object
# find package in repository
repo = ctx.packagedb.which_repo(name)
repo = packagedb.which_repo(name)
if repo:
repodb = pisi.db.repodb.RepoDB()
ctx.ui.info(_("Package %s found in repository %s") % (name, repo))
repo = ctx.repodb.get_repo(repo)
pkg = ctx.packagedb.get_package(name)
repo = repodb.get_repo(repo)
pkg = packagedb.get_package(name)
delta = None
installdb = pisi.db.installdb.InstallDB()
# Package is installed. This is an upgrade. Check delta.
if ctx.installdb.is_installed(pkg.name):
(version, release, build) = ctx.installdb.get_version(pkg.name)
if installdb.has_package(pkg.name):
(version, release, build) = installdb.get_version(pkg.name)
delta = pkg.get_delta(buildFrom=build)
# If delta exists than use the delta uri.
@@ -103,6 +106,8 @@ class Install(AtomicOperation):
self.metadata = self.package.metadata
self.files = self.package.files
self.pkginfo = self.metadata.package
self.filesdb = pisi.db.filesdb.FilesDB()
self.installdb = pisi.db.installdb.InstallDB()
def install(self, ask_reinstall = True):
if ctx.get_option('fetch_only'):
@@ -123,13 +128,7 @@ class Install(AtomicOperation):
self.store_pisi_files()
self.postinstall()
txn = ctx.dbenv.txn_begin()
try:
self.update_databases(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
self.update_databases()
ctx.enable_keyboard_interrupts()
@@ -156,11 +155,6 @@ class Install(AtomicOperation):
raise Error(_("%s package cannot be installed unless the dependencies are satisfied") %
self.pkginfo.name)
# check if package is in database
# If it is not, put it into 3rd party packagedb
if not ctx.packagedb.has_package(self.pkginfo.name):
ctx.packagedb.add_package(self.pkginfo, pisi.db.itembyrepodb.thirdparty)
# If it is explicitly specified that package conflicts with this package and also
# we passed check_conflicts tests in operations.py than this means a non-conflicting
# pkg is in "order" to be installed that has no file conflict problem with this package.
@@ -174,15 +168,15 @@ class Install(AtomicOperation):
# check file conflicts
file_conflicts = []
for f in self.files.list:
if ctx.filesdb.has_file(f.path):
pkg, existing_file = ctx.filesdb.get_file(f.path)
if self.filesdb.has_file(f.path):
pkg, existing_file = self.filesdb.get_file(f.path)
dst = pisi.util.join_path(ctx.config.dest_dir(), f.path)
if pkg != self.pkginfo.name and not os.path.isdir(dst) and really_conflicts(pkg):
file_conflicts.append( (pkg, existing_file) )
if file_conflicts:
file_conflicts_str = ""
for (pkg, existing_file) in file_conflicts:
file_conflicts_str += _("/%s from %s package\n") % (existing_file.path, pkg)
file_conflicts_str += _("/%s from %s package\n") % (existing_file, pkg)
msg = _('File conflicts:\n%s') % file_conflicts_str
if self.ignore_file_conflicts:
ctx.ui.warning(msg)
@@ -196,14 +190,10 @@ class Install(AtomicOperation):
self.reinstall = False
self.upgrade = False
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
#FIXME: consider REPOSITORY instead of DISTRIBUTION -- exa
#ipackage = ctx.packagedb.get_package(pkg.name, pisi.db.itembyrepodb.installed)
ipkg = ctx.installdb.get_info(pkg.name)
if self.installdb.has_package(pkg.name): # is this a reinstallation?
ipkg = self.installdb.get_info(pkg.name)
repomismatch = ipkg.distribution != pkg.distribution
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
(iversion, irelease, ibuild) = self.installdb.get_version(pkg.name)
# determine if same version
self.same_ver = False
@@ -249,8 +239,8 @@ class Install(AtomicOperation):
raise Error(_('Package downgrade declined'))
# schedule for reinstall
self.old_files = ctx.installdb.files(pkg.name)
self.old_path = ctx.installdb.pkg_dir(pkg.name, iversion, irelease)
self.old_files = self.installdb.get_files(pkg.name)
self.old_path = self.installdb.pkg_dir(pkg.name, iversion, irelease)
self.reinstall = True
self.remove_old = Remove(pkg.name)
self.remove_old.run_preremove()
@@ -341,7 +331,7 @@ class Install(AtomicOperation):
# of these files may be relocated to some other directory in the new package.
# We handle these cases here.
def relocate_files():
for old_file, new_file in pisi.delta.find_relocations(self.old_files, self.files):
for old_file, new_file in pisi.operations.delta.find_relocations(self.old_files, self.files):
old_path, new_path = ("/" + old_file.path, "/" + new_file.path)
destdir = os.path.dirname(new_path)
@@ -417,26 +407,19 @@ class Install(AtomicOperation):
ctx.ui.info(_('Storing %s') % fpath, verbose=True)
self.package.extract_file(fpath, self.package.pkg_dir())
def update_databases(self, txn):
def update_databases(self):
"update databases"
if self.reinstall:
self.remove_old.remove_db(txn)
self.remove_old.remove_db()
# installdb
ctx.installdb.install(self.metadata.package.name,
self.metadata.package.version,
self.metadata.package.release,
self.metadata.package.build,
self.metadata.package.distribution,
config_later = self.config_later,
txn = txn)
if self.config_later:
self.installdb.mark_pending(self.pkginfo.name)
# filesdb
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn=txn)
self.filesdb.add_files(self.metadata.package.name, self.files)
# installed packages
ctx.packagedb.add_package(self.pkginfo, pisi.db.itembyrepodb.installed, txn=txn)
self.installdb.add_package(self.pkginfo)
def install_single(pkg, upgrade = False):
"""install a single package from URI or ID"""
@@ -463,10 +446,12 @@ class Remove(AtomicOperation):
def __init__(self, package_name, ignore_dep = None):
super(Remove, self).__init__(ignore_dep)
self.installdb = pisi.db.installdb.InstallDB()
self.filesdb = pisi.db.filesdb.FilesDB()
self.package_name = package_name
self.package = ctx.packagedb.get_package(self.package_name, pisi.db.itembyrepodb.installed)
self.package = self.installdb.get_package(self.package_name)
try:
self.files = ctx.installdb.files(self.package_name)
self.files = self.installdb.get_files(self.package_name)
except pisi.Error, e:
# for some reason file was deleted, we still allow removes!
ctx.ui.error(unicode(e))
@@ -478,7 +463,7 @@ class Remove(AtomicOperation):
ctx.ui.status(_('Removing package %s') % self.package_name)
ctx.ui.notify(pisi.ui.removing, package = self.package, files = self.files)
if not ctx.installdb.is_installed(self.package_name):
if not self.installdb.has_package(self.package_name):
raise Exception(_('Trying to remove nonexistent package ')
+ self.package_name)
@@ -488,13 +473,7 @@ class Remove(AtomicOperation):
for fileinfo in self.files.list:
self.remove_file(fileinfo, self.package_name)
txn = ctx.dbenv.txn_begin()
try:
self.remove_db(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
self.remove_db()
self.remove_pisi_files()
ctx.ui.close()
@@ -511,12 +490,13 @@ class Remove(AtomicOperation):
def remove_file(fileinfo, package_name):
fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path)
filesdb = pisi.db.filesdb.FilesDB()
# we should check if the file belongs to another
# package (this can legitimately occur while upgrading
# two packages such that a file has moved from one package to
# another as in #2911)
if ctx.filesdb.has_file(fileinfo.path):
pkg, existing_file = ctx.filesdb.get_file(fileinfo.path)
if filesdb.has_file(fileinfo.path):
pkg, existing_file = filesdb.get_file(fileinfo.path)
if pkg != package_name:
ctx.ui.warning(_('Not removing conflicted file : %s') % fpath)
return
@@ -559,11 +539,12 @@ class Remove(AtomicOperation):
def remove_pisi_files(self):
util.clean_dir(self.package.pkg_dir())
def remove_db(self, txn):
ctx.installdb.remove(self.package_name, txn)
ctx.filesdb.remove_files(self.files, txn)
# FIXME: something goes wrong here, if we use ctx operations ends up with segmentation fault!
pisi.db.packagedb.remove_tracking_package(self.package_name, txn)
def remove_db(self):
self.installdb.remove_package(self.package_name)
self.filesdb.remove_files(self.files.list)
# FIX:DB
# # FIXME: something goes wrong here, if we use ctx operations ends up with segmentation fault!
# pisi.db.packagedb.remove_tracking_package(self.package_name)
def remove_single(package_name):
@@ -571,61 +552,5 @@ def remove_single(package_name):
def build(package):
# wrapper for build op
import pisi.build
return pisi.build.build(package)
def virtual_install(metadata, files, txn):
"""Recreate the package info for rebuilddb command"""
# installdb
ctx.installdb.install(metadata.package.name,
metadata.package.version,
metadata.package.release,
metadata.package.build,
metadata.package.distribution,
rebuild=True,
txn=txn)
# filesdb
if files:
ctx.filesdb.add_files(metadata.package.name, files, txn=txn)
# installed packages
ctx.packagedb.add_package(metadata.package, pisi.db.itembyrepodb.installed, txn=txn)
def resurrect_package(package_fn, write_files, txn = None):
"""Resurrect the package from xml files"""
metadata_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.metadata_xml)
if not os.path.exists(metadata_xml):
raise Error, _("Metadata XML '%s' cannot be found") % metadata_xml
metadata = pisi.metadata.MetaData()
metadata.read(metadata_xml)
errs = metadata.errors()
if errs:
util.print_errors(errs)
raise Error, _("MetaData format wrong (%s)") % package_fn
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
if write_files:
files_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.files_xml)
if not os.path.exists(files_xml):
raise Error, _("Files XML '%s' cannot be found") % files_xml
files = pisi.files.Files()
files.read(files_xml)
if files.errors():
raise Error, _("Invalid %s") % ctx.const.files_xml
else:
files = None
#import pisi.atomicoperations
def f(t):
pisi.atomicoperations.virtual_install(metadata, files, t)
ctx.txn_proc(f, txn)
ctx.ui.info(_('OK.'))
import pisi.operations.build
return pisi.operations.build.build(package)
+6 -6
View File
@@ -121,20 +121,20 @@ class CLI(pisi.ui.UI):
return False
def display_progress(self, operation, percent, info="", **ka):
def display_progress(self, **ka):
""" display progress of any operation """
if operation in ["removing", "rebuilding-db"]:
if ka['operation'] in ["removing", "rebuilding-db"]:
return
elif operation == "fetching":
elif ka['operation'] == "fetching":
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
(ka['filename'], totalsize, percent,
(ka['filename'], totalsize, ka['percent'],
ka['rate'], ka['symbol'], ka['eta'])
self.output(out)
else:
self.output("\r%s (%d%%)" % (info, percent))
self.output("\r%s (%d%%)" % (ka['info'], ka['percent']))
if percent == 100:
if ka['percent'] == 100:
self.output(pisi.util.colorize(_(' [complete]\n'), 'gray'))
def status(self, msg = None):
+69
View File
@@ -0,0 +1,69 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.api
import pisi.cli.command as command
import pisi.context as ctx
class AddRepo(command.Command):
"""Add a repository
Usage: add-repo <repo> <indexuri>
<repo>: name of repository to add
<indexuri>: URI of index file
If no repo is given, add-repo pardus-devel repo is added by default
NB: We support only local files (e.g., /a/b/c) and http:// URIs at the moment
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(AddRepo, self).__init__(args)
name = ("add-repo", "ar")
def options(self):
group = optparse.OptionGroup(self.parser, _("add-repo options"))
group.add_option("--at", action="store",
type="int", default=None,
help=_("Add repository at given position (0 is first)"))
self.parser.add_option_group(group)
def run(self):
if len(self.args)==2 or len(self.args)==0:
self.init()
if len(self.args)==2:
name = self.args[0]
indexuri = self.args[1]
else:
name = 'pardus-2007'
indexuri = 'http://paketler.pardus.org.tr/pardus-2007/pisi-index.xml.bz2'
pisi.api.add_repo(name, indexuri, ctx.get_option('at'))
if ctx.ui.confirm(_('Update PiSi database for repository %s?') % name):
try:
pisi.api.update_repo(name)
except pisi.fetcher.FetchError:
ctx.ui.warning(_("%s repository could not be reached. Removing %s from system.") % (name, name))
pisi.api.remove_repo(name)
else:
self.help()
return
+119
View File
@@ -0,0 +1,119 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.api
import pisi.cli.command as command
import pisi.context as ctx
class Build(command.Command):
"""Build PiSi packages
Usage: build [<pspec.xml> | <sourcename>] ...
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__ = command.autocommand
def __init__(self, args):
super(Build, self).__init__(args)
self.comar = True
name = ("build", "bi")
package_formats = ('1.0', '1.1')
def options(self):
self.add_steps_options()
group = optparse.OptionGroup(self.parser, _("build options"))
self.add_options(group)
self.parser.add_option_group(group)
def add_options(self, group):
group.add_option("--ignore-build-no", action="store_true",
default=False,
help=_("Do not take build no into account."))
group.add_option("--ignore-dependency", action="store_true",
default=False,
help=_("Do not take dependency information into account"))
group.add_option("-O", "--output-dir", action="store", default=None,
help=_("Output directory for produced packages"))
group.add_option("--ignore-action-errors",
action="store_true", default=False,
help=_("Bypass errors from ActionsAPI"))
group.add_option("--ignore-safety", action="store_true",
default=False, help=_("Bypass safety switch"))
group.add_option("--ignore-check", action="store_true",
default=False, help=_("Bypass testing step"))
group.add_option("--create-static", action="store_true",
default=False, help=_("Create a static package with ar files"))
group.add_option("--no-install", action="store_true",
default=False, help=_("Do not install build dependencies, fail if a build dependency is present"))
group.add_option("-F", "--package-format", action="store", default='1.1',
help=_("PiSi binary package formats: '1.0', '1.1' (default)"))
group.add_option("--use-quilt", action="store_true", default=False,
help=_("Use quilt patch management system instead of GNU patch"))
group.add_option("--enable-sandbox", action="store_true", default=False,
help=_("Constrain build process inside the build folder"))
def add_steps_options(self):
group = optparse.OptionGroup(self.parser, _("build steps"))
group.add_option("--fetch", dest="until", action="store_const",
const="fetch", help=_("Break build after fetching the source archive"))
group.add_option("--unpack", dest="until", action="store_const",
const="unpack", help=_("Break build after unpacking the source archive, checking sha1sum and applying patches"))
group.add_option("--setup", dest="until", action="store_const",
const="setup", help=_("Break build after running configure step"))
group.add_option("--build", dest="until", action="store_const",
const="build", help=_("Break build after running compile step"))
group.add_option("--check", dest="until", action="store_const",
const="check", help=_("Break build after running check step"))
group.add_option("--install", dest="until", action="store_const",
const="install", help=_("Break build after running install step"))
group.add_option("--package", dest="until", action="store_const",
const="package", help=_("create PiSi package"))
self.parser.add_option_group(group)
def run(self):
if not self.args:
self.help()
return
if self.options.no_install:
self.init(database=True, write=False)
else:
self.init()
if ctx.get_option('package_format') not in Build.package_formats:
raise Error(_('package_format must be one of %s ') % pisi.util.strlist(Build.package_formats))
if ctx.get_option('output_dir'):
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
else:
ctx.ui.info(_('Outputting packages in the working directory.'))
ctx.config.options.output_dir = '.'
for x in self.args:
if ctx.get_option('until'):
pisi.api.build_until(x, ctx.get_option('until'))
else:
pisi.api.build(x)
+77
View File
@@ -0,0 +1,77 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.api
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class Check(command.Command):
"""Verify installation
Usage: check [<package1> <package2> ... <packagen>]
<packagei>: package name
A cryptographic checksum is stored for each installed
file. Check command uses the checksums to verify a package.
Just give the names of packages.
If no packages are given, checks all installed packages.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Check, self).__init__(args)
self.installdb = pisi.db.installdb.InstallDB()
self.componentdb = pisi.db.componentdb.ComponentDB()
name = ("check", None)
def options(self):
group = optparse.OptionGroup(self.parser, _("check options"))
group.add_option("-c", "--component", action="store",
default=None, help=_("Check installed packages under given component"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
component = ctx.get_option('component')
if component:
#FIXME: pisi api is insufficient to do this
from sets import Set as set
installed = pisi.api.list_installed()
component_pkgs = self.componentdb.get_union_packages(component, walk=True)
pkgs = list(set(installed) & set(component_pkgs))
elif self.args:
pkgs = self.args
else:
ctx.ui.info(_('Checking all installed packages'))
pkgs = pisi.api.list_installed()
for pkg in pkgs:
ctx.ui.info(_('* Checking %s... ') % pkg, noln=True)
if self.installdb.has_package(pkg):
corrupt = pisi.api.check(pkg)
if corrupt:
ctx.ui.info(_('\nPackage %s is corrupt.') % pkg)
else:
ctx.ui.info(_("OK"), verbose=False)
else:
ctx.ui.info(_('Package %s not installed') % pkg)
+32
View File
@@ -0,0 +1,32 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi.cli.command as command
class Clean(command.Command):
"""Clean stale locks
Usage: clean
PiSi uses filesystem locks for managing database access.
This command deletes unused locks from the database directory."""
__metaclass__ = command.autocommand
def __init__(self, args=None):
super(Clean, self).__init__(args)
name = ("clean", None)
def run(self):
self.init()
+269
View File
@@ -0,0 +1,269 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.api
import pisi.context as ctx
class autocommand(type):
def __init__(cls, name, bases, dict):
super(autocommand, cls).__init__(name, bases, dict)
Command.cmd.append(cls)
name = getattr(cls, 'name', None)
if name is None:
raise pisi.cli.Error(_('Command lacks name'))
longname, shortname = name
def add_cmd(cmd):
if Command.cmd_dict.has_key(cmd):
raise pisi.cli.Error(_('Duplicate command %s') % cmd)
else:
Command.cmd_dict[cmd] = cls
add_cmd(longname)
if shortname:
add_cmd(shortname)
class Command(object):
"""generic help string for any command"""
# class variables
cmd = []
cmd_dict = {}
@staticmethod
def commands_string():
s = ''
l = [x.name[0] for x in Command.cmd]
l.sort()
for name in l:
commandcls = Command.cmd_dict[name]
trans = gettext.translation('pisi', fallback=True)
summary = trans.ugettext(commandcls.__doc__).split('\n')[0]
name = commandcls.name[0]
if commandcls.name[1]:
name += ' (%s)' % commandcls.name[1]
s += '%21s - %s\n' % (name, summary)
return s
@staticmethod
def get_command(cmd, fail=False, args=None):
if Command.cmd_dict.has_key(cmd):
return Command.cmd_dict[cmd](args)
if fail:
raise Error(_("Unrecognized command: %s") % cmd)
else:
return None
# instance variabes
def __init__(self, args = None):
# now for the real parser
import pisi
self.comar = False
self.parser = optparse.OptionParser(usage=getattr(self, "__doc__"),
version="%prog " + pisi.__version__,
formatter=PisiHelpFormatter())
self.options()
self.commonopts()
(self.options, self.args) = self.parser.parse_args(args)
if self.args:
self.args.pop(0) # exclude command arg
self.process_opts()
def commonopts(self):
'''common options'''
p = self.parser
group = optparse.OptionGroup(self.parser, _("general options"))
group.add_option("-D", "--destdir", action="store", default = None,
help = _("Change the system root for PiSi commands"))
group.add_option("-y", "--yes-all", action="store_true",
default=False, help = _("Assume yes in all yes/no queries"))
group.add_option("-u", "--username", action="store")
group.add_option("-p", "--password", action="store")
group.add_option("-v", "--verbose", action="store_true",
dest="verbose", default=False,
help=_("Detailed output"))
group.add_option("-d", "--debug", action="store_true",
default=False, help=_("Show debugging information"))
group.add_option("-N", "--no-color", action="store_true", default=False,
help = _("Suppresses all coloring of PiSi's output"))
p.add_option_group(group)
return p
def options(self):
"""This is a fall back function. If the implementer module provides an
options function it will be called"""
pass
def process_opts(self):
self.check_auth_info()
# make destdir absolute
if self.options.destdir:
d = str(self.options.destdir)
import os.path
if not os.path.exists(d):
pisi.cli.printu(_('Destination directory %s does not exist. Creating directory.\n') % d)
os.makedirs(d)
self.options.destdir = os.path.realpath(d)
def check_auth_info(self):
username = self.options.username
password = self.options.password
# TODO: We'll get the username, password pair from a configuration
# file from users home directory. Currently we need user to
# give it from the user interface.
# if not username and not password:
# if someauthconfig.username and someauthconfig.password:
# self.authInfo = (someauthconfig.username,
# someauthconfig.password)
# return
if username and password:
self.options.authinfo = (username, password)
return
if username and not password:
from getpass import getpass
password = getpass(_("Password: "))
self.options.authinfo = (username, password)
else:
self.options.authinfo = None
def init(self, database = True, write = True):
"""initialize PiSi components"""
if self.options:
ui = pisi.cli.CLI(self.options.debug, self.options.verbose)
else:
ui = pisi.cli.CLI()
pisi.api.set_userinterface(ui)
pisi.api.set_options(self.options)
pisi.api.set_comar(self.comar and not ctx.get_option('ignore_comar'))
def get_name(self):
return self.__class__.name
def format_name(self):
(name, shortname) = self.get_name()
if shortname:
return "%s (%s)" % (name, shortname)
else:
return name
def help(self):
"""print help for the command"""
trans = gettext.translation('pisi', fallback=True)
print "%s: %s\n" % (self.format_name(), trans.ugettext(self.__doc__))
print self.parser.format_option_help()
def die(self):
"""exit program"""
#FIXME: not called from anywhere?
ctx.ui.error(_('Command terminated abnormally.'))
sys.exit(-1)
class PackageOp(Command):
"""Abstract package operation command"""
def __init__(self, args):
super(PackageOp, self).__init__(args)
self.comar = True
def options(self, group):
group.add_option("--ignore-dependency", action="store_true",
default=False,
help=_("Do not take dependency information into account"))
group.add_option("--ignore-comar", action="store_true",
default=False, help=_("Bypass comar configuration agent"))
group.add_option("--ignore-safety", action="store_true",
default=False, help=_("Bypass safety switch"))
group.add_option("-n", "--dry-run", action="store_true", default=False,
help = _("Do not perform any action, just show what would be done"))
def init(self, database=True, write=True):
super(PackageOp, self).init(database, write)
class PisiHelpFormatter(optparse.HelpFormatter):
def __init__(self,
indent_increment=1,
max_help_position=32,
width=None,
short_first=1):
optparse.HelpFormatter.__init__(
self, indent_increment, max_help_position, width, short_first)
self._short_opt_fmt = "%s"
self._long_opt_fmt = "%s"
def format_usage(self, usage):
return _("usage: %s\n") % usage
def format_heading(self, heading):
return "%*s%s:\n" % (self.current_indent, "", heading)
def format_option_strings(self, option):
"""Return a comma-separated list of option strings & metavariables."""
if option.takes_value():
short_opts = [self._short_opt_fmt % sopt
for sopt in option._short_opts]
long_opts = [self._long_opt_fmt % lopt
for lopt in option._long_opts]
else:
short_opts = option._short_opts
long_opts = option._long_opts
if long_opts and short_opts:
opt = "%s [%s]" % (short_opts[0], long_opts[0])
else:
opt = long_opts[0] or short_opts[0]
if option.takes_value():
opt += " arg"
return opt
def format_option(self, option):
import textwrap
result = []
opts = self.option_strings[option]
opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width:
opts = "%*s%s\n" % (self.current_indent, "", opts)
indent_first = self.help_position
else: # start help on same line as opts
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
indent_first = 0
result.append(opts)
if option.help:
help_text = self.expand_default(option)
help_lines = textwrap.wrap(help_text, self.help_width)
result.append(": %*s%s\n" % (indent_first, "", help_lines[0]))
result.extend([" %*s%s\n" % (self.help_position, "", line)
for line in help_lines[1:]])
elif opts[-1] != "\n":
result.append("\n")
return "".join(result)
-1688
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.api
import pisi.cli.command as command
class ConfigurePending(command.PackageOp):
"""Configure pending packages
If COMAR configuration of some packages were not
done at installation time, they are added to a list
of packages waiting to be configured. This command
configures those packages.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ConfigurePending, self).__init__(args)
name = ("configure-pending", "cp")
def options(self):
group = optparse.OptionGroup(self.parser, _("configure-pending options"))
super(ConfigurePending, self).options(group)
self.parser.add_option_group(group)
def run(self):
self.init()
pisi.api.configure_pending()
+35
View File
@@ -0,0 +1,35 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi
import pisi.api
import pisi.cli.command as command
class DeleteCache(command.Command):
"""Delete cache files
Usage: delete-cache
Sources, packages and temporary files are stored
under /var directory. Since these accumulate they can
consume a lot of disk space."""
__metaclass__ = command.autocommand
def __init__(self, args=None):
super(DeleteCache, self).__init__(args)
name = ("delete-cache", "dc")
def run(self):
self.init(database=False, write=True)
pisi.api.delete_cache()
+67
View File
@@ -0,0 +1,67 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
class Delta(command.Command):
"""Creates delta PiSi packages
Usage: delta oldpackage newpackage
Delta command finds the changed files between the given packages by comparing the sha1sum of the files
and creates a delta pisi package with the changed files between two releases.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Delta, self).__init__(args)
name = ("delta", "dt")
def options(self):
group = optparse.OptionGroup(self.parser, _("delta options"))
self.add_options(group)
self.parser.add_option_group(group)
def add_options(self, group):
group.add_option("-O", "--output-dir", action="store", default=None,
help=_("Output directory for produced packages"))
def run(self):
from pisi.operations.delta import create_delta_package
self.init(database=False, write=False)
if len(self.args) != 2:
self.help()
return
if ctx.get_option('output_dir'):
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
else:
ctx.ui.info(_('Outputting packages in the working directory.'))
ctx.config.options.output_dir = '.'
oldpackage = self.args[0]
newpackage = self.args[1]
create_delta_package(oldpackage, newpackage)
+66
View File
@@ -0,0 +1,66 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.cli.build as build
import pisi.context as ctx
import pisi.api
class Emerge(build.Build):
"""Build and install PiSi source packages from repository
Usage: emerge <sourcename> ...
You should give the name of a source package to be
downloaded from a repository containing sources.
You can also give the name of a component.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Emerge, self).__init__(args)
self.comar = True
name = ("emerge", "em")
def options(self):
group = optparse.OptionGroup(self.parser, _("emerge options"))
super(Emerge, self).add_options(group)
group.add_option("--ignore-file-conflicts", action="store_true",
default=False, help=_("Ignore file conflicts"))
group.add_option("--ignore-package-conflicts", action="store_true",
default=False, help=_("Ignore package conflicts"))
group.add_option("--ignore-comar", action="store_true",
default=False, help=_("Bypass comar configuration agent"))
self.parser.add_option_group(group)
def run(self):
if not self.args:
self.help()
return
self.init(database = True)
if ctx.get_option('output_dir'):
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
else:
ctx.ui.info(_('Outputting binary packages in the package cache.'))
ctx.config.options.output_dir = ctx.config.cached_packages_dir()
pisi.api.emerge(self.args)
+92
View File
@@ -0,0 +1,92 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.api
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class Graph(command.Command):
"""Graph package relations
Usage: graph [<package1> <package2> ...]
Write a graph of package relations, tracking dependency and
conflicts relations starting from given packages. By default
shows the package relations among repository packages, and writes
the package in graphviz format to 'pgraph.dot'.
"""
__metaclass__ = command.autocommand
def __init__(self, args=None):
super(Graph, self).__init__(args)
def options(self):
group = optparse.OptionGroup(self.parser, _("graph options"))
group.add_option("-r", "--repository", action="store",
default=None,
help=_("Specify a particular repository"))
group.add_option("-i", "--installed", action="store_true",
default=False,
help=_("Graph of installed packages"))
group.add_option("--ignore-installed", action="store_true",
default=False,
help=_("Do not show installed packages"))
group.add_option("-o", "--output", action="store",
default='pgraph.dot',
help=_("Dot output file"))
self.parser.add_option_group(group)
name = ("graph", None)
def run(self):
self.init(write=False)
if not ctx.get_option('installed'):
# Graph from package database
packagedb = pisi.db.packagedb.PackageDB()
if ctx.get_option('repository'):
repo = ctx.get_option('repository')
ctx.ui.info(_('Plotting packages in repository %s') % repo)
else:
repo = None
ctx.ui.info(_('Plotting a graph of relations among all repository packages'))
if self.args:
a = self.args
else:
a = pisi.api.list_available(repo)
else:
# Graph from installed packages database
packagedb = pisi.db.installdb.InstallDB()
if self.args:
a = self.args
else:
# if A is empty, then graph all packages
ctx.ui.info(_('Plotting a graph of relations among all installed packages'))
a = pisi.api.list_installed()
g = pisi.api.package_graph(a, packagedb,
ignore_installed = ctx.get_option('ignore_installed'))
g.write_graphviz(file(ctx.get_option('output'), 'w'))
+59
View File
@@ -0,0 +1,59 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli
import pisi.cli.command as command
import pisi.context as ctx
class Help(command.Command):
"""Prints help for given commands
Usage: help [ <command1> <command2> ... <commandn> ]
If run without parameters, it prints the general help."""
__metaclass__ = command.autocommand
def __init__(self, args = None):
super(Help, self).__init__(args)
name = ("help", "?")
def run(self):
if not self.args:
self.parser.set_usage(usage_text)
pisi.cli.printu(self.parser.format_help())
return
self.init(database = False, write = False)
for arg in self.args:
obj = command.Command.get_command(arg, True)
obj.help()
ctx.ui.info('')
usage_text1 = _("""%prog [options] <command> [arguments]
where <command> is one of:
""")
usage_text2 = _("""
Use \"%prog help <command>\" for help on a specific command.
""")
usage_text = (usage_text1 + command.Command.commands_string() + usage_text2)
+73
View File
@@ -0,0 +1,73 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
class Index(command.Command):
"""Index PiSi files in a given directory
Usage: index <directory> ...
This command searches for all PiSi files in a directory, collects PiSi
tags from them and accumulates the information in an output XML file,
named by default 'pisi-index.xml'. In particular, it indexes both
source and binary packages.
If you give multiple directories, the command still works, but puts
everything in a single index file.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Index, self).__init__(args)
name = ("index", "ix")
def options(self):
group = optparse.OptionGroup(self.parser, _("index options"))
group.add_option("-a", "--absolute-urls", action="store_true",
default=False,
help=_("Store absolute links for indexed files."))
group.add_option("-o", "--output", action="store",
default='pisi-index.xml',
help=_("Index output file"))
group.add_option("--skip-sources", action="store_true",
default=False,
help=_("Do not index PiSi spec files."))
group.add_option("--skip-signing", action="store_true",
default=False,
help=_("Do not sign index."))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
from pisi.api import index
if len(self.args)>0:
index(self.args, ctx.get_option('output'),
skip_sources = ctx.get_option('skip_sources'),
skip_signing = ctx.get_option('skip_signing'))
elif len(self.args)==0:
ctx.ui.info(_('Indexing current directory.'))
index(['.'], ctx.get_option('output'),
skip_sources = ctx.get_option('skip_sources'),
skip_signing = ctx.get_option('skip_signing'))
+160
View File
@@ -0,0 +1,160 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.util as util
import pisi.api
import pisi.db
class Info(command.Command):
"""Display package information
Usage: info <package1> <package2> ... <packagen>
<packagei> is either a package name or a .pisi file,
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Info, self).__init__(args)
self.installdb = pisi.db.installdb.InstallDB()
self.componentdb = pisi.db.componentdb.ComponentDB()
self.packagedb = pisi.db.packagedb.PackageDB()
name = ("info", None)
def options(self):
group = optparse.OptionGroup(self.parser, _("info options"))
self.add_options(group)
self.parser.add_option_group(group)
def add_options(self, group):
group.add_option("-f", "--files", action="store_true",
default=False,
help=_("Show a list of package files."))
group.add_option("-c", "--component", action="append",
default=None, help=_("Info about the given component"))
group.add_option("-F", "--files-path", action="store_true",
default=False,
help=_("Show only paths."))
group.add_option("-s", "--short", action="store_true",
default=False, help=_("Do not show details"))
group.add_option("--xml", action="store_true",
default=False, help=_("Output in xml format"))
def run(self):
self.init(database = True, write = False)
components = ctx.get_option('component')
if not components and not self.args:
self.help()
return
index = pisi.index.Index()
index.distribution = None
# info of components
if components:
for name in components:
if self.componentdb.has_component(name):
component = self.componentdb.get_union_component(name)
if self.options.xml:
index.add_component(component)
else:
if not self.options.short:
ctx.ui.info(unicode(component))
else:
ctx.ui.info("%s - %s" % (component.name, component.summary))
# info of packages
for arg in self.args:
if self.options.xml:
index.packages.append(pisi.api.info(arg)[0].package)
else:
self.info_package(arg)
if self.options.xml:
errs = []
index.newDocument()
index.encode(index.rootNode(), errs)
index.writexmlfile(sys.stdout)
sys.stdout.write('\n')
def info_package(self, arg):
if arg.endswith(ctx.const.package_suffix):
self.pisifile_info(arg)
return
self.installdb_info(arg)
self.packagedb_info(arg)
def print_files(self, files):
files.list.sort(key = lambda x:x.path)
for fileinfo in files.list:
if self.options.files:
print fileinfo
else:
print "/" + fileinfo.path
def print_metadata(self, metadata, packagedb):
if ctx.get_option('short'):
pkg = metadata.package
ctx.ui.info('%15s - %s' % (pkg.name, unicode(pkg.summary)))
else:
ctx.ui.info(unicode(metadata.package))
revdeps = [name for name, dep in packagedb.get_rev_deps(metadata.package.name)]
print _('Reverse Dependencies:'), util.strlist(revdeps)
print
def pisifile_info(self, package):
metadata, files = pisi.api.info_file(package)
ctx.ui.info(_('Package file: %s') % package)
self.print_pkginfo(metadata, files)
def installdb_info(self, package):
if self.installdb.has_package(package):
metadata, files, repo = pisi.api.info_name(package, True)
if self.options.files or self.options.files_path:
self.print_files(files)
return
if self.options.short:
ctx.ui.info(_('[inst] '), noln=True)
else:
ctx.ui.info(_('Installed package:'))
self.print_metadata(metadata, self.installdb)
else:
ctx.ui.info(_("%s is not installed") % package)
def packagedb_info(self, package):
if self.packagedb.has_package(package):
metadata, files, repo = pisi.api.info_name(package, False)
if self.options.short:
ctx.ui.info(_('[repo] '), noln=True)
else:
ctx.ui.info(_('Package found in %s repository:') % repo)
self.print_metadata(metadata, self.packagedb)
else:
ctx.ui.info(_("%s is not found in repositories") % package)
+81
View File
@@ -0,0 +1,81 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
import pisi.db
class Install(command.PackageOp):
"""Install PiSi packages
Usage: install <package1> <package2> ... <packagen>
You may use filenames, URI's or package names for packages. If you have
specified a package name, it should exist in a specified repository.
You can also specify components instead of package names, which will be
expanded to package names.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Install, self).__init__(args)
self.componentdb = pisi.db.componentdb.ComponentDB()
name = "install", "it"
def options(self):
group = optparse.OptionGroup(self.parser, _("install options"))
super(Install, self).options(group)
group.add_option("--ignore-build-no", action="store_true",
default=False,
help=_("Do not take build no into account."))
group.add_option("--reinstall", action="store_true",
default=False, help=_("Reinstall already installed packages"))
group.add_option("--ignore-file-conflicts", action="store_true",
default=False, help=_("Ignore file conflicts"))
group.add_option("--ignore-package-conflicts", action="store_true",
default=False, help=_("Ignore package conflicts"))
group.add_option("-c", "--component", action="append",
default=None, help=_("Install component's and recursive components' packages"))
group.add_option("-f", "--fetch-only", action="store_true",
default=False, help=_("Fetch upgrades but do not install."))
self.parser.add_option_group(group)
def run(self):
if self.options.fetch_only:
self.init(database=True, write=False)
else:
self.init()
components = ctx.get_option('component')
if not components and not self.args:
self.help()
return
packages = []
if components:
for name in components:
if self.componentdb.has_component(name):
packages.extend(self.componentdb.get_union_packages(name, walk=True))
packages.extend(self.args)
pisi.api.install(packages, ctx.get_option('reinstall'))
+90
View File
@@ -0,0 +1,90 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.util as util
import pisi.api
import pisi.db
class ListAvailable(command.Command):
"""List available packages in the repositories
Usage: list-available [ <repo1> <repo2> ... repon ]
Gives a brief list of PiSi packages published in the specified
repositories. If no repository is specified, we list packages in
all repositories.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListAvailable, self).__init__(args)
self.componentdb = pisi.db.componentdb.ComponentDB()
self.packagedb = pisi.db.packagedb.PackageDB()
name = ("list-available", "la")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-available options"))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
group.add_option("-c", "--component", action="store",
default=None, help=_("List available packages under given component"))
group.add_option("-U", "--uninstalled", action="store_true",
default=False, help=_("Show uninstalled packages only"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
if not (ctx.get_option('no_color') or ctx.config.get_option('uninstalled')):
ctx.ui.info(util.colorize(_('Installed packages are shown in this color'), 'green'))
if self.args:
for arg in self.args:
self.print_packages(arg)
else:
# print for all repos
for repo in pisi.api.list_repos():
ctx.ui.info(_("Repository : %s\n") % repo)
self.print_packages(repo)
def print_packages(self, repo):
component = ctx.get_option('component')
if component:
l = self.componentdb.get_packages(component, repo=repo, walk=True)
else:
l = pisi.api.list_available(repo)
installed_list = pisi.api.list_installed()
l.sort()
for p in l:
package = self.packagedb.get_package(p, repo)
if self.options.long:
ctx.ui.info(unicode(package))
else:
lenp = len(p)
if p in installed_list:
if ctx.config.get_option('uninstalled'):
continue
p = util.colorize(p, 'green')
p = p + ' ' * max(0, 15 - lenp)
ctx.ui.info('%s - %s ' % (p, unicode(package.summary)))
+60
View File
@@ -0,0 +1,60 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class ListComponents(command.Command):
"""List available components
Usage: list-components
Gives a brief list of PiSi components published in the
repositories.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListComponents, self).__init__(args)
self.componentdb = pisi.db.componentdb.ComponentDB()
name = ("list-components", "lc")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-components options"))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
l = self.componentdb.list_components()
l.sort()
for p in l:
component = self.componentdb.get_component(p)
if self.options.long:
ctx.ui.info(unicode(component))
else:
lenp = len(p)
#if p in installed_list:
# p = util.colorize(p, 'cyan')
p = p + ' ' * max(0, 15 - lenp)
ctx.ui.info('%s - %s ' % (component.name, unicode(component.summary)))
+76
View File
@@ -0,0 +1,76 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
import pisi.db
class ListInstalled(command.Command):
"""Print the list of all installed packages
Usage: list-installed
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListInstalled, self).__init__(args)
self.installdb = pisi.db.installdb.InstallDB()
self.componentdb = pisi.db.componentdb.ComponentDB()
name = ("list-installed", "li")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-installed options"))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
group.add_option("-c", "--component", action="store",
default=None, help=_("List installed packages under given component"))
group.add_option("-i", "--install-info", action="store_true",
default=False, help=_("Show detailed install info"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
installed = self.installdb.list_installed()
component = ctx.get_option('component')
if component:
#FIXME: pisi api is insufficient to do this
from sets import Set as set
component_pkgs = self.componentdb.get_union_packages(component, walk=True)
installed = list(set(installed) & set(component_pkgs))
installed.sort()
if self.options.install_info:
ctx.ui.info(_('Package Name |St| Version| Rel.| Build| Distro| Date'))
print '========================================================================'
for pkg in installed:
package = self.installdb.get_package(pkg)
inst_info = self.installdb.get_info(pkg)
if self.options.long:
ctx.ui.info(unicode(package))
ctx.ui.info(unicode(inst_info))
elif self.options.install_info:
ctx.ui.info('%-15s |%s' % (package.name, inst_info.one_liner()))
else:
ctx.ui.info('%15s - %s' % (package.name, unicode(package.summary)))
+43
View File
@@ -0,0 +1,43 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
class ListPending(command.Command):
"""List pending packages
Lists packages waiting to be configured.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListPending, self).__init__(args)
name = ("list-pending", "lp")
def run(self):
self.init(database = True, write = False)
A = pisi.api.list_pending()
order = pisi.api.generate_pending_order(A)
if len(order):
for p in order:
print p
else:
ctx.ui.info(_('There are no packages waiting to be configured'))
+38
View File
@@ -0,0 +1,38 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class ListRepo(command.Command):
"""List repositories
Usage: list-repo
Lists currently tracked repositories.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListRepo, self).__init__(args)
self.repodb = pisi.db.repodb.RepoDB()
name = ("list-repo", "lr")
def run(self):
self.init(database = True, write = False)
for repo in self.repodb.list_repos():
ctx.ui.info(repo)
#FIXME: repodb.get_repo(repo).indexuri.get_uri()??? ick!
print ' ', self.repodb.get_repo(repo).indexuri.get_uri()
+60
View File
@@ -0,0 +1,60 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class ListSources(command.Command):
"""List available sources
Usage: list-sources
Gives a brief list of sources published in the repositories.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListSources, self).__init__(args)
self.sourcedb = pisi.db.sourcedb.SourceDB()
name = ("list-sources", "ls")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-sources options"))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
l = self.sourcedb.list_sources()
l.sort()
for p in l:
sf, repo = self.sourcedb.get_spec_repo(p)
if self.options.long:
ctx.ui.info('[Repository: ' + repo + ']')
ctx.ui.info(unicode(sf.source))
else:
lenp = len(p)
#if p in installed_list:
# p = util.colorize(p, 'cyan')
p = p + ' ' * max(0, 15 - lenp)
ctx.ui.info('%s - %s' % (sf.source.name, unicode(sf.source.summary)))
+80
View File
@@ -0,0 +1,80 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
import pisi.db
class ListUpgrades(command.Command):
"""List packages to be upgraded
Usage: list-upgrades
Lists the packages that will be upgraded.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(ListUpgrades, self).__init__(args)
self.componentdb = pisi.db.componentdb.ComponentDB()
self.installdb = pisi.db.installdb.InstallDB()
name = ("list-upgrades", "lu")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-upgrades options"))
group.add_option("--ignore-build-no", action="store_true",
default=False,
help=_("Do not take build no into account."))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
group.add_option("-c", "--component", action="store",
default=None, help=_("List upgradable packages under given component"))
group.add_option("-i", "--install-info", action="store_true",
default=False, help=_("Show detailed install info"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
upgradable_pkgs = pisi.api.list_upgradable()
component = ctx.get_option('component')
if component:
#FIXME: PiSi api is insufficient to do this
from sets import Set as set
component_pkgs = self.componentdb.get_union_packages(component, walk=True)
upgradable_pkgs = list(set(upgradable_pkgs) & set(component_pkgs))
if not upgradable_pkgs:
ctx.ui.info(_('No packages to upgrade.'))
upgradable_pkgs.sort()
if self.options.install_info:
ctx.ui.info(_('Package Name |St| Version| Rel.| Build| Distro| Date'))
print '========================================================================'
for pkg in upgradable_pkgs:
package = self.installdb.get_package(pkg)
inst_info = self.installdb.get_info(pkg)
if self.options.long:
ctx.ui.info(package)
print inst_info
elif self.options.install_info:
ctx.ui.info('%-15s | %s ' % (package.name, inst_info.one_liner()))
else:
ctx.ui.info('%15s - %s ' % (package.name, package.summary))
+38 -11
View File
@@ -11,7 +11,7 @@
#
import sys
from optparse import OptionParser
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
@@ -19,18 +19,45 @@ _ = __trans.ugettext
import pisi
import pisi.cli
from pisi.cli import printu
from pisi.uri import URI
from pisi.cli.commands import *
import pisi.cli.command as command
import pisi.cli.addrepo
import pisi.cli.build
import pisi.cli.check
import pisi.cli.clean
import pisi.cli.configurepending
import pisi.cli.deletecache
import pisi.cli.delta
import pisi.cli.emerge
import pisi.cli.graph
import pisi.cli.index
import pisi.cli.info
import pisi.cli.install
import pisi.cli.listavailable
import pisi.cli.listcomponents
import pisi.cli.listinstalled
import pisi.cli.listpending
import pisi.cli.listrepo
import pisi.cli.listsources
import pisi.cli.listupgrades
import pisi.cli.rebuilddb
import pisi.cli.remove
import pisi.cli.removerepo
import pisi.cli.searchfile
import pisi.cli.search
import pisi.cli.updaterepo
import pisi.cli.upgrade
#FIXME: why does this has to be imported last
import pisi.cli.help
class ParserError(pisi.Exception):
pass
class PreParser(OptionParser):
class PreParser(optparse.OptionParser):
"""consumes any options, and finds arguments from command line"""
def __init__(self, version):
OptionParser.__init__(self, usage=usage_text, version=version)
optparse.OptionParser.__init__(self, usage=pisi.cli.help.usage_text, version=version)
def error(self, msg):
raise ParserError, msg
@@ -91,17 +118,17 @@ class PisiCLI(object):
sys.exit(0)
elif 'help' in opts or 'h' in opts:
self.die()
raise Error(_('No command given'))
raise pisi.cli.Error(_('No command given'))
cmd_name = args[0]
except ParserError:
raise Error(_('Command line parsing error'))
raise pisi.cli.Error(_('Command line parsing error'))
self.command = Command.get_command(cmd_name, args=orig_args)
self.command = command.Command.get_command(cmd_name, args=orig_args)
if not self.command:
raise Error(_("Unrecognized command: %s") % cmd_name)
raise pisi.cli.Error(_("Unrecognized command: %s") % cmd_name)
def die(self):
printu('\n' + self.parser.format_help())
pisi.cli.printu('\n' + self.parser.format_help())
sys.exit(1)
def run_command(self):
+52
View File
@@ -0,0 +1,52 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
class RebuildDb(command.Command):
"""Rebuild Databases
Usage: rebuilddb [ <package1> <package2> ... <packagen> ]
Rebuilds the PiSi databases
If package specs are given, they should be the names of package
dirs under /var/lib/pisi
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(RebuildDb, self).__init__(args)
name = ("rebuild-db", "rdb")
def options(self):
group = optparse.OptionGroup(self.parser, _("rebuild-db options"))
group.add_option("-f", "--files", action="store_true",
default=False, help=_("Rebuild files database"))
self.parser.add_option_group(group)
def run(self):
self.init(database=True)
if ctx.ui.confirm(_('Rebuild PiSi databases?')):
pisi.api.rebuild_db(ctx.get_option('files'))
+65
View File
@@ -0,0 +1,65 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
import pisi.db
class Remove(command.PackageOp):
"""Remove PiSi packages
Usage: remove <package1> <package2> ... <packagen>
Remove package(s) from your system. Just give the package names to remove.
You can also specify components instead of package names, which will be
expanded to package names.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Remove, self).__init__(args)
self.component = pisi.db.componentdb.ComponentDB()
name = ("remove", "rm")
def options(self):
group = optparse.OptionGroup(self.parser, _("remove options"))
super(Remove, self).options(group)
group.add_option("-c", "--component", action="append",
default=None, help=_("Remove component's and recursive components' packages"))
self.parser.add_option_group(group)
def run(self):
self.init()
components = ctx.get_option('component')
if not components and not self.args:
self.help()
return
packages = []
if components:
for name in components:
if self.componentdb.has_component(name):
packages.extend(self.componentdb.get_union_packages(name, walk=True))
packages.extend(self.args)
pisi.api.remove(packages)
+38
View File
@@ -0,0 +1,38 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi.cli.command as command
import pisi.api
class RemoveRepo(command.Command):
"""Remove repositories
Usage: remove-repo <repo1> <repo2> ... <repon>
Remove all repository information from the system.
"""
__metaclass__ = command.autocommand
def __init__(self,args):
super(RemoveRepo, self).__init__(args)
name = ("remove-repo", "rr")
def run(self):
if len(self.args)>=1:
self.init()
for repo in self.args:
pisi.api.remove_repo(repo)
else:
self.help()
return
+87
View File
@@ -0,0 +1,87 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
class Search(command.Command):
"""Search packages
Usage: search <term1> <term2> ... <termn>
Finds a package containing specified search terms
in summary, description, and package name fields.
Default search is done in package database. Use
options to search in install database or source
database.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Search, self).__init__(args)
name = ("search", "sr")
def options(self):
group = optparse.OptionGroup(self.parser, _("search options"))
group.add_option("-l", "--language", action="store",
type="string", default=None, help=_('Summary and description language'))
group.add_option("-r", "--repository", action="store",
type="string", default=None, help=_('Name of the source or package repository'))
group.add_option("-i", "--installdb", action="store_true",
default=False, help=_("Search in installdb"))
group.add_option("-s", "--sourcedb", action="store_true",
default=False, help=_("Search in sourcedb"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
if not self.args:
self.help()
return
lang = ctx.get_option('language')
repo = ctx.get_option('repository')
if ctx.get_option('installdb'):
db = pisi.db.installdb.InstallDB()
pkgs = db.search_package(self.args, lang)
get_info = db.get_package
get_name_sum = lambda pkg:(pkg.name, pkg.summary)
elif ctx.get_option('sourcedb'):
db = pisi.db.sourcedb.SourceDB()
pkgs = db.search_spec(self.args, lang, repo)
get_info = db.get_spec
get_name_sum = lambda pkg:(pkg.source.name, pkg.source.summary)
else:
db = pisi.db.packagedb.PackageDB()
pkgs = db.search_package(self.args, lang, repo)
get_info = db.get_package
get_name_sum = lambda pkg:(pkg.name, pkg.summary)
for pkg in pkgs:
pkg_info = get_info(pkg)
name, summary = get_name_sum(pkg_info)
if lang and summary.has_key(lang):
print "%s - %s" % (name, summary[lang])
else:
print "%s - %s" % (name, summary)
+69
View File
@@ -0,0 +1,69 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.cli.command as command
class SearchFile(command.Command):
"""Search for a file
Usage: search-file <path1> <path2> ... <pathn>
Finds the installed package which contains the specified file.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(SearchFile, self).__init__(args)
name = ("search-file", "sf")
def options(self):
group = optparse.OptionGroup(self.parser, _("search-file options"))
group.add_option("-l", "--long", action="store_true",
default=False, help=_("Show in long format"))
group.add_option("-q", "--quiet", action="store_true",
default=False, help=_("Show only package name"))
self.parser.add_option_group(group)
def search_file(self, path):
found = pisi.api.search_file(path)
for pkg, files in found:
for pkg_file in files:
ctx.ui.info(_("Package %s has file /%s") % (pkg, pkg_file))
if not found:
ctx.ui.error(_("Path '%s' does not belong to an installed package") % path)
def run(self):
self.init(database = True, write = False)
if not self.args:
self.help()
return
# search among existing files
for path in self.args:
if not ctx.config.options.quiet:
ctx.ui.info(_('Searching for %s') % path)
import os.path
if os.path.exists(path):
path = os.path.realpath(path)
self.search_file(path)
+59
View File
@@ -0,0 +1,59 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
class UpdateRepo(command.Command):
"""Update repository databases
Usage: update-repo [<repo1> <repo2> ... <repon>]
<repoi>: repository name
Synchronizes the PiSi databases with the current repository.
If no repository is given, all repositories are updated.
"""
__metaclass__ = command.autocommand
def __init__(self,args):
super(UpdateRepo, self).__init__(args)
name = ("update-repo", "ur")
def options(self):
group = optparse.OptionGroup(self.parser, _("update-repo options"))
group.add_option("-f", "--force", action="store_true",
default=False,
help=_("Update database in any case"))
self.parser.add_option_group(group)
def run(self):
self.init(database = True)
if self.args:
repos = self.args
else:
repos = pisi.api.list_repos()
for repo in repos:
pisi.api.update_repo(repo, ctx.get_option('force'))
+141
View File
@@ -0,0 +1,141 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import optparse
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.cli.command as command
import pisi.context as ctx
import pisi.api
import pisi.db
class Upgrade(command.PackageOp):
"""Upgrade PiSi packages
Usage: Upgrade [<package1> <package2> ... <packagen>]
<packagei>: package name
Upgrades the entire system if no package names are given
You may use only package names to specify packages because
the package upgrade operation is defined only with respect
to repositories. If you have specified a package name, it
should exist in the package repositories. If you just want to
reinstall a package from a PiSi file, use the install command.
You can also specify components instead of package names, which will be
expanded to package names.
"""
__metaclass__ = command.autocommand
def __init__(self, args):
super(Upgrade, self).__init__(args)
self.componentdb = pisi.db.componentdb.ComponentDB()
name = ("upgrade", "up")
def options(self):
group = optparse.OptionGroup(self.parser, _("upgrade options"))
super(Upgrade, self).options(group)
group.add_option("--ignore-build-no", action="store_true",
default=False,
help=_("Do not take build no into account."))
group.add_option("--security-only", action="store_true",
default=False, help=_("Security related package upgrades only"))
group.add_option("-r", "--bypass-update-repo", action="store_true",
default=False, help=_("Do not update repositories"))
group.add_option("--ignore-file-conflicts", action="store_true",
default=False, help=_("Ignore file conflicts"))
group.add_option("--ignore-package-conflicts", action="store_true",
default=False, help=_("Ignore package conflicts"))
group.add_option("-c", "--component", action="append",
default=None, help=_("Upgrade component's and recursive components' packages"))
group.add_option("-f", "--fetch-only", action="store_true",
default=False, help=_("Fetch upgrades but do not install."))
group.add_option("-x", "--exclude", action="append",
default=None, help=_("When upgrading system, ignore packages and components whose basenames match pattern."))
group.add_option("--exclude-from", action="store",
default=None, help=_("When upgrading system, ignore packages and components whose basenames \
match any pattern contained in file."))
self.parser.add_option_group(group)
def exclude_from(self, packages, exfrom):
patterns = []
if os.path.exists(exfrom):
for line in open(exfrom, "r").readlines():
if not line.startswith('#') and not line == '\n':
patterns.append(line.strip())
if patterns:
return self.exclude(packages, patterns)
return packages
def exclude(self, packages, patterns):
from sets import Set as set
import fnmatch
packages = set(packages)
for pattern in patterns:
# match pattern in package names
match = fnmatch.filter(packages, pattern)
packages = packages - set(match)
if not match:
# match pattern in component names
for compare in fnmatch.filter(self.componentdb.list_components(), pattern):
packages = packages - set(self.componentdb.get_union_packages(compare, walk=True))
return list(packages)
def run(self):
if self.options.fetch_only:
self.init(database=True, write=False)
else:
self.init()
if not ctx.get_option('bypass_update_repo'):
ctx.ui.info(_('Updating repositories'))
repos = pisi.api.list_repos()
for repo in repos:
pisi.api.update_repo(repo)
else:
ctx.ui.info(_('Will not update repositories'))
components = ctx.get_option('component')
packages = []
if components:
for name in components:
if self.componentdb.has_component(name):
packages.extend(self.componentdb.get_union_packages(name, walk=True))
packages.extend(self.args)
if packages == []:
packages = pisi.api.list_installed()
if os.path.exists(ctx.const.blacklist):
packages = self.exclude_from(packages, ctx.const.blacklist)
if ctx.get_option('exclude_from'):
packages = self.exclude_from(packages, ctx.get_option('exclude_from'))
if ctx.get_option('exclude'):
packages = self.exclude(packages, ctx.get_option('exclude'))
pisi.api.upgrade(packages)
-5
View File
@@ -18,8 +18,6 @@ import pisi
import pisi.context as ctx
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.db.lockeddbshelve as shelve
import pisi.db.itembyrepodb
class Error(pisi.Error):
pass
@@ -73,9 +71,6 @@ class Component(xmlfile.XmlFile):
t_Icon = [ autoxml.String, autoxml.optional]
t_VisibleTo = [autoxml.String, autoxml.optional]
# Dependencies to other components
t_Dependencies = [ [autoxml.String], autoxml.optional, "Dependencies/Component"]
# the parts of this component.
# to be filled by the component database, thus it is optional.
t_Packages = [ [autoxml.String], autoxml.optional, "Parts/Package"]
+7 -4
View File
@@ -92,14 +92,17 @@ class Config(object):
def lib_dir(self):
return self.subdir(self.values.dirs.lib_dir)
def db_dir(self):
return self.subdir(self.values.dirs.db_dir)
def info_dir(self):
return self.subdir(self.values.dirs.info_dir)
def packages_dir(self):
return self.subdir(self.values.dirs.packages_dir)
def archives_dir(self):
return self.subdir(self.values.dirs.archives_dir)
def packages_dir(self):
return self.subdir(self.values.dirs.packages_dir)
def cached_packages_dir(self):
return self.subdir(self.values.dirs.cached_packages_dir)
def compiled_packages_dir(self):
return self.subdir(self.values.dirs.compiled_packages_dir)
+7 -4
View File
@@ -35,11 +35,12 @@
#
#[directories]
#lib_dir = /var/lib/pisi
#db_dir = /var/db/pisi
#info_dir = "/var/lib/pisi/info"
#archives_dir = /var/cache/pisi/archives
#packages_dir = /var/cache/pisi/packages
#cached_packages_dir = /var/cache/pisi/packages
#compiled_packages_dir = "/var/cache/pisi/packages"
#index_dir = /var/cache/pisi/index
#packages_dir = /var/cache/pisi/package
#tmp_dir = /var/pisi
#kde_dir = /usr/kde/3.5
#qt_dir = /usr/qt/3
@@ -64,6 +65,7 @@ class GeneralDefaults:
autoclean = False
distribution = "Pardus"
distribution_release = "2007"
architecture = "i686"
http_proxy = os.getenv("HTTP_PROXY") or None
https_proxy = os.getenv("HTTPS_PROXY") or None
ftp_proxy = os.getenv("FTP_PROXY") or None
@@ -87,10 +89,11 @@ class DirectoriesDefaults:
"Default values for [directories] section"
lib_dir = "/var/lib/pisi"
log_dir = "/var/log"
db_dir = "/var/db/pisi"
info_dir = "/var/lib/pisi/info"
archives_dir = "/var/cache/pisi/archives"
packages_dir = "/var/cache/pisi/packages"
cached_packages_dir = "/var/cache/pisi/packages"
compiled_packages_dir = "/var/cache/pisi/packages"
packages_dir = "/var/lib/pisi/package"
index_dir = "/var/lib/pisi/index"
tmp_dir = "/var/pisi"
kde_dir = "/usr/kde/3.5"
+1 -1
View File
@@ -43,7 +43,7 @@ given conflicting spec"""
def package_conflicts(pkg, confs):
for c in confs:
if pkg.name == c.package and c.satisfies_relation(pkg.name, pkg.version, pkg.release):
if pkg.name == c.package and c.satisfies_relation(pkg.version, pkg.release):
return c
return None
+5 -1
View File
@@ -80,7 +80,11 @@ class Constants:
self.__c.install_tar = "install.tar"
self.__c.install_tar_lzma = "install.tar.lzma"
self.__c.mirrors_conf = "/etc/pisi/mirrors.conf"
self.__c.blacklist = "/etc/pisi/blacklist"
self.__c.config_pending = "configpending"
self.__c.files_db = "files.db"
self.__c.repos = "repos"
#file/directory permissions
self.__c.umask = 0022
+4 -43
View File
@@ -16,9 +16,10 @@ import signal
import pisi.constants
import pisi.signalhandler
import pisi.ui
const = pisi.constants.Constants()
sig = None
sig = pisi.signalhandler.SignalHandler()
config = None
@@ -30,61 +31,21 @@ def set_option(opt, val):
def get_option(opt):
return config and config.get_option(opt)
# default UI is CLI
ui = None # not now
ui = pisi.ui.UI()
# stdout, stderr for PiSi API
stdout = None
stderr = None
dbenv = None
installdb = None
packagedb = None
repodb = None
sourcedb = None
filesdb = None
componentdb = None
invidx = None
comar = None
comar = True
comar_sockname = None
initialized = False
# Bug #2879
# FIXME: Maybe we can create a simple rollback mechanism. There are other
# places which need this, too.
# this is needed in build process to clean after if something goes wrong.
build_leftover = None
#def register(_impl):
# """ Register a UI implementation"""
# ui = _impl
import bsddb3.db as db
# copy of DBShelve.txn_proc, the only difference is it doesn't need a shelf object
#FIXME: remove this redundancy, and move all this stuff to database.py
def txn_proc(proc, txn = None):
# can be used to txn protect a method automatically
if not txn:
if dbenv:
autotxn = dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise e
except Exception, e:
autotxn.abort()
raise e
autotxn.commit()
else:
retval = proc(None)
return retval
else:
return proc(txn)
def disable_keyboard_interrupts():
sig and sig.disable_signal(signal.SIGINT)
+1
View File
@@ -9,3 +9,4 @@
#
# Please read the COPYING file.
#
+108 -161
View File
@@ -10,198 +10,145 @@
# Please read the COPYING file.
#
import re
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import piksemel
import pisi
import pisi.db.lockeddbshelve as shelve
import pisi.context as ctx
import pisi.db.repodb
import pisi.db.itembyrepo
import pisi.component
import pisi.db.lazydb as lazydb
class Error(pisi.Error):
pass
class ComponentDB(lazydb.LazyDB):
class ComponentDB(object):
"""a database of components"""
def init(self):
def __init__(self):
self.d = pisi.db.itembyrepodb.ItemByRepoDB('component')
component_nodes = {}
component_packages = {}
component_sources = {}
def close(self):
self.d.close()
self.repodb = pisi.db.repodb.RepoDB()
def destroy(self):
self.d.destroy()
for repo in self.repodb.list_repos():
doc = self.repodb.get_repo_doc(repo)
component_nodes[repo] = self.__generate_components(doc)
component_packages[repo] = self.__generate_packages(doc)
component_sources[repo] = self.__generate_sources(doc)
def has_component(self, name, repo = pisi.db.itembyrepodb.repos, txn = None):
name = str(name)
return self.d.has_key(name, repo, txn)
self.cdb = pisi.db.itembyrepo.ItemByRepo(component_nodes)
self.cpdb = pisi.db.itembyrepo.ItemByRepo(component_packages)
self.csdb = pisi.db.itembyrepo.ItemByRepo(component_sources)
def get_component(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.db.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def __generate_packages(self, doc):
components = {}
for pkg in doc.tags("Package"):
components.setdefault(pkg.getTagData("PartOf"), []).append(pkg.getTagData("Name"))
return components
def get_component_repo(self, name, repo=None, txn = None):
try:
return self.d.get_item_repo(name, repo, txn=txn)
except pisi.db.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def __generate_sources(self, doc):
components = {}
for spec in doc.tags("SpecFile"):
src = spec.getTag("Source")
components.setdefault(src.getTagData("PartOf"), []).append(src.getTagData("Name"))
return components
def __generate_components(self, doc):
return dict(map(lambda x: (x.getTagData("Name"), x.toString()), doc.tags("Component")))
def get_union_comp(self, name, txn = None, repo = pisi.db.itembyrepodb.repos ):
"""get a union of all repository components packages, not just the first repo in order.
get only basic repo info from the first repo"""
def proc(txn):
s = self.d.d.get(name, txn=txn)
pkgs = set()
srcs = set()
for repostr in self.d.order(repo = repo):
if s.has_key(repostr):
pkgs |= set(s[repostr].packages)
srcs |= set(s[repostr].sources)
comp = self.get_component(name)
comp.packages = list(pkgs)
comp.sources = list(srcs)
return comp
return self.d.txn_proc(proc, txn)
def has_component(self, name, repo = None):
return self.cdb.has_item(name, repo)
def list_components(self, repo=None):
return self.d.list(repo)
return self.cdb.get_item_keys(repo)
# walk: walks through the underlying components' packages
def get_union_packages(self, component_name, walk=False, repo=pisi.db.itembyrepodb.repos, txn = None):
"""returns union of all repository component's packages, not just the first repo's
component's in order"""
def search_component(self, terms, lang=None, repo=None):
rename = '<LocalName xml:lang="%s">.*?%s.*?</LocalName>'
resum = '<Summary xml:lang="%s">.*?%s.*?</Summary>'
redesc = '<Description xml:lang="%s">.*?%s.*?</Description>'
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
found = []
for name, xml in self.cdb.get_items_iter(repo):
if name not in found and terms == filter(lambda term: re.compile(rename % (lang, term), re.I).search(xml) or \
re.compile(resum % (lang, term), re.I).search(xml) or \
re.compile(redesc % (lang, term), re.I).search(xml), terms):
found.append(name)
return found
# Returns the component in given repo or first found component in repo order if repo is None
def get_component(self, component_name, repo = None):
if not self.has_component(component_name, repo):
raise Exception(_('Component %s not found') % component_name)
component = pisi.component.Component()
component.parse(self.cdb.get_item(component_name, repo))
try:
component.packages = self.cpdb.get_item(component_name, repo)
component.sources = self.csdb.get_item(component_name, repo)
except Exception: #FIXME: what exception could we catch here, replace with that.
pass
return component
# Returns the component with combined packages and sources from all repos that contain this component
def get_union_component(self, component_name):
component = pisi.component.Component()
component.parse(self.cdb.get_item(component_name))
component = self.get_union_comp(component_name, txn, repo)
for repo in self.repodb.list_repos():
try:
component.packages.extend(self.cpdb.get_item(component_name, repo))
component.sources.extend(self.csdb.get_item(component_name, repo))
except Exception: #FIXME: what exception could we catch here, replace with that.
pass
return component
# Returns packages of given component from given repo or first found component's packages in repo
# order if repo is None.
# If walk is True than also the sub components' packages are returned
def get_packages(self, component_name, repo=None, walk=False):
component = self.get_component(component_name, repo)
if not walk:
return component.packages
packages = []
packages.extend(component.packages)
for dep in component.dependencies:
packages.extend(self.get_union_packages(dep, walk, repo, txn))
sub_components = filter(lambda x:x.startswith(component_name+"."), self.list_components(repo))
for sub in sub_components:
try:
packages.extend(self.get_component(sub, repo).packages)
except Exception: #FIXME: what exception could we catch here, replace with that.
pass
return packages
# walk: walks through the underlying components' packages
def get_packages(self, component_name, walk=False, repo=None, txn = None):
"""returns the given component's and underlying recursive components' packages"""
component = self.get_component(component_name, repo, txn)
# Returns the component with combined packages and sources from all repos that contain this component
# If walk is True than also the sub components' packages from all repos are returned
def get_union_packages(self, component_name, walk=False):
component = self.get_union_component(component_name)
if not walk:
return component.packages
packages = []
packages.extend(component.packages)
for dep in component.dependencies:
packages.extend(self.get_packages(dep, walk, repo, txn))
sub_components = filter(lambda x:x.startswith(component_name+"."), self.list_components())
for sub in sub_components:
try:
packages.extend(self.get_union_component(sub).packages)
except Exception: #FIXME: what exception could we catch here, replace with that.
pass
return packages
def add_child(self, component, repo, txn = None):
"""update component tree"""
parent_name = ".".join(component.name.split(".")[:-1])
if not parent_name: # root component
return
if self.has_component(parent_name, repo, txn):
parent = self.get_component(parent_name, repo, txn)
else:
parent = pisi.component.Component(name = parent_name)
if component.name not in parent.dependencies:
parent.dependencies.append(component.name)
self.d.add_item(parent_name, parent, repo, txn)
def update_component(self, component, repo, txn = None):
def proc(txn):
if self.has_component(component.name, repo, txn):
# preserve list of sources, packages and dependencies
current = self.d.get_item(component.name, repo, txn)
component.packages = current.packages
component.sources = current.sources
component.dependencies = current.dependencies
self.d.add_item(component.name, component, repo, txn)
self.add_child(component, repo, txn)
self.d.txn_proc(proc, txn)
def add_package(self, component_name, package, repo, txn = None):
def proc(txn):
assert component_name
if self.has_component(component_name, repo, txn):
component = self.get_component(component_name, repo, txn)
else:
component = pisi.component.Component( name = component_name )
if not package in component.packages:
component.packages.append(package)
self.d.add_item(component_name, component, repo, txn) # update
self.add_child(component, repo, txn)
self.d.txn_proc(proc, txn)
def remove_package(self, component_name, package, repo = None, txn = None):
def proc(txn, repo):
if not self.has_component(component_name, repo, txn):
raise Error(_('Information for component %s not available') % component_name)
if not repo:
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
component = self.get_component(component_name, repo, txn)
if package in component.packages:
component.packages.remove(package)
self.d.add_item(component_name, component, repo, txn) # update
ctx.txn_proc(lambda x: proc(txn, repo), txn)
def add_spec(self, component_name, spec, repo, txn = None):
def proc(txn):
assert component_name
if self.has_component(component_name, repo, txn):
component = self.get_component(component_name, repo, txn)
else:
component = pisi.component.Component( name = component_name )
if not spec in component.sources:
component.sources.append(spec)
self.d.add_item(component_name, component, repo, txn) # update
self.add_child(component, repo, txn)
self.d.txn_proc(proc, txn)
def remove_spec(self, component_name, spec, repo = None, txn = None):
def proc(txn, repo):
if not self.has_component(component_name, repo, txn):
raise Error(_('Information for component %s not available') % component_name)
if not repo:
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
component = self.get_component(component_name, repo, txn)
if spec in component.sources:
component.sources.remove(spec)
self.d.add_item(component_name, component, repo, txn) # update
ctx.txn_proc(lambda x: proc(txn, repo), txn)
def clear(self, txn = None):
self.d.clear(txn)
def remove_component(self, name, repo = None, txn = None):
name = str(name)
self.d.remove_item(name, repo, txn)
def remove_repo(self, repo, txn = None):
self.d.remove_repo(repo, txn=txn)
componentdb = None
def init():
global componentdb
if componentdb is not None:
return componentdb
componentdb = ComponentDB()
return componentdb
def finalize():
global componentdb
if componentdb is not None:
componentdb.close()
componentdb = None
-311
View File
@@ -1,311 +0,0 @@
#------------------------------------------------------------------------
# Copyright (c) 1997-2001 by Total Control Software
# All Rights Reserved
#------------------------------------------------------------------------
#
# Module Name: dbShelve.py
#
# Description: A reimplementation of the standard shelve.py that
# forces the use of cPickle, and DB.
#
# Creation Date: 11/3/97 3:39:04PM
#
# License: This is free software. You may use this software for any
# purpose including modification/redistribution, so long as
# this header remains intact and that you do not claim any
# rights of ownership or authorship of this software. This
# software has been tested, but no warranty is expressed or
# implied.
#
# 13-Dec-2000: Updated to be used with the new bsddb3 package.
# Added DBShelfCursor class.
#
# 13-Dec-2005: Minor hacking by exa to make it work better with PiSi
#------------------------------------------------------------------------
"""Manage shelves of pickled objects using bsddb database files for the
storage.
Add transaction processing by default to dictionary ops also
Also other minor improvements -- exa
Now added support for overriding the marshalling method
"""
#------------------------------------------------------------------------
import cPickle
import bsddb3.db as db
import bsddb3.dbobj as dbobj
import string
import pisi
class CodingError(pisi.Error):
pass
class DBShelf:
"""A shelf to hold pickled objects, built upon a bsddb DB object. It
automatically pickles/unpickles data objects going to/from the DB.
"""
def __init__(self, dbenv = None):
self.dbenv = dbenv
# how lame is bsddb3?
if self.dbenv:
self.db = dbobj.DB(dbenv)
else:
self.db = db.DB(None)
# it is better to explicitly close a shelf
#def __del__(self):
# self.close()
def has_key(self, key, txn = None):
if txn:
return self.db.has_key(key, txn)
else:
return self.db.has_key(key)
def txn_proc(self, proc, txn):
# can be used to txn protect a method automatically
if not txn:
if self.dbenv:
autotxn = self.dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise pisi.Error, e
autotxn.commit()
else: # execute without transactions
retval = proc(None)
return retval
else:
return proc(txn)
def decode(self, data):
try:
return cPickle.loads(data)
except cPickle.UnpicklingError:
raise CodingError()
def encode(self, obj):
return cPickle.dumps(obj, 1)
def clear(self, txn = None):
def proc(txn):
for x in self.keys(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
def delete(self, x, txn):
def proc(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
# another lame pythonic implementation method:
#def __getattr__(self, name):
# """Many methods we can just pass through to the DB object.
# (See below)
# """
# print 'aptal bsddb3', name
# return getattr(self.db, name)
#-----------------------------------
# Dictionary access methods
def __len__(self):
return len(self.db)
def __getitem__(self, key):
def proc(txn):
data = self.db.get(key)
return self.decode(data)
return self.txn_proc(proc, None)
def __setitem__(self, key, value):
# hyperdandik transactions
def proc(txn):
data = self.encode(value)
self.db.put(key,data,txn)
return self.txn_proc(proc, None)
def __delitem__(self, key):
txn = self.dbenv.txn_begin()
try:
self.db.delete(key, txn)
except db.DBError, e:
txn.abort()
raise e
txn.commit()
def keys(self, txn=None):
if txn != None:
return self.db.keys(txn)
else:
return self.db.keys()
def items(self, txn=None):
if txn != None:
items = self.db.items(txn)
else:
items = self.db.items()
newitems = []
for k, v in items:
newitems.append( (k, self.decode(v) ) )
return newitems
def values(self, txn=None):
if txn != None:
values = self.db.values(txn)
else:
values = self.db.values()
return map(lambda x : self.decode(x), values)
#-----------------------------------
# Other methods
def __append(self, value, txn=None):
data = self.encode(value)
return self.db.append(data, txn)
def append(self, value, txn=None):
if self.get_type() != db.DB_RECNO:
self.append = self.__append
return self.append(value, txn=txn)
raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO"
def associate(self, secondaryDB, callback, flags=0):
def _shelf_callback(priKey, priData, realCallback=callback):
data = self.decode(priData)
return realCallback(priKey, data)
return self.db.associate(secondaryDB, _shelf_callback, flags)
#def get(self, key, default=None, txn=None, flags=0):
def get(self, *args, **kw):
# We do it with *args and **kw so if the default value wasn't
# given nothing is passed to the extension module. That way
# an exception can be raised if set_get_returns_none is turned
# off.
data = apply(self.db.get, args, kw)
try:
return self.decode(data)
except (TypeError, CodingError):
return data # we may be getting the default value, or None,
# so it doesn't need unpickled.
def get_both(self, key, value, txn=None, flags=0):
data = self.encode(value)
data = self.db.get(key, data, txn, flags)
return self.decode(data)
def cursor(self, txn=None, flags=0):
c = DBShelfCursor(self.db.cursor(txn, flags))
c.binary = self.binary
return c
def put(self, key, value, txn=None, flags=0):
data = self.encode(value)
return self.db.put(key, data, txn, flags)
def join(self, cursorList, flags=0):
raise NotImplementedError
#----------------------------------------------
# Methods allowed to pass-through to self.db
#
# close, delete, fd, get_byteswapped, get_type, has_key,
# key_range, open, remove, rename, stat, sync,
# upgrade, verify, and all set_* methods.
#---------------------------------------------------------------------------
class DBShelfCursor:
"""
"""
def __init__(self, cursor):
self.dbc = cursor
def __del__(self):
self.close()
def __getattr__(self, name):
"""Some methods we can just pass through to the cursor object. (See below)"""
return getattr(self.dbc, name)
#----------------------------------------------
def dup(self, flags=0):
return DBShelfCursor(self.dbc.dup(flags))
def put(self, key, value, flags=0):
data = self.encode(value)
return self.dbc.put(key, data, flags)
def get(self, *args):
count = len(args) # a method overloading hack
method = getattr(self, 'get_%d' % count)
apply(method, args)
def get_1(self, flags):
rec = self.dbc.get(flags)
return self._extract(rec)
def get_2(self, key, flags):
rec = self.dbc.get(key, flags)
return self._extract(rec)
def get_3(self, key, value, flags):
data = self.encode(value)
rec = self.dbc.get(key, flags)
return self._extract(rec)
def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
def get_both(self, key, value, flags=0):
data = self.encode(value)
rec = self.dbc.get_both(key, flags)
return self._extract(rec)
def set(self, key, flags=0):
rec = self.dbc.set(key, flags)
return self._extract(rec)
def set_range(self, key, flags=0):
rec = self.dbc.set_range(key, flags)
return self._extract(rec)
def set_recno(self, recno, flags=0):
rec = self.dbc.set_recno(recno, flags)
return self._extract(rec)
set_both = get_both
def _extract(self, rec):
if rec is None:
return None
else:
key, data = rec
return key, self.decode(data)
#----------------------------------------------
# Methods allowed to pass-through to self.dbc
#
# close, count, delete, get_recno, join_item
#---------------------------------------------------------------------------
+58 -60
View File
@@ -9,81 +9,79 @@
#
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import os
import re
import shelve
import md5
import pisi
import pisi.db.lockeddbshelve as shelve
import pisi.context as ctx
import pisi.db.lazydb as lazydb
class Error(pisi.Error):
pass
# FIXME:
# We could traverse through files.xml files of the packages to find the path and
# the package - a linear search - as some well known package managers do. But the current
# file conflict mechanism of pisi prevents this and needs a fast has_file function.
# So currently filesdb is the only db and we cant still get rid of rebuild-db :/
class FilesDB(shelve.LockedDBShelf):
class FilesDB(lazydb.LazyDB):
def __init__(self):
shelve.LockedDBShelf.__init__(self, 'files')
def init(self):
self.filesdb = {}
self.__check_filesdb()
def has_file(self, path):
return self.filesdb.has_key(md5.new(path).digest())
def add_files(self, pkg_name, files, txn = None):
def proc(txn):
for x in files.list:
path = x.path
del x.path # don't store redundant attribute in db
self.put(path, (pkg_name, x), txn)
x.path = path # store it back in
self.txn_proc(proc, txn)
def get_file(self, path):
return self.filesdb[md5.new(path).digest()], path
def remove_files(self, files, txn = None):
def proc(txn):
for x in files.list:
if self.has_key(x.path):
self.delete(x.path, txn)
self.txn_proc(proc, txn)
def search_file(self, term):
installdb = pisi.db.installdb.InstallDB()
found = []
for pkg in installdb.list_installed():
files_xml = open(os.path.join(installdb.package_path(pkg), ctx.const.files_xml)).read()
paths = re.compile('<Path>(.*?%s.*?)</Path>' % term, re.I).findall(files_xml)
if paths:
found.append((pkg, paths))
return found
def has_file(self, path, txn = None):
return self.has_key(str(path), txn)
def add_files(self, pkg, files):
def get_file(self, path, txn = None):
path = str(path)
def proc(txn):
if not self.has_key(path, txn):
return None
else:
(name, fileinfo) = self.get(path, txn)
fileinfo.path = path
return (name, fileinfo)
return self.txn_proc(proc, txn)
self.__check_filesdb()
def match_files(self, glob):
# NB: avoid using, this reads the entire db
import fnmatch
glob = str(glob)
infos = []
for key in self.keys():
if fnmatch.fnmatch(key, glob):
for f in files.list:
self.filesdb[md5.new(f.path).digest()] = pkg
# FIXME: Why should we assign path attribute manually
# in fileinfo? This is also done in get_file(), seems
# like a dirty workaround... - baris
name = self[key][0]
fileinfo = self[key][1]
fileinfo.path = key
infos.append((name, fileinfo))
return infos
def remove_files(self, files):
for f in files:
if self.filesdb.has_key(md5.new(f.path).digest()):
del self.filesdb[md5.new(f.path).digest()]
filesdb = None
def destroy(self):
files_db = os.path.join(ctx.config.info_dir(), ctx.const.files_db)
if os.path.exists(files_db):
os.unlink(files_db)
def close(self):
if isinstance(self.filesdb, shelve.DbfilenameShelf):
self.filesdb.close()
def init():
global filesdb
if filesdb is not None:
return filesdb
def __check_filesdb(self):
if isinstance(self.filesdb, shelve.DbfilenameShelf):
return
filesdb = FilesDB()
return filesdb
files_db = os.path.join(ctx.config.info_dir(), ctx.const.files_db)
def finalize():
global filesdb
if filesdb is not None:
filesdb.close()
filesdb = None
if not os.path.exists(files_db):
flag = "n"
elif os.access(files_db, os.W_OK):
flag = "w"
else:
flag = "r"
self.filesdb = shelve.open(files_db, flag)
+134 -136
View File
@@ -13,25 +13,29 @@
# installation database
#
import os
import re
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import piksemel
# PiSi
import pisi
import pisi.context as ctx
import pisi.db.lockeddbshelve as shelve
import pisi.dependency
import pisi.files
import pisi.util
import pisi.db.lazydb as lazydb
class InstallDBError(pisi.Error):
pass
class InstallInfo:
# some data is replicated from packagedb
# we store as an object, hey, we can waste O(1) space.
# this is also easier to modify in the future, without
# requiring database upgrades! wow!
state_map = { 'i': _('installed'), 'ip':_('installed-pending') }
def __init__(self, state, version, release, build, distribution, time):
self.state = state
self.version = version
@@ -48,9 +52,6 @@ class InstallInfo:
time_str)
return s
state_map = { 'i': _('installed'), 'ip':_('installed-pending'),
'r':_('removed'), 'p': _('purged') }
def __str__(self):
s = _("State: %s\nVersion: %s, Release: %s, Build: %s\n") % \
(InstallInfo.state_map[self.state], self.version,
@@ -61,150 +62,147 @@ class InstallInfo:
time_str)
return s
class InstallDB(lazydb.LazyDB):
class InstallDB:
def init(self):
self.installed_db = self.__generate_installed_pkgs()
self.confing_pending_db = self.__generate_config_pending()
self.rev_deps_db = self.__generate_revdeps()
def __init__(self):
self.d = shelve.LockedDBShelf('install')
self.dp = shelve.LockedDBShelf('configpending')
self.files_dir = pisi.util.join_path(ctx.config.db_dir(), 'files')
def __generate_installed_pkgs(self):
return dict(map(lambda x:pisi.util.parse_package_name(x), os.listdir(ctx.config.packages_dir())))
def close(self):
self.d.close()
self.dp.close()
def __generate_config_pending(self):
pending_info_path = os.path.join(ctx.config.info_dir(), ctx.const.config_pending)
if os.path.exists(pending_info_path):
return open(pending_info_path, "r").read().split()
return []
def files_name(self, pkg, version, release):
pkg_dir = self.pkg_dir(pkg, version, release)
return pisi.util.join_path(pkg_dir, ctx.const.files_xml)
def __add_to_revdeps(self, package, revdeps):
metadata_xml = os.path.join(self.package_path(package), ctx.const.metadata_xml)
meta_doc = piksemel.parse(metadata_xml)
name = meta_doc.getTag("Package").getTagData('Name')
deps = meta_doc.getTag("Package").getTag('RuntimeDependencies')
if deps:
for dep in deps.tags("Dependency"):
revdeps.setdefault(dep.firstChild().data(), set()).add((name, dep.toString()))
def files(self, pkg):
pkg = str(pkg)
pkginfo = self.d[pkg]
def __generate_revdeps(self):
revdeps = {}
for package in self.list_installed():
self.__add_to_revdeps(package, revdeps)
return revdeps
def list_installed(self):
return self.installed_db.keys()
def has_package(self, package):
return self.installed_db.has_key(package)
def get_version(self, package):
metadata_xml = os.path.join(self.package_path(package), ctx.const.metadata_xml)
meta_doc = piksemel.parse(metadata_xml)
history = meta_doc.getTag("Package").getTag("History")
build = meta_doc.getTag("Package").getTagData("Build")
version = history.getTag("Update").getTagData("Version")
release = history.getTag("Update").getAttribute("release")
return version, release, build and int(build)
def get_files(self, package):
files = pisi.files.Files()
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
files_xml = os.path.join(self.package_path(package), ctx.const.files_xml)
files.read(files_xml)
return files
def search_package(self, terms, lang=None):
resum = '<Summary xml:lang="%s">.*?%s.*?</Summary>'
redesc = '<Description xml:lang="%s">.*?%s.*?</Description>'
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
found = []
for name in self.list_installed():
xml = open(os.path.join(self.package_path(name), ctx.const.metadata_xml)).read()
if terms == filter(lambda term: re.compile(term, re.I).search(name) or \
re.compile(resum % (lang, term), re.I).search(xml) or \
re.compile(redesc % (lang, term), re.I).search(xml), terms):
found.append(name)
return found
def get_info(self, package):
files_xml = os.path.join(self.package_path(package), ctx.const.files_xml)
ctime = pisi.util.creation_time(files_xml)
pkg = self.get_package(package)
state = "i"
if pkg.name in self.list_pending():
state = "ip"
info = InstallInfo(state,
pkg.version,
pkg.release,
pkg.build,
pkg.distribution,
ctime)
return info
def get_rev_deps(self, name):
rev_deps = []
if self.rev_deps_db.has_key(name):
for pkg, dep in self.rev_deps_db[name]:
node = piksemel.parseString(dep)
dependency = pisi.dependency.Dependency()
dependency.package = node.firstChild().data()
if node.attributes():
attr = node.attributes()[0]
dependency.__dict__[attr] = node.getAttribute(attr)
rev_deps.append((pkg, dependency))
return rev_deps
def pkg_dir(self, pkg, version, release):
return pisi.util.join_path(ctx.config.lib_dir(), 'package',
pkg + '-' + version + '-' + release)
return pisi.util.join_path(ctx.config.packages_dir(), pkg + '-' + version + '-' + release)
def is_recorded(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
return self.d.has_key(pkg)
return self.d.txn_proc(proc, txn)
def get_package(self, package):
metadata = pisi.metadata.MetaData()
metadata_xml = os.path.join(self.package_path(package), ctx.const.metadata_xml)
metadata.read(metadata_xml)
return metadata.package
def is_installed(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.is_recorded(pkg, txn):
info = self.d.get(pkg, txn)
return info.state=='i' or info.state=='ip'
else:
return False
return self.d.txn_proc(proc, txn)
def mark_pending(self, package):
if package not in self.confing_pending_db:
self.confing_pending_db.append(package)
self.__write_config_pending()
def list_installed(self, txn = None):
def proc(txn):
l = []
for (pkg, info) in self.d.items(txn):
if info.state=='i' or info.state=='ip':
l.append(pkg)
return l
return self.d.txn_proc(proc, txn)
def add_package(self, pkginfo):
self.installed_db[pkginfo.name] = "%s-%s" % (pkginfo.version, pkginfo.release)
self.__add_to_revdeps(pkginfo.name, self.rev_deps_db)
def remove_package(self, package_name):
if self.installed_db.has_key(package_name):
del self.installed_db[package_name]
self.clear_pending(package_name)
def list_pending(self):
# warning: reads the entire db
d = {}
for (pkg, x) in self.dp.items():
pkginfo = self.d[pkg]
d[pkg] = pkginfo
return d
return self.confing_pending_db
def get_info(self, pkg):
pkg = str(pkg)
return self.d[pkg]
def clear_pending(self, package):
if package in self.confing_pending_db:
self.confing_pending_db.remove(package)
self.__write_config_pending()
def get_version(self, pkg):
pkg = str(pkg)
info = self.d[pkg]
return (info.version, info.release, info.build)
def __write_config_pending(self):
pending_info_file = os.path.join(ctx.config.info_dir(), ctx.const.config_pending)
pending = open(pending_info_file, "w")
for pkg in set(self.confing_pending_db):
pending.write("%s\n" % pkg)
pending.close()
def is_removed(self, pkg):
pkg = str(pkg)
if self.is_recorded(pkg):
info = self.d[pkg]
return info.state=='r'
else:
return False
def package_path(self, package):
def install(self, pkg, version, release, build, distro = "",
config_later = False, rebuild=False, txn = None):
"""install package with specific version, release, build"""
pkg = str(pkg)
def proc(txn):
if self.is_installed(pkg, txn):
raise InstallDBError(_("Already installed"))
if config_later:
state = 'ip'
self.dp.put(pkg, True, txn)
else:
state = 'i'
if self.installed_db.has_key(package):
return os.path.join(ctx.config.packages_dir(), "%s-%s" % (package, self.installed_db[package]))
# FIXME: it might be more appropriate to pass date
# as an argument, or installation data afterwards
# to do this -- exa
if not rebuild:
import time
ctime = time.localtime()
else:
files_xml = self.files_name(pkg, version, release)
ctime = pisi.util.creation_time(files_xml)
self.d.put(pkg, InstallInfo(state, version, release, build, distro, ctime), txn)
self.d.txn_proc(proc,txn)
def clear_pending(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
if self.is_installed(pkg, txn):
assert info.state == 'ip'
info.state = 'i'
self.d.put(pkg, info, txn)
self.dp.delete(pkg, txn)
self.d.txn_proc(proc,txn)
def remove(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
info.state = 'r'
self.d.put(pkg, info, txn)
if self.dp.has_key(pkg):
self.dp.delete(pkg, txn)
self.d.txn_proc(proc, txn)
def purge(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.d.has_key(pkg, txn):
self.d.delete(pkg, txn)
self.d.txn_proc(proc, txn)
db = None
def init():
global db
if db is not None:
return db
db = InstallDB()
return db
def finalize():
global db
if db is not None:
db.close()
db = None
raise Exception(_('Package %s is not installed') % package)
+95
View File
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gzip
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.db
class ItemByRepo:
def __init__(self, dbobj, compressed=False):
self.dbobj = dbobj
self.compressed = compressed
self.repodb = pisi.db.repodb.RepoDB()
def has_repo(self, repo):
return self.dbobj.has_key(repo)
def has_item(self, item, repo=None):
for r in self.item_repos(repo):
if self.dbobj.has_key(r) and self.dbobj[r].has_key(item):
return True
return False
def which_repo(self, item):
for r in self.repodb.list_repos():
if self.dbobj.has_key(r) and self.dbobj[r].has_key(item):
return r
raise Exception(_("Item not found"))
def get_item_repo(self, item, repo=None):
for r in self.item_repos(repo):
if self.dbobj.has_key(r) and self.dbobj[r].has_key(item):
if self.compressed:
return gzip.zlib.decompress(self.dbobj[r][item]), r
else:
return self.dbobj[r][item], r
raise Exception(_("Repo item not found"))
def get_item(self, item, repo=None):
item, repo = self.get_item_repo(item, repo)
return item
def get_item_keys(self, repo=None):
items = []
for r in self.item_repos(repo):
if not self.has_repo(r):
raise Exception(_('Repository %s does not exist.') % repo)
if self.dbobj.has_key(r):
items.extend(self.dbobj[r].keys())
return list(set(items))
def get_list_item(self, repo=None):
items = []
for r in self.item_repos(repo):
if not self.has_repo(r):
raise Exception(_('Repository %s does not exist.') % repo)
if self.dbobj.has_key(r):
items.extend(self.dbobj[r])
return list(set(items))
def get_items_iter(self, repo=None):
for r in self.item_repos(repo):
if not self.has_repo(r):
raise Exception(_('Repository %s does not exist.') % repo)
if self.compressed:
for item in self.dbobj[r].keys():
yield item, gzip.zlib.decompress(self.dbobj[r][item])
else:
for item in self.dbobj[r].keys():
yield item, self.dbobj[r][item]
def item_repos(self, repo=None):
repos = self.repodb.list_repos()
if repo:
repos = [repo]
return repos
-214
View File
@@ -1,214 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.db.lockeddbshelve as shelve
installed, thirdparty, repos, all = range(1, 5)
"""installed and thirdparty are special databases to keep track
of already installed stuff and third party stuff not in any real repository.
repos means search in repositories only, and all means search in
repositories and special databases (called tracking databases)
"""
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
pass
class ItemByRepoDB(object):
def __init__(self, name):
self.d = shelve.LockedDBShelf(name)
#self.dbyrepo = shelve.LockedDBShelf(name + '-byrepo')
def close(self):
self.d.close()
def clear(self, txn = None):
self.d.clear(txn=txn)
def txn_proc(self, proc, txn):
return self.d.txn_proc(proc, txn)
def items(self):
return self.d.items()
@staticmethod
def not_just_tracking(data):
keys = data.keys()
if len(keys)==1:
if 'trdparty' in keys or 'inst' in keys:
return False
elif len(keys)==2:
if 'trdparty' in keys and 'inst' in keys:
return False
return True
#below is a slower way
#for x in data.keys():
# if x.startsWith('repo-'):
# return True
#return False
def list_if(self, pred):
return [ k for k,data in self.d.items() if pred(k, data)]
def list(self, repo = None):
if repo == None:
repo = repos
if repo not in [repos, all]:
return [ k for k,data in self.d.items() if data.has_key(self.repo_str(repo))]
else:
if repo == all:
return [ pkg for pkg in self.d.keys() ]
else:
return self.list_if( lambda k,data: ItemByRepoDB.not_just_tracking(data) )
# TODO: carry this to repodb, really :/
def order(self, repo = None):
if repo == None:
repo = repos
assert repo in [all, repos]
order = [ 'repo-' + x for x in ctx.repodb.list() ]
if repo == all:
order += ['trdparty', 'inst']
return order
# def list_repo(self, repo):
# return self.dbyrepo[repo]
def repo_str(self, repo):
if repo==thirdparty:
repo='trdparty'
elif repo==installed:
repo='inst'
else:
assert type(repo) == type("")
repo='repo-'+repo
return repo
def str_repo(self, str):
if str.startswith('repo-'):
return str[5:]
elif str=='trdparty':
return thirdparty
elif str=='inst':
return installed
else:
raise Error(_('Invalid repository string'))
def has_key(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
haskey = self.d.has_key(name, txn)
if repo == all:
return haskey
elif repo == repos:
data = self.d.get(name, txn)
return haskey and ItemByRepoDB.not_just_tracking(data)
else:
repostr = self.repo_str(repo)
return haskey and self.d.get(name, txn).has_key(repostr)
def get_item_repo(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
def proc(txn):
if not self.d.has_key(name, txn=txn):
raise NotfoundError(_('Key %s not found') % name)
s = self.d.get(name, txn=txn)
if repo in [repos, all]:
for repostr in self.order(repo):
if s.has_key(repostr):
return (s[repostr], self.str_repo(repostr))
else:
repostr = self.repo_str(repo)
if s.has_key(repostr):
return (s[repostr], repo)
raise NotfoundError(_('Key %s in repo %s not found') % (name, repo))
#return None
return self.d.txn_proc(proc, txn)
def get_item(self, name, repo = None, txn = None):
if repo == None:
repo = repos
x = self.get_item_repo(name, repo, txn)
if x:
item, repo = x
# discard repo, not always needed
return item
else:
return None
def which_repo(self, name, txn = None):
x = self.get_item_repo(name, txn=txn)
if x:
item, repo = x
return repo
else:
return None
def add_item(self, name, obj, repo, txn = None):
assert not repo in [all, repos]
repostr = self.repo_str(repo)
def proc(txn):
if not self.d.has_key(name):
s = dict()
else:
s = self.d.get(name, txn)
s[ repostr ] = obj
self.d.put(name, s, txn)
self.d.txn_proc(proc, txn)
def remove_item_repo(self, name, repo, txn = None):
assert not repo in [all, repos]
name = str(name)
def p(txn):
s = self.d.get(name, txn)
repostr = self.repo_str(repo)
if s.has_key(repostr):
del s[repostr]
if not len(s):
self.d.delete(name, txn)
else:
self.d.put(name, s, txn)
self.d.txn_proc(p, txn)
def remove_item_only(self, name, txn = None):
def p(txn):
repo = self.which_repo(name, txn=txn)
self.remove_item_repo(name, repo, txn=txn)
self.d.txn_proc(p, txn)
def remove_item(self, name, repo=None, txn=None):
if repo == None:
repo = repos
if not repo in [all, repos]:
self.remove_item_repo(name, repo,txn=txn)
else:
self.remove_item_only(name,txn=txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
for key in self.d.keys():
self.remove_item_repo(key, repo, txn=txn)
self.d.txn_proc(proc, txn)
+41
View File
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import time
import pisi.context as ctx
class Singleton(object):
def __new__(type):
if not '_the_instance' in type.__dict__:
type._the_instance = object.__new__(type)
return type._the_instance
class LazyDB(Singleton):
def __init__(self):
if not self.__dict__.has_key("initialized"):
self.initialized = False
def is_initialized(self):
return self.initialized
def __getattr__(self, attr):
if not self.initialized:
start = time.time()
self.init()
end = time.time()
ctx.ui.debug("%s initialized in %s." % (self.__class__.__name__, end - start))
self.initialized = True
if not self.__dict__.has_key(attr):
raise AttributeError, attr
return self.__dict__[attr]
-174
View File
@@ -1,174 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import fcntl
import types
import bsddb3.db as db
import bsddb3.dbobj as dbobj
import pisi.db.dbshelve as shelve
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.util
import pisi.version
class Error(pisi.Error):
pass
# check database version
# if write is given it knows it has write access
# if force is given it updates the specified db version
def check_dbversion(versionfile, ver, write=False, update=False):
verfn = pisi.util.join_path(pisi.context.config.db_dir(), versionfile)
firsttime = False
if os.path.exists(verfn):
verfile = file(verfn, 'r')
ls = verfile.readlines()
currver = pisi.version.Version(ls[0])
dbver = pisi.version.Version(ver)
if currver < dbver:
if not update:
raise Error(_('Database version for %s insufficient. Please run rebuild-db command.') % versionfile)
else:
pass # continue to update, then
elif currver > dbver:
raise Error(_('Database version for %s greater than PiSi version. You need a newer PiSi.') % versionfile)
elif not update:
return True # db version is OK
else:
firsttime = True
if write and (update or firsttime):
if os.access(pisi.context.config.db_dir(), os.W_OK):
verfile = file(verfn, 'w')
verfile.write(ver)
verfile.close()
else:
raise Error(_('Cannot attain write access to database environment'))
else:
raise Error(_('Database version %s not present.') % versionfile)
def lock_dbenv():
ctx.dbenv_lock = file(pisi.util.join_path(pisi.context.config.db_dir(), 'dbenv.lock'), 'w')
try:
fcntl.flock(ctx.dbenv_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
# write: write access to database environment
# writeversion: would you like to be able
def init_dbenv(write=False, writeversion=False):
if os.access(pisi.context.config.db_dir(), os.R_OK):
# try to read version
check_dbversion('dbversion', pisi.__dbversion__, write=write, update=writeversion)
check_dbversion('filesdbversion', pisi.__filesdbversion__, write=write, update=writeversion)
else:
raise Error(_('Cannot attain read access to database environment'))
if write:
if os.access(pisi.context.config.db_dir(), os.W_OK):
lock_dbenv()
ctx.dbenv = dbobj.DBEnv()
flags = (db.DB_INIT_MPOOL | # cache
db.DB_INIT_TXN | # transaction subsystem
db.DB_INIT_LOG | # logging subsystem
db.DB_RECOVER | # run normal recovery
db.DB_CREATE) # allow db to create files
ctx.dbenv.set_cachesize(0, 4*1024*1024)
ctx.dbenv.open(pisi.context.config.db_dir(), flags)
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
else:
raise Error(_("Cannot attain write access to PiSi database. You have to be root for this operation."))
else:
ctx.dbenv = None # read-only access to database
class LockedDBShelf(shelve.DBShelf):
"""A simple wrapper to implement locking for bsddb's dbshelf"""
def __init__(self, dbname, mode=0644,
filetype=db.DB_BTREE, dbenv = None):
if dbenv == None:
dbenv = ctx.dbenv
shelve.DBShelf.__init__(self, dbenv)
filename = pisi.util.join_path(pisi.context.config.db_dir(), dbname + '.bdb')
if dbenv and os.access(os.path.dirname(filename), os.W_OK):
flags = 'w'
elif os.access(filename, os.R_OK):
flags = 'r'
else:
raise Error(_('Cannot attain read or write access to database %s') % dbname)
self.open(filename, dbname, filetype, flags, mode)
def destroy(self):
os.unlink(self.filename)
def __del__(self):
# superclass does something funky, we don't need that
pass
def open(self, filename, dbname, filetype, flags=db.DB_CREATE, mode=0644):
self.filename = filename
self.closed = False
if type(flags) == type(''):
sflag = flags
if sflag == 'r':
flags = db.DB_RDONLY
elif sflag == 'rw':
flags = 0
elif sflag == 'w':
flags = db.DB_CREATE
elif sflag == 'c':
flags = db.DB_CREATE
elif sflag == 'n':
flags = db.DB_TRUNCATE | db.DB_CREATE
else:
raise Error, _("Flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
self.flags = flags
if self.flags & db.DB_RDONLY == 0:
flags |= db.DB_AUTO_COMMIT # use txn subsystem in write mode
self.lock()
filename = os.path.realpath(filename) # we give absolute path due to dbenv
#print 'opening', filename, filetype, flags, mode
return self.db.open(filename, None, filetype, flags, mode)
def lock(self):
self.lockfile = file(self.filename + '.lock', 'w')
try:
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
def close(self):
if self.closed:
return
self.db.close()
if self.flags & db.DB_RDONLY == 0:
self.unlock()
self.closed = True
def unlock(self):
self.lockfile.close()
os.unlink(self.filename + '.lock')
@staticmethod
def encodekey(key):
'''utility method for dbs that must store unicodes in keys'''
if type(key)==types.UnicodeType:
return key.encode('utf-8')
elif type(key)==types.StringType:
return key
else:
raise Error('Key must be either string or unicode')
+112 -154
View File
@@ -17,182 +17,140 @@ we basically store everything in PackageInfo class
yes, we are cheap
"""
import re
import gzip
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.db.itembyrepodb
import piksemel
class Error(pisi.Error):
pass
import pisi.db
import pisi.metadata
import pisi.dependency
import pisi.db.itembyrepo
import pisi.db.lazydb as lazydb
class NotfoundError(pisi.Error):
def __init__(self, pkg):
pisi.Error.__init__("Package %s not found" % pkg)
self.pkg = pkg
class PackageDB(lazydb.LazyDB):
class PackageDB(object):
"""PackageDB class provides an interface to the package database
using shelf objects"""
def init(self):
def __init__(self):
self.d = pisi.db.itembyrepodb.ItemByRepoDB('package')
self.dr = pisi.db.itembyrepodb.ItemByRepoDB('revdep')
self.do = pisi.db.itembyrepodb.ItemByRepoDB('obsoleted')
self.drp = pisi.db.itembyrepodb.ItemByRepoDB('replaces')
self.__package_nodes = {} # Packages
self.__revdeps = {} # Reverse dependencies
self.__obsoletes = {} # Obsoletes
self.__replaces = {} # Replaces
def close(self):
self.d.close()
self.dr.close()
self.do.close()
self.drp.close()
repodb = pisi.db.repodb.RepoDB()
def destroy(self):
self.d.destroy()
self.dr.destroy()
self.do.destroy()
self.drp.destroy()
for repo in repodb.list_repos():
doc = repodb.get_repo_doc(repo)
self.__package_nodes[repo] = self.__generate_packages(doc)
self.__revdeps[repo] = self.__generate_revdeps(doc)
self.__obsoletes[repo] = self.__generate_obsoletes(doc)
self.__replaces[repo] = self.__generate_replaces(doc)
def has_package(self, name, repo=None, txn = None):
return self.d.has_key(name, repo, txn=txn)
self.pdb = pisi.db.itembyrepo.ItemByRepo(self.__package_nodes, compressed=True)
self.rvdb = pisi.db.itembyrepo.ItemByRepo(self.__revdeps)
self.odb = pisi.db.itembyrepo.ItemByRepo(self.__obsoletes)
self.rpdb = pisi.db.itembyrepo.ItemByRepo(self.__replaces)
def get_package(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.db.itembyrepodb.NotfoundError:
raise Error(_('Package %s not found') % name)
def __generate_replaces(self, doc):
return [x.getTagData("Name") for x in doc.tags("Package") if x.getTagData("Replaces")]
def __generate_obsoletes(self, doc):
distribution = doc.getTag("Distribution")
obsoletes = distribution and distribution.getTag("Obsoletes")
def get_package_repo(self, name, repo=None, txn = None):
return self.d.get_item_repo(name, repo, txn=txn)
if not obsoletes:
return []
def which_repo(self, name, txn = None):
return self.d.which_repo(name, txn=txn)
return map(lambda x: x.firstChild().data(), obsoletes.tags("Package"))
def __generate_packages(self, doc):
return dict(map(lambda x: (x.getTagData("Name"), gzip.zlib.compress(x.toString())), doc.tags("Package")))
def __generate_revdeps(self, doc):
revdeps = {}
for node in doc.tags("Package"):
name = node.getTagData('Name')
deps = node.getTag('RuntimeDependencies')
if deps:
for dep in deps.tags("Dependency"):
revdeps.setdefault(dep.firstChild().data(), set()).add((name, dep.toString()))
return revdeps
def has_package(self, name, repo=None):
return self.pdb.has_item(name, repo)
def get_package(self, name, repo=None):
pkg, repo = self.get_package_repo(name, repo)
return pkg
def search_package(self, terms, lang=None, repo=None):
resum = '<Summary xml:lang="%s">.*?%s.*?</Summary>'
redesc = '<Description xml:lang="%s">.*?%s.*?</Description>'
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
found = []
for name, xml in self.pdb.get_items_iter(repo):
if terms == filter(lambda term: re.compile(term, re.I).search(name) or \
re.compile(resum % (lang, term), re.I).search(xml) or \
re.compile(redesc % (lang, term), re.I).search(xml), terms):
found.append(name)
return found
def get_version(self, name, repo):
if not self.has_package(name, repo):
raise Exception(_('Package %s not found.') % name)
pkg_doc = piksemel.parseString(self.pdb.get_item(name, repo))
history = pkg_doc.getTag("History")
build = pkg_doc.getTagData("Build")
version = history.getTag("Update").getTagData("Version")
release = history.getTag("Update").getAttribute("release")
return version, release, build and int(build)
def get_package_repo(self, name, repo=None):
pkg, repo = self.pdb.get_item_repo(name, repo)
package = pisi.metadata.Package()
package.parse(pkg)
return package, repo
def which_repo(self, name):
return self.pdb.which_repo(name)
def get_obsoletes(self, repo=None):
obsoletes = []
for r in self.do.list(repo):
obsoletes.extend(self.do.get_item(r, repo))
replaces = self.get_replaces(repo)
return set(str(o) for o in obsoletes) - set(replaces.keys())
return self.odb.get_list_item(repo)
def get_rev_deps(self, name, repo=None):
try:
rvdb = self.rvdb.get_item(name, repo)
except Exception: #FIXME: what exception could we catch here, replace with that.
return []
rev_deps = []
for pkg, dep in rvdb:
node = piksemel.parseString(dep)
dependency = pisi.dependency.Dependency()
dependency.package = node.firstChild().data()
if node.attributes():
attr = node.attributes()[0]
dependency.__dict__[attr] = node.getAttribute(attr)
rev_deps.append((pkg, dependency))
return rev_deps
# replacesdb holds the info about the replaced packages (ex. gaim -> pidgin)
def get_replaces(self, repo = None):
def get_replaces(self, repo=None):
pairs = {}
for pkg_name in self.drp.list(repo):
replaces = self.drp.get_item(pkg_name, repo)
for pkg_name in self.rpdb.get_list_item():
replaces = self.get_package(pkg_name).replaces
for r in replaces:
if pisi.replace.installed_package_replaced(r):
pairs[r.package] = pkg_name
return pairs
def get_rev_deps(self, name, repo = None, txn = None):
if self.dr.has_key(name, repo, txn=txn):
return self.dr.get_item(name, repo, txn=txn)
else:
return []
def get_deps(self, name, repo = None, txn = None):
if self.d.has_key(name, repo, txn=txn):
pinfo = self.d.get_item(name, repo, txn=txn)
return pinfo.packageDependencies
else:
return []
def list_packages(self, repo=None):
return self.d.list(repo)
def add_obsoletes(self, obsoletes, repo, txn = None):
def proc(txn):
self.do.add_item(repo, obsoletes, repo, txn)
ctx.txn_proc(proc, txn)
def add_package(self, package_info, repo, txn = None):
name = str(package_info.name)
def proc(txn):
self.d.add_item(name, package_info, repo, txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, repo, txn):
revdep = self.dr.get_item(dep_name, repo, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
revdep.append( (name, dep) )
self.dr.add_item(dep_name, revdep, repo, txn)
else:
self.dr.add_item(dep_name, [ (name, dep) ], repo, txn)
if package_info.replaces:
self.drp.add_item(name, package_info.replaces, repo, txn)
# add component
ctx.componentdb.add_package(package_info.partOf, package_info.name, repo, txn)
ctx.txn_proc(proc, txn)
def clear(self, txn = None):
self.d.clear()
self.dr.clear()
self.do.clear()
self.drp.clear()
def remove_package(self, name, repo = None, txn = None):
name = str(name)
def proc(txn):
package_info = self.d.get_item(name, repo, txn=txn)
self.d.remove_item(name, repo, txn=txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, repo, txn):
revdep = self.dr.get_item(dep_name, repo, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
if revdep:
self.dr.add_item(dep_name, revdep, repo, txn)
else:
# Bug 3558: removal of revdep list of a package from revdepdb
# should only be done by the list members (dep. packages), not
# the package itself. So if a package is removed, it is removed
# from packagedb but its revdepdb part may still exist, until
# all the list members are removed.
self.dr.remove_item(dep_name, repo, txn=txn)
# remove from component
ctx.componentdb.remove_package(package_info.partOf, package_info.name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dr.remove_repo(repo, txn=txn)
self.do.remove_repo(repo, txn=txn)
self.drp.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
def remove_tracking_package(name, txn = None):
# remove the guy from the tracking databases
if pkgdb.has_package(name, pisi.db.itembyrepodb.installed, txn=txn):
pkgdb.remove_package(name, pisi.db.itembyrepodb.installed, txn=txn)
if pkgdb.has_package(name, pisi.db.itembyrepodb.thirdparty, txn=txn):
pkgdb.remove_package(name, pisi.db.itembyrepodb.thirdparty, txn=txn)
pkgdb = None
def init():
global pkgdb
if pkgdb is not None:
return pkgdb
pkgdb = PackageDB()
return pkgdb
def finalize():
global pkgdb
if pkgdb is not None:
pkgdb.close()
pkgdb = None
def list_packages(self, repo):
return self.pdb.get_item_keys(repo)
+109 -88
View File
@@ -10,115 +10,136 @@
# Please read the COPYING file.
#
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import os
import piksemel
import pisi
import pisi.db.lockeddbshelve as shelve
import pisi.uri
import pisi.util
import pisi.index
import pisi.context as ctx
class Error(pisi.Error):
pass
import pisi.db.lazydb as lazydb
class Repo:
def __init__(self, indexuri):
self.indexuri = indexuri
#class HttpRepo
medias = (cd, usb, remote, local) = range(4)
#class FtpRepo
class RepoOrder:
#class RemovableRepo
def __init__(self):
self.repos = self._get_repos()
def add(self, repo_name, repo_url, repo_type="remote"):
repo_doc = self._get_doc()
class RepoDB(object):
"""RepoDB maps repo ids to repository information"""
try:
node = [x for x in repo_doc.tags("Repo")][-1]
repo_node = node.appendTag("Repo")
except IndexError:
repo_node = repo_doc.insertTag("Repo")
name_node = repo_node.insertTag("Name")
name_node.insertData(repo_name)
def __init__(self, txn = None):
self.d = shelve.LockedDBShelf("repo")
def proc(txn):
if not self.d.has_key("order", txn):
self.d.put("order", [], txn)
self.d.txn_proc(proc, txn)
url_node = repo_node.insertTag("Url")
url_node.insertData(repo_url)
def close(self):
self.d.close()
media_node = repo_node.insertTag("Media")
media_node.insertData(repo_type)
def repo_name(self, ix):
l = self.list()
return l[ix]
self._update(repo_doc)
def remove(self, repo_name):
repo_doc = self._get_doc()
for r in repo_doc.tags("Repo"):
if r.getTagData("Name") == repo_name:
r.hide()
self._update(repo_doc)
def get_order(self):
order = []
#FIXME: get media order from pisi.conf
for m in ["cd", "usb", "remote", "local"]:
if self.repos.has_key(m):
order.extend(self.repos[m])
return order
def _update(self, doc):
repos_file = os.path.join(ctx.config.info_dir(), ctx.const.repos)
open(repos_file, "w").write("%s\n" % doc.toPrettyString())
self.repos = self._get_repos()
def _get_doc(self):
repos_file = os.path.join(ctx.config.info_dir(), ctx.const.repos)
if not os.path.exists(repos_file):
return piksemel.newDocument("REPOS")
return piksemel.parse(repos_file)
def _get_repos(self):
repo_doc = self._get_doc()
order = {}
for r in repo_doc.tags("Repo"):
media = r.getTagData("Media")
name = r.getTagData("Name")
order.setdefault(media, []).append(name)
return order
class RepoDB(lazydb.LazyDB):
def init(self):
self.repoorder = RepoOrder()
def has_repo(self, name):
name = str(name)
return self.d.has_key("repo-" + name)
return name in self.list_repos()
def get_repo(self, name):
name = str(name)
return self.d["repo-" + name]
def get_repo_doc(self, repo_name):
repo = self.get_repo(repo_name)
index = os.path.basename(repo.indexuri.get_uri())
index_path = pisi.util.join_path(ctx.config.index_dir(), repo_name, index)
def set_default_repo(self, name, txn = None):
name = str(name)
def proc(txn):
order = self.d.get("order", txn)
try:
index = order.index(name)
order[0], order[index] = order[index], order[0]
self.d.put("order", order, txn)
except ValueError:
raise Error(_('No repository named %s exists') % name)
self.d.txn_proc(proc, txn)
if index_path.endswith("bz2"):
index_path = index_path.split(".bz2")[0]
def add_repo(self, name, repo_info, txn = None, at = None):
"""add repository with name and repo_info at a given optional position"""
name = str(name)
assert (isinstance(repo_info,Repo))
def proc(txn):
if self.d.has_key("repo-" + name, txn):
raise Error(_('Repository %s already exists') % name)
self.d.put("repo-" + name, repo_info, txn)
order = self.d.get("order", txn)
if at == None:
order.append(name)
else:
if at<0 or at>len(order):
raise Error(_("Cannot add repository at position %s") % at)
order.insert(at, name)
self.d.put("order", order, txn)
self.d.txn_proc(proc, txn)
return piksemel.parse(index_path)
def get_repo(self, repo):
urifile_path = pisi.util.join_path(ctx.config.index_dir(), repo, "uri")
uri = open(urifile_path, "r").read()
return Repo(pisi.uri.URI(uri))
def add_repo(self, name, repo_info, at = None):
repo_path = pisi.util.join_path(ctx.config.index_dir(), name)
os.makedirs(repo_path)
urifile_path = pisi.util.join_path(ctx.config.index_dir(), name, "uri")
uri = open(urifile_path, "w").write(repo_info.indexuri.get_uri())
self.repoorder.add(name, repo_info.indexuri.get_uri())
def list(self):
return self.d["order"]
def remove_repo(self, name):
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
self.repoorder.remove(name)
def clear(self):
self.d.clear()
def get_source_repos(self):
repos = []
for r in self.list_repos():
if self.get_repo_doc(r).getTag("SpecFile"):
repos.append(r)
return repos
def remove_repo(self, name, txn = None):
name = str(name)
def proc(txn):
self.d.delete("repo-" + name, txn)
l = self.d.get("order", txn)
l.remove(name)
self.d.put("order", l, txn)
ctx.packagedb.remove_repo(name, txn=txn)
ctx.sourcedb.remove_repo(name, txn=txn)
ctx.componentdb.remove_repo(name, txn=txn)
self.d.txn_proc(proc, txn)
def get_binary_repos(self):
repos = []
for r in self.list_repos():
if not self.get_repo_doc(r).getTag("SpecFile"):
repos.append(r)
return repos
db = None
def init():
global db
if db is not None:
return db
db = RepoDB()
return db
def finalize():
global db
if db is not None:
db.close()
db = None
def list_repos(self):
return self.repoorder.get_order()
+52 -74
View File
@@ -10,97 +10,75 @@
# Please read the COPYING file.
#
"""
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 re
import gzip
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
import pisi.db.itembyrepodb
import piksemel
class NotfoundError(pisi.Error):
pass
import pisi
import pisi.specfile
import pisi.db.lazydb as lazydb
class SourceDB(object):
class SourceDB(lazydb.LazyDB):
def __init__(self):
self.d = pisi.db.itembyrepodb.ItemByRepoDB('source')
self.dpkgtosrc = pisi.db.itembyrepodb.ItemByRepoDB('pkgtosrc')
def init(self):
def close(self):
self.d.close()
self.dpkgtosrc.close()
self.__source_nodes = {}
self.__pkgstosrc = {}
def list(self):
return self.d.list()
repodb = pisi.db.repodb.RepoDB()
def has_spec(self, name, repo=None, txn=None):
return self.d.has_key(name, repo, txn)
for repo in repodb.list_repos():
doc = repodb.get_repo_doc(repo)
self.__source_nodes[repo], self.__pkgstosrc[repo] = self.__generate_sources(doc)
def get_spec(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn)
except pisi.db.itembyrepodb.NotfoundError:
raise NotfoundError(_("Source package %s not found") % name)
self.sdb = pisi.db.itembyrepo.ItemByRepo(self.__source_nodes, compressed=True)
self.psdb = pisi.db.itembyrepo.ItemByRepo(self.__pkgstosrc)
def get_spec_repo(self, name, repo=None, txn = None):
try:
return self.d.get_item_repo(name, repo, txn)
except pisi.db.itembyrepodb.NotfoundError:
raise NotfoundError(_("Source package %s not found") % name)
def __generate_sources(self, doc):
def pkgtosrc(self, name, txn = None):
return self.dpkgtosrc.get_item(name, txn=txn)
sources = {}
pkgstosrc = {}
def add_spec(self, spec, repo, txn = None):
assert not spec.errors()
name = str(spec.source.name)
def proc(txn):
self.d.add_item(name, spec, repo, txn)
for pkg in spec.packages:
self.dpkgtosrc.add_item(pkg.name, name, repo, txn)
ctx.componentdb.add_spec(spec.source.partOf, spec.source.name, repo, txn)
self.d.txn_proc(proc, txn)
for spec in doc.tags("SpecFile"):
src_name = spec.getTag("Source").getTagData("Name")
sources[src_name] = gzip.zlib.compress(spec.toString())
for package in spec.tags("Package"):
pkgstosrc[package.getTagData("Name")] = src_name
return sources, pkgstosrc
def remove_spec(self, name, repo, txn = None):
name = str(name)
def proc(txn):
assert self.has_spec(name, txn=txn)
spec = self.d.get_item(name, repo, txn)
self.d.remove_item(name, txn=txn)
for pkg in spec.packages:
self.dpkgtosrc.remove_item_repo(pkg.name, repo, txn)
ctx.componentdb.remove_spec(spec.source.partOf, spec.source.name, repo, txn)
def list_sources(self, repo=None):
return self.sdb.get_item_keys(repo)
self.d.txn_proc(proc, txn)
def has_spec(self, name, repo=None):
return self.sdb.has_item(name, repo)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dpkgtosrc.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
def get_spec(self, name, repo=None):
spec, repo = self.get_spec_repo(name, repo)
return spec
srcdb = None
def search_spec(self, terms, lang=None, repo=None):
resum = '<Summary xml:lang="%s">.*?%s.*?</Summary>'
redesc = '<Description xml:lang="%s">.*?%s.*?</Description>'
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
found = []
for name, xml in self.sdb.get_items_iter(repo):
if terms == filter(lambda term: re.compile(term, re.I).search(name) or \
re.compile(resum % (lang, term), re.I).search(xml) or \
re.compile(redesc % (lang, term), re.I).search(xml), terms):
found.append(name)
return found
def init():
global srcdb
if srcdb is not None:
return srcdb
def get_spec_repo(self, name, repo=None):
src, repo = self.sdb.get_item_repo(name, repo)
spec = pisi.specfile.SpecFile()
spec.parse(src)
return spec, repo
srcdb = SourceDB()
return srcdb
def finalize():
global srcdb
if srcdb is not None:
srcdb.close()
srcdb = None
def pkgtosrc(self, name, repo=None):
return self.psdb.get_item(name, repo)
+10 -6
View File
@@ -18,6 +18,7 @@ _ = __trans.ugettext
import pisi.context as ctx
import pisi.relation
import pisi.db
""" Dependency relation """
class Dependency(pisi.relation.Relation):
@@ -45,7 +46,7 @@ def dict_satisfies_dep(dict, depinfo):
else:
pkg = dict[pkg_name]
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies_relation(pkg_name, version, release)
return depinfo.satisfies_relation(version, release)
def installed_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
@@ -55,13 +56,14 @@ dependency spec"""
def repo_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
dependency spec"""
packagedb = pisi.db.packagedb.PackageDB()
pkg_name = depinfo.package
if not ctx.packagedb.has_package(pkg_name):
if not packagedb.has_package(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name)
pkg = packagedb.get_package(pkg_name)
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies_relation(pkg_name, version, release)
return depinfo.satisfies_relation(version, release)
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
for dep in deps:
@@ -72,13 +74,15 @@ def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
return True
def satisfies_runtime_deps(pkg):
deps = ctx.packagedb.get_package(pkg).runtimeDependencies()
packagedb = pisi.db.packagedb.PackageDB()
deps = packagedb.get_package(pkg).runtimeDependencies()
return satisfies_dependencies(pkg, deps)
def installable(pkg):
"""calculate if pkg name is installable currently
which means it has to satisfy both install and runtime dependencies"""
if not ctx.packagedb.has_package(pkg):
packagedb = pisi.db.packagedb.PackageDB()
if not packagedb.has_package(pkg):
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
return False
elif satisfies_runtime_deps(pkg):
-2
View File
@@ -15,8 +15,6 @@ during the build process of a package and used in installation.'''
import pisi.pxml.autoxml as autoxml
import pisi.db.lockeddbshelve as shelve
class FileInfo:
"""File holds the information for a File node/tag in files.xml"""
+3 -29
View File
@@ -49,7 +49,7 @@ class Index(xmlfile.XmlFile):
def read_uri(self, uri, tmpdir, force = False):
self.read(uri, tmpDir=tmpdir, sha1sum=not force,
compress=pisi.file.File.auto, sign=pisi.file.File.detached, copylocal = True)
compress=pisi.file.File.auto, sign=pisi.file.File.detached, copylocal = True, nodecode = True)
# read index for a given repo, force means download even if remote not updated
def read_uri_of_repo(self, uri, repo = None, force = False):
@@ -109,32 +109,6 @@ class Index(xmlfile.XmlFile):
ctx.ui.info(_('Adding %s to package index') % pkg)
self.add_package(pkg, deltas, repo_uri)
def update_db(self, repo, txn = None):
# FIXME: updating db takes too much time. So a notify mechanism is used to inform the status
# of the operation.
self.progress = ctx.ui.Progress(len(self.packages)+len(self.specs))
self.processed = 0
def update_progress():
self.processed += 1
ctx.ui.display_progress(operation = "updatingrepo",
percent = self.progress.update(self.processed),
info = _("Updating package database of %s") % repo)
ctx.componentdb.remove_repo(repo, txn=txn)
for comp in self.components:
ctx.componentdb.update_component(comp, repo, txn)
ctx.packagedb.remove_repo(repo, txn=txn)
ctx.packagedb.add_obsoletes(self.distribution.obsoletes, repo, txn=txn)
for pkg in self.packages:
ctx.packagedb.add_package(pkg, repo, txn=txn)
update_progress()
ctx.sourcedb.remove_repo(repo, txn=txn)
for sf in self.specs:
ctx.sourcedb.add_spec(sf, repo, txn=txn)
update_progress()
def add_package(self, path, deltas, repo_uri):
package = pisi.package.Package(path, 'r')
md = package.get_metadata()
@@ -187,10 +161,10 @@ class Index(xmlfile.XmlFile):
#ctx.ui.error(str(Error(*errs)))
def add_spec(self, path, repo_uri):
import pisi.build
import pisi.operations.build
ctx.ui.info(_('Adding %s to source index') % path)
#TODO: may use try/except to handle this
builder = pisi.build.Builder(path)
builder = pisi.operations.build.Builder(path)
#ctx.ui.error(_('SpecFile in %s is corrupt, skipping...') % path)
#ctx.ui.error(str(Error(*errs)))
builder.fetch_component()
+1 -1
View File
@@ -42,7 +42,7 @@ class Source:
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [specfile.Packager, autoxml.mandatory]
class Package(specfile.Package):
class Package(specfile.Package, xmlfile.XmlFile):
__metaclass__ = autoxml.autoxml
t_Build = [ autoxml.Integer, autoxml.optional]
-819
View File
@@ -1,819 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.package
import pisi.context as ctx
import pisi.util as util
import pisi.dependency as dependency
import pisi.conflict
import pisi.pgraph as pgraph
import pisi.cli
import pisi.atomicoperations as atomicoperations
import pisi.ui as ui
class Error(pisi.Error):
pass
def upgrade_pisi():
"""forces to reload pisi modules and runs rebuild-db if needed."""
import pisi
import pisi.context as ctx
import pisi.version
old_filesdbversion = pisi.__filesdbversion__
old_dbversion = pisi.__dbversion__
# we have to keep old_ui or by calling raw init, we lose it
old_ui = ctx.ui
def rebuild_db():
"""rebuild_db is necessary if database structures has changed."""
if pisi.version.Version(pisi.__filesdbversion__) > pisi.version.Version(old_filesdbversion) or \
pisi.version.Version(pisi.__dbversion__) > pisi.version.Version(old_dbversion):
pisi.api.init(database=False, ui=old_ui)
#FIXME: we can not use _( ) here or NoneType object is not callable error is seen from package-manager
ctx.ui.info("* PiSi database version has changed. Rebuilding database...")
pisi.api.rebuild_db(files=True)
ctx.ui.info("* Database rebuild operation is completed succesfully.")
pisi.api.finalize()
def reload_pisi():
for module in sys.modules.keys():
if not module.startswith("pisi.ui") and not module.startswith("pisi.cli") and module.startswith("pisi."):
"""removal from sys.modules forces reload via import"""
del(sys.modules[module])
pisi.api.finalize()
reload_pisi()
reload(pisi)
rebuild_db()
pisi.api.init(ui=old_ui)
# high level operations
def install(packages, reinstall = False, ignore_file_conflicts=False):
"""install a list of packages (either files/urls, or names)"""
if not ctx.get_option('ignore_file_conflicts'):
ctx.set_option('ignore_file_conflicts', ignore_file_conflicts)
# determine if this is a list of files/urls or names
if packages and packages[0].endswith(ctx.const.package_suffix): # they all have to!
return install_pkg_files(packages)
else:
return install_pkg_names(packages, reinstall)
def reorder_base_packages(order):
"""system.base packages must be first in order"""
systembase = ctx.componentdb.get_union_comp('system.base').packages
systembase_order = []
nonbase_order = []
for pkg in order:
if pkg in systembase:
systembase_order.append(pkg)
else:
nonbase_order.append(pkg)
return systembase_order + nonbase_order
def install_pkg_files(package_URIs):
"""install a number of pisi package files"""
ctx.ui.debug('A = %s' % str(package_URIs))
for x in package_URIs:
if not x.endswith(ctx.const.package_suffix):
raise Error(_('Mixing file names and package names not supported yet.'))
if ctx.config.get_option('ignore_dependency'):
# simple code path then
for x in package_URIs:
atomicoperations.install_single_file(x)
return # short circuit
# read the package information into memory first
# regardless of which distribution they come from
d_t = {}
dfn = {}
for x in package_URIs:
package = pisi.package.Package(x)
package.read()
name = str(package.metadata.package.name)
d_t[name] = package.metadata.package
dfn[name] = x
def satisfiesDep(dep):
# is dependency satisfied among available packages
# or packages to be installed?
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.runtimeDependencies()
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:
ctx.ui.info(_("""The following packages will be installed
in the respective order to satisfy extra dependencies:
""") + util.strlist(extra_packages))
if not ctx.ui.confirm(_('Do you want to continue?')):
raise Error(_('External dependencies not satisfied'))
install_pkg_names(extra_packages)
class PackageDB:
def get_package(self, key, repo = None):
return d_t[str(key)]
packagedb = PackageDB()
A = d_t.keys()
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
# 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():
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()
if not ctx.get_option('ignore_package_conflicts'):
conflicts = check_conflicts(order, packagedb)
if conflicts:
remove_conflicting_packages(conflicts)
order.reverse()
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
atomicoperations.install_single_file(dfn[x])
pisi_installed = ctx.installdb.is_installed('pisi')
if 'pisi' in order and pisi_installed:
upgrade_pisi()
# FIXME: this should be done in atomicoperations automatically.
def remove_replaced_packages(order, replaces):
replaced = []
inorder = set(order).intersection(replaces.values())
if inorder:
for pkg in replaces.keys():
if replaces[pkg] in inorder:
replaced.append(pkg)
if replaced:
if remove(replaced, ignore_dep=True, ignore_safety=True):
raise Error(_("Replaced package remains"))
def remove_conflicting_packages(conflicts):
if remove(conflicts, ignore_dep=True, ignore_safety=True):
raise Error(_("Conflicts remain"))
def remove_obsoleted_packages():
obsoletes = filter(ctx.installdb.is_installed, ctx.packagedb.get_obsoletes())
if obsoletes:
if remove(obsoletes, ignore_dep=True, ignore_safety=True):
raise Error(_("Obsoleted packages remaining"))
def check_conflicts(order, packagedb):
"""check if upgrading to the latest versions will cause havoc
done in a simple minded way without regard for dependencies of
conflicts, etc."""
(C, D, pkg_conflicts) = pisi.conflict.calculate_conflicts(order, packagedb)
if D:
raise Error(_("Selected packages [%s] are in conflict with each other.") %
util.strlist(list(D)))
if pkg_conflicts:
conflicts = ""
for pkg in pkg_conflicts.keys():
conflicts += _("[%s conflicts with: %s]\n") % (pkg, util.strlist(pkg_conflicts[pkg]))
ctx.ui.info(_("The following packages have conflicts:\n%s") %
conflicts)
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
raise Error(_("Conflicts remain"))
return list(C)
def is_upgradable(name, ignore_build = False):
if not ctx.installdb.is_installed(name):
return False
(version, release, build) = ctx.installdb.get_version(name)
try:
pkg = ctx.packagedb.get_package(name)
except KeyboardInterrupt:
raise
except Exception, e: #FIXME: what exception could we catch here, replace with that.
return False
if ignore_build or (not build) or (not pkg.build):
return pisi.version.Version(release) < pisi.version.Version(pkg.release)
else:
return build < pkg.build
def upgrade_base(A = set(), ignore_package_conflicts = False):
ignore_build = ctx.get_option('ignore_build_no')
if not ctx.get_option('ignore_safety'):
if ctx.componentdb.has_component('system.base'):
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
extra_installs = filter(lambda x: not ctx.installdb.is_installed(x), systembase - set(A))
if extra_installs:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
util.strlist(extra_installs))
G_f, install_order = plan_install_pkg_names(extra_installs, ignore_package_conflicts)
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(install_order))
upgrade_order = []
if extra_upgrades:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
util.strlist(extra_upgrades))
G_f, upgrade_order = plan_upgrade(extra_upgrades)
# return packages that must be added to any installation
return set(install_order + upgrade_order)
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
return set()
def install_pkg_names(A, reinstall = False):
"""This is the real thing. It installs packages from
the repository, trying to perform a minimum number of
installs"""
A = [str(x) for x in A] #FIXME: why do we still get unicode input here? :/ -- exa
# A was a list, remove duplicates
A_0 = A = set(A)
# filter packages that are already installed
if not reinstall:
Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
d = A - Ap
if len(d) > 0:
ctx.ui.warning(_("The following package(s) are already installed and are not going to be installed again:\n") +
util.strlist(d))
A = Ap
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
A |= upgrade_base(A)
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_install_pkg_names(A)
else:
G_f = None
order = list(A)
# Bug 4211
if ctx.componentdb.has_component('system.base'):
order = reorder_base_packages(order)
if len(order) > 1:
ctx.ui.info(_("Following packages will be installed in the respective "
"order to satisfy dependencies:\n") + util.strlist(order))
total_size, cached_size = calculate_download_sizes(order)
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - 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)
pisi_installed = ctx.installdb.is_installed('pisi')
for x in order:
atomicoperations.install_single_name(x, True) # allow reinstalls here
if 'pisi' in order and pisi_installed:
upgrade_pisi()
def plan_install_pkg_names(A, ignore_package_conflicts = False):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.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 = ctx.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()
if not ctx.get_option('ignore_package_conflicts') and not ignore_package_conflicts:
conflicts = check_conflicts(order, ctx.packagedb)
if conflicts:
remove_conflicting_packages(conflicts)
return G_f, order
def upgrade(A):
upgrade_pkg_names(A)
def upgrade_pkg_names(A = []):
"""Re-installs packages from the repository, trying to perform
a minimum or maximum number of upgrades according to options."""
ignore_build = ctx.get_option('ignore_build_no')
security_only = ctx.get_option('security_only')
replaced = []
replaces = ctx.packagedb.get_replaces()
if not A:
# if A is empty, then upgrade all packages
A = ctx.installdb.list_installed()
A_0 = A = set(A)
Ap = []
for x in A:
if x.endswith(ctx.const.package_suffix):
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
# Handling of replacement packages
if x in replaces.values():
Ap.append(x)
continue
if x in replaces.keys():
Ap.append(replaces[x])
continue
if not ctx.installdb.is_installed(x):
ctx.ui.info(_('Package %s is not installed.') % x, True)
continue
(version, release, build) = ctx.installdb.get_version(x)
if ctx.packagedb.has_package(x):
pkg = ctx.packagedb.get_package(x)
else:
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
continue
if security_only:
updates = [i for i in pkg.history if pisi.version.Version(i.release) > pisi.version.Version(release)]
if not pisi.util.any(lambda i:i.type == 'security', updates):
continue
if ignore_build or (not build) or (not pkg.build):
if pisi.version.Version(release) < pisi.version.Version(pkg.release):
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest release %s.')
% (pkg.name, pkg.release), True)
else:
if build < pkg.build:
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest build %s.')
% (pkg.name, pkg.build), True)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to upgrade.'))
return True
A |= upgrade_base(A)
ctx.ui.debug('A = %s' % str(A))
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_upgrade(A)
else:
G_f = None
order = list(A)
# Bug 4211
if ctx.componentdb.has_component('system.base'):
order = reorder_base_packages(order)
if not ctx.get_option('ignore_package_conflicts'):
conflicts = check_conflicts(order, ctx.packagedb)
ctx.ui.info(_('The following packages will be upgraded: ') +
util.strlist(order))
total_size, cached_size = calculate_download_sizes(order)
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - A_0 - set(replaces.values()):
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)
paths = []
for x in order:
ctx.ui.info(util.colorize(_("Downloading %d / %d") % (order.index(x)+1, len(order)), "yellow"))
install_op = atomicoperations.Install.from_name(x)
paths.append(install_op.package_fname)
# fetch to be upgraded packages but do not install them.
if ctx.get_option('fetch_only'):
return
if not ctx.get_option('ignore_package_conflicts'):
if conflicts:
remove_conflicting_packages(conflicts)
if replaces:
remove_replaced_packages(order, replaces)
remove_obsoleted_packages()
for path in paths:
ctx.ui.info(util.colorize(_("Installing %d / %d") % (paths.index(path)+1, len(paths)), "yellow"))
install_op = atomicoperations.Install(path, ignore_file_conflicts = True)
install_op.install(True)
if 'pisi' in order:
upgrade_pisi()
def plan_upgrade(A):
# try to construct a pisi graph of packages to
# install / reinstall
packagedb = ctx.packagedb
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
# TODO: conflicts
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
# add packages that can be upgraded
if ctx.installdb.is_installed(dep.package) and dependency.installed_satisfies_dep(dep):
continue
if dependency.repo_satisfies_dep(dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
else:
ctx.ui.error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
raise Error(_("Upgrade is not possible."))
B = Bp
# now, search reverse dependencies to see if anything
# should be upgraded
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
rev_deps = packagedb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
# add only installed but unsatisfied reverse dependencies
if ctx.installdb.is_installed(rev_dep) and \
(not dependency.installed_satisfies_dep(depinfo)):
if not dependency.repo_satisfies_dep(depinfo):
raise Error(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
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()
order.reverse()
return G_f, order
def remove(A, ignore_dep = False, ignore_safety = False):
"""remove set A of packages from system (A is a list of package names)"""
A = [str(x) for x in A]
# filter packages that are not installed
A_0 = A = set(A)
if not ctx.get_option('ignore_safety') and not ignore_safety:
if ctx.componentdb.has_component('system.base'):
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
refused = A.intersection(systembase)
if refused:
ctx.ui.warning(_('Safety switch: cannot remove the following packages in system.base: ') +
util.strlist(refused))
A = A - systembase
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
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 False
if not ctx.config.get_option('ignore_dependency') and not ignore_dep:
G_f, order = plan_remove(A)
else:
G_f = None
order = A
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?')):
ctx.ui.warning(_('Package removal declined'))
return False
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
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)
def plan_remove(A):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb, pisi.db.itembyrepodb.installed) # 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:
rev_deps = ctx.packagedb.get_rev_deps(x, pisi.db.itembyrepodb.installed)
for (rev_dep, depinfo) in rev_deps:
# we don't deal with uninstalled rev deps
# and unsatisfied dependencies (this is important, too)
if ctx.packagedb.has_package(rev_dep, pisi.db.itembyrepodb.installed) 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()
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_union_comp(x).sources)
else:
Ap.add(x)
return Ap
def emerge(A):
# A was a list, remove duplicates and expand components
A = [str(x) for x in A]
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 emerge.'))
return
#A |= upgrade_base(A)
# FIXME: Errr... order_build changes type conditionally and this
# is not good. - baris
if not ctx.config.get_option('ignore_dependency'):
G_f, order_inst, order_build = plan_emerge(A)
else:
G_f = None
order_inst = []
order_build = A
if order_inst:
ctx.ui.info(_("""The following minimal list of packages will be installed
from repository in the respective order to satisfy dependencies:
""") + util.strlist(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_build))
if ctx.get_option('dry_run'):
return
if len(order_inst) + len(order_build) > 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_inst)
pisi_installed = ctx.installdb.is_installed('pisi')
for x in order_inst:
atomicoperations.install_single_name(x)
#ctx.ui.notify(ui.packagestogo, order = order_build)
for x in order_build:
package_names = atomicoperations.build(x)[0]
install_pkg_files(package_names) # handle inter-package deps here
# FIXME: take a look at the fixme above :(, we have to be sure
# that order_build is a known type...
U = set(order_build)
U.update(order_inst)
if 'pisi' in order_build or (('pisi' in U) and pisi_installed):
upgrade_pisi()
def plan_emerge(A):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pisi.graph.Digraph()
def get_spec(name):
if ctx.sourcedb.has_spec(name):
return ctx.sourcedb.get_spec(name)
else:
raise Error(_('Cannot find source package: %s') % name)
def get_src(name):
return get_spec(name).source
def add_src(src):
if not str(src.name) in G_f.vertices():
G_f.add_vertex(str(src.name), (src.version, src.release))
def pkgtosrc(pkg):
return ctx.sourcedb.pkgtosrc(pkg)
# setup first
#specfiles = [ ctx.sourcedb.get_source(x)[1] for x in A ]
#pkgtosrc = {}
B = A
install_list = set()
while len(B) > 0:
Bp = set()
for x in B:
sf = get_spec(x)
src = sf.source
add_src(src)
# add dependencies
def process_dep(dep):
if not dependency.installed_satisfies_dep(dep):
if dependency.repo_satisfies_dep(dep):
install_list.add(dep.package)
return
srcdep = pkgtosrc(dep.package)
if not srcdep in G_f.vertices():
Bp.add(srcdep)
add_src(get_src(srcdep))
if not src.name == srcdep: # firefox - firefox-devel thing
G_f.add_edge(src.name, srcdep)
for builddep in src.buildDependencies:
process_dep(builddep)
for pkg in sf.packages:
for rtdep in pkg.packageDependencies:
process_dep(rtdep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order_build = G_f.topological_sort()
order_build.reverse()
G_f2, order_inst = plan_install_pkg_names(install_list)
return G_f, order_inst, order_build
def calculate_download_sizes(order):
total_size = cached_size = 0
for pkg in [ctx.packagedb.get_package(name) for name in order]:
delta = None
if ctx.installdb.is_installed(pkg.name):
(version, release, build) = ctx.installdb.get_version(pkg.name)
delta = pkg.get_delta(buildFrom=build)
if delta:
fn = os.path.basename(delta.packageURI)
pkg_hash = delta.packageHash
pkg_size = delta.packageSize
else:
fn = os.path.basename(pkg.packageURI)
pkg_hash = pkg.packageHash
pkg_size = pkg.packageSize
path = util.join_path(ctx.config.packages_dir(), fn)
# check the file and sha1sum to be sure it _is_ the cached package
if os.path.exists(path) and util.sha1_file(path) == pkg_hash:
cached_size += pkg_size
total_size += pkg_size
ctx.ui.notify(ui.cached, total=total_size, cached=cached_size)
return total_size, cached_size
@@ -1,7 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
# Copyright (C) 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
@@ -11,15 +10,3 @@
# Please read the COPYING file.
#
from pisi.actionsapi import autotools
WorkDir='popt-1.7'
def setup():
autotools.configure( '--with-nls' )
def build():
autotools.make()
def install():
autotools.install()
+15 -11
View File
@@ -29,7 +29,7 @@ import pisi.util
import pisi.file
import pisi.context as ctx
import pisi.dependency as dependency
import pisi.operations as operations
import pisi.operations.install as install
import pisi.sourcearchive
import pisi.files
import pisi.fetcher
@@ -39,12 +39,11 @@ import pisi.package
import pisi.component as component
import pisi.archive as archive
import pisi.actionsapi.variables
import pisi.db
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
@@ -93,9 +92,11 @@ class Builder:
@staticmethod
def from_name(name):
repodb = pisi.db.repodb.RepoDB()
sourcedb = pisi.db.sourcedb.SourceDB()
# download package and return an installer object
# find package in repository
sf, reponame = ctx.sourcedb.get_spec_repo(name)
sf, reponame = sourcedb.get_spec_repo(name)
src = sf.source
if src:
@@ -103,7 +104,7 @@ class Builder:
if src_uri.is_absolute_path():
src_path = str(src_uri)
else:
repo = ctx.repodb.get_repo(reponame)
repo = repodb.get_repo(reponame)
#FIXME: don't use dirname to work on URLs
src_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(src_uri.path()))
@@ -138,6 +139,9 @@ class Builder:
self.actionGlobals = None
self.srcDir = None
self.componentdb = pisi.db.componentdb.ComponentDB()
self.installdb = pisi.db.installdb.InstallDB()
def set_spec_file(self, specuri):
if not specuri.is_remote_file():
specuri = pisi.uri.URI(os.path.realpath(specuri.get_uri())) # FIXME: doesn't work for file://
@@ -429,7 +433,7 @@ class Builder:
os.chdir(self.srcDir)
if func in self.actionLocals:
if ctx.get_option('ignore_sandbox'):
if not ctx.get_option('enable_sandbox'):
self.actionLocals[func]()
else:
import catbox
@@ -464,11 +468,11 @@ class Builder:
build_deps = self.spec.source.buildDependencies
if not ctx.get_option('ignore_safety'):
if ctx.componentdb.has_component('system.devel'):
if self.componentdb.has_component('system.devel'):
build_deps_names = set([x.package for x in build_deps])
devel_deps_names = set(ctx.componentdb.get_component('system.devel').packages)
devel_deps_names = set(self.componentdb.get_component('system.devel').packages)
extra_names = devel_deps_names - build_deps_names
extra_names = filter(lambda x: not ctx.installdb.is_installed(x), extra_names)
extra_names = filter(lambda x: not self.installdb.has_package(x), extra_names)
if extra_names:
ctx.ui.warning(_('Safety switch: following extra packages in system.devel will be installed: ') +
pisi.util.strlist(extra_names))
@@ -502,7 +506,7 @@ class Builder:
if ctx.ui.confirm(
_('Do you want to install the unsatisfied build dependencies')):
ctx.ui.info(_('Installing build dependencies.'))
operations.install([dep.package for dep in dep_unsatis])
install.install([dep.package for dep in dep_unsatis])
else:
fail()
else:
@@ -607,7 +611,7 @@ class Builder:
metadata.package.distribution = ctx.config.values.general.distribution
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
metadata.package.architecture = "Any"
metadata.package.architecture = ctx.config.values.general.architecture
metadata.package.packageFormat = ctx.get_option('package_format')
size = 0
+146
View File
@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.operations
import pisi.context as ctx
import pisi.util as util
import pisi.atomicoperations as atomicoperations
import pisi.dependency as dependency
import pisi.ui as ui
import pisi.db
def emerge(A):
# A was a list, remove duplicates and expand components
A = [str(x) for x in A]
A_0 = A = pisi.operations.helper.expand_src_components(set(A))
ctx.ui.debug('A = %s' % str(A))
if len(A)==0:
ctx.ui.info(_('No packages to emerge.'))
return
#A |= upgrade_base(A)
# FIXME: Errr... order_build changes type conditionally and this
# is not good. - baris
if not ctx.config.get_option('ignore_dependency'):
G_f, order_inst, order_build = plan_emerge(A)
else:
G_f = None
order_inst = []
order_build = A
if order_inst:
ctx.ui.info(_("""The following minimal list of packages will be installed
from repository in the respective order to satisfy dependencies:
""") + util.strlist(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_build))
if ctx.get_option('dry_run'):
return
if len(order_inst) + len(order_build) > 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_inst)
for x in order_inst:
atomicoperations.install_single_name(x)
#ctx.ui.notify(ui.packagestogo, order = order_build)
for x in order_build:
package_names = atomicoperations.build(x)[0]
pisi.operations.install.install_pkg_files(package_names) # handle inter-package deps here
# FIXME: take a look at the fixme above :(, we have to be sure
# that order_build is a known type...
U = set(order_build)
U.update(order_inst)
def plan_emerge(A):
sourcedb = pisi.db.sourcedb.SourceDB()
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pisi.graph.Digraph()
def get_spec(name):
if sourcedb.has_spec(name):
return sourcedb.get_spec(name)
else:
raise Exception(_('Cannot find source package: %s') % name)
def get_src(name):
return get_spec(name).source
def add_src(src):
if not str(src.name) in G_f.vertices():
G_f.add_vertex(str(src.name), (src.version, src.release))
def pkgtosrc(pkg):
return sourcedb.pkgtosrc(pkg)
# setup first
#specfiles = [ sourcedb.get_source(x)[1] for x in A ]
#pkgtosrc = {}
B = A
install_list = set()
while len(B) > 0:
Bp = set()
for x in B:
sf = get_spec(x)
src = sf.source
add_src(src)
# add dependencies
def process_dep(dep):
if not dependency.installed_satisfies_dep(dep):
if dependency.repo_satisfies_dep(dep):
install_list.add(dep.package)
return
srcdep = pkgtosrc(dep.package)
if not srcdep in G_f.vertices():
Bp.add(srcdep)
add_src(get_src(srcdep))
if not src.name == srcdep: # firefox - firefox-devel thing
G_f.add_edge(src.name, srcdep)
for builddep in src.buildDependencies:
process_dep(builddep)
for pkg in sf.packages:
for rtdep in pkg.packageDependencies:
process_dep(rtdep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order_build = G_f.topological_sort()
order_build.reverse()
G_f2, order_inst = pisi.operations.install.plan_install_pkg_names(install_list)
return G_f, order_inst, order_build
+108
View File
@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.util as util
import pisi.ui as ui
import pisi.conflict
import pisi.db
def reorder_base_packages(order):
componentdb = pisi.db.componentdb.ComponentDB()
"""system.base packages must be first in order"""
systembase = componentdb.get_union_component('system.base').packages
systembase_order = []
nonbase_order = []
for pkg in order:
if pkg in systembase:
systembase_order.append(pkg)
else:
nonbase_order.append(pkg)
return systembase_order + nonbase_order
def check_conflicts(order, packagedb):
"""check if upgrading to the latest versions will cause havoc
done in a simple minded way without regard for dependencies of
conflicts, etc."""
(C, D, pkg_conflicts) = pisi.conflict.calculate_conflicts(order, packagedb)
if D:
raise Exception(_("Selected packages [%s] are in conflict with each other.") %
util.strlist(list(D)))
if pkg_conflicts:
conflicts = ""
for pkg in pkg_conflicts.keys():
conflicts += _("[%s conflicts with: %s]\n") % (pkg, util.strlist(pkg_conflicts[pkg]))
ctx.ui.info(_("The following packages have conflicts:\n%s") %
conflicts)
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
raise Exception(_("Conflicts remain"))
return list(C)
def expand_src_components(A):
componentdb = pisi.db.componentdb.ComponentDB()
Ap = set()
for x in A:
if componentdb.has_component(x):
Ap = Ap.union(componentdb.get_union_component(x).sources)
else:
Ap.add(x)
return Ap
def calculate_download_sizes(order):
total_size = cached_size = 0
installdb = pisi.db.installdb.InstallDB()
packagedb = pisi.db.packagedb.PackageDB()
for pkg in [packagedb.get_package(name) for name in order]:
delta = None
if installdb.has_package(pkg.name):
(version, release, build) = installdb.get_version(pkg.name)
delta = pkg.get_delta(buildFrom=build)
if delta:
fn = os.path.basename(delta.packageURI)
pkg_hash = delta.packageHash
pkg_size = delta.packageSize
else:
fn = os.path.basename(pkg.packageURI)
pkg_hash = pkg.packageHash
pkg_size = pkg.packageSize
path = util.join_path(ctx.config.cached_packages_dir(), fn)
# check the file and sha1sum to be sure it _is_ the cached package
if os.path.exists(path) and util.sha1_file(path) == pkg_hash:
cached_size += pkg_size
total_size += pkg_size
ctx.ui.notify(ui.cached, total=total_size, cached=cached_size)
return total_size, cached_size
+239
View File
@@ -0,0 +1,239 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.util as util
import pisi.atomicoperations as atomicoperations
import pisi.operations as operations
import pisi.pgraph as pgraph
import pisi.dependency as dependency
import pisi.ui as ui
import pisi.db
def install(packages, reinstall = False, ignore_file_conflicts=False):
"""install a list of packages (either files/urls, or names)"""
if not ctx.get_option('ignore_file_conflicts'):
ctx.set_option('ignore_file_conflicts', ignore_file_conflicts)
# determine if this is a list of files/urls or names
if packages and packages[0].endswith(ctx.const.package_suffix): # they all have to!
return install_pkg_files(packages)
else:
return install_pkg_names(packages, reinstall)
def install_pkg_names(A, reinstall = False):
"""This is the real thing. It installs packages from
the repository, trying to perform a minimum number of
installs"""
installdb = pisi.db.installdb.InstallDB()
A = [str(x) for x in A] #FIXME: why do we still get unicode input here? :/ -- exa
# A was a list, remove duplicates
A_0 = A = set(A)
# filter packages that are already installed
if not reinstall:
Ap = set(filter(lambda x: not installdb.has_package(x), A))
d = A - Ap
if len(d) > 0:
ctx.ui.warning(_("The following package(s) are already installed and are not going to be installed again:\n") +
util.strlist(d))
A = Ap
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
A |= operations.upgrade.upgrade_base(A)
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_install_pkg_names(A)
else:
G_f = None
order = list(A)
componentdb = pisi.db.componentdb.ComponentDB()
# Bug 4211
if componentdb.has_component('system.base'):
order = operations.helper.reorder_base_packages(order)
if len(order) > 1:
ctx.ui.info(_("Following packages will be installed in the respective "
"order to satisfy dependencies:\n") + util.strlist(order))
total_size, cached_size = operations.helper.calculate_download_sizes(order)
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - 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, True) # allow reinstalls here
def install_pkg_files(package_URIs):
"""install a number of pisi package files"""
ctx.ui.debug('A = %s' % str(package_URIs))
for x in package_URIs:
if not x.endswith(ctx.const.package_suffix):
raise Exception(_('Mixing file names and package names not supported yet.'))
if ctx.config.get_option('ignore_dependency'):
# simple code path then
for x in package_URIs:
atomicoperations.install_single_file(x)
return # short circuit
# read the package information into memory first
# regardless of which distribution they come from
d_t = {}
dfn = {}
for x in package_URIs:
package = pisi.package.Package(x)
package.read()
name = str(package.metadata.package.name)
d_t[name] = package.metadata.package
dfn[name] = x
def satisfiesDep(dep):
# is dependency satisfied among available packages
# or packages to be installed?
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.runtimeDependencies()
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:
ctx.ui.info(_("""The following packages will be installed
in the respective order to satisfy extra dependencies:
""") + util.strlist(extra_packages))
if not ctx.ui.confirm(_('Do you want to continue?')):
raise Exception(_('External dependencies not satisfied'))
install_pkg_names(extra_packages)
class PackageDB:
def get_package(self, key, repo = None):
return d_t[str(key)]
packagedb = PackageDB()
A = d_t.keys()
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
# 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():
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()
if not ctx.get_option('ignore_package_conflicts'):
conflicts = operations.helper.check_conflicts(order, packagedb)
if conflicts:
operations.remove.remove_conflicting_packages(conflicts)
order.reverse()
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
atomicoperations.install_single_file(dfn[x])
def plan_install_pkg_names(A, ignore_package_conflicts = False):
# try to construct a pisi graph of packages to
# install / reinstall
packagedb = pisi.db.packagedb.PackageDB()
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()
if not ctx.get_option('ignore_package_conflicts') and not ignore_package_conflicts:
conflicts = operations.helper.check_conflicts(order, packagedb)
if conflicts:
operations.remove.remove_conflicting_packages(conflicts)
return G_f, order
+143
View File
@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.atomicoperations as atomicoperations
import pisi.dependency as dependency
import pisi.pgraph as pgraph
import pisi.util as util
import pisi.ui as ui
import pisi.db
def remove(A, ignore_dep = False, ignore_safety = False):
"""remove set A of packages from system (A is a list of package names)"""
componentdb = pisi.db.componentdb.ComponentDB()
installdb = pisi.db.installdb.InstallDB()
A = [str(x) for x in A]
# filter packages that are not installed
A_0 = A = set(A)
if not ctx.get_option('ignore_safety') and not ignore_safety:
if componentdb.has_component('system.base'):
systembase = set(componentdb.get_union_component('system.base').packages)
refused = A.intersection(systembase)
if refused:
raise pisi.Error(_('Safety switch: cannot remove the following packages in system.base: ') +
util.strlist(refused))
A = A - systembase
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
Ap = []
for x in A:
if installdb.has_package(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 False
if not ctx.config.get_option('ignore_dependency') and not ignore_dep:
G_f, order = plan_remove(A)
else:
G_f = None
order = A
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?')):
ctx.ui.warning(_('Package removal declined'))
return False
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
if installdb.has_package(x):
atomicoperations.remove_single(x)
else:
ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x)
def plan_remove(A):
# try to construct a pisi graph of packages to
# install / reinstall
installdb = pisi.db.installdb.InstallDB()
G_f = pgraph.PGraph(installdb) # 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:
rev_deps = installdb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
# we don't deal with uninstalled rev deps
# and unsatisfied dependencies (this is important, too)
if installdb.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()
return G_f, order
# FIXME: this should be done in atomicoperations automatically.
def remove_replaced_packages(order, replaces):
replaced = []
inorder = set(order).intersection(replaces.values())
if inorder:
for pkg in replaces.keys():
if replaces[pkg] in inorder:
replaced.append(pkg)
if replaced:
if remove(replaced, ignore_dep=True, ignore_safety=True):
raise Exception(_("Replaced package remains"))
def remove_conflicting_packages(conflicts):
if remove(conflicts, ignore_dep=True, ignore_safety=True):
raise Exception(_("Conflicts remain"))
def remove_obsoleted_packages():
installdb = pisi.db.installdb.InstallDB()
packagedb = pisi.db.packagedb.PackageDB()
obsoletes = filter(installdb.has_package, packagedb.get_obsoletes())
if obsoletes:
if remove(obsoletes, ignore_dep=True, ignore_safety=True):
raise Exception(_("Obsoleted packages remaining"))
+261
View File
@@ -0,0 +1,261 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.ui as ui
import pisi.context as ctx
import pisi.pgraph as pgraph
import pisi.atomicoperations as atomicoperations
import pisi.operations as operations
import pisi.util as util
import pisi.dependency as dependency
import pisi.db
def upgrade(A):
upgrade_pkg_names(A)
def upgrade_pkg_names(A = []):
"""Re-installs packages from the repository, trying to perform
a minimum or maximum number of upgrades according to options."""
ignore_build = ctx.get_option('ignore_build_no')
security_only = ctx.get_option('security_only')
packagedb = pisi.db.packagedb.PackageDB()
replaces = packagedb.get_replaces()
installdb = pisi.db.installdb.InstallDB()
if not A:
# if A is empty, then upgrade all packages
A = installdb.list_installed()
A_0 = A = set(A)
Ap = []
for x in A:
if x.endswith(ctx.const.package_suffix):
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
# Handling of replacement packages
if x in replaces.values():
Ap.append(x)
continue
if x in replaces.keys():
Ap.append(replaces[x])
continue
if not installdb.has_package(x):
ctx.ui.info(_('Package %s is not installed.') % x, True)
continue
(version, release, build) = installdb.get_version(x)
if packagedb.has_package(x):
pkg = packagedb.get_package(x)
else:
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
continue
if security_only:
updates = [i for i in pkg.history if pisi.version.Version(i.release) > pisi.version.Version(release)]
if not pisi.util.any(lambda i:i.type == 'security', updates):
continue
if ignore_build or (not build) or (not pkg.build):
if pisi.version.Version(release) < pisi.version.Version(pkg.release):
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest release %s.')
% (pkg.name, pkg.release), True)
else:
if build < pkg.build:
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest build %s.')
% (pkg.name, pkg.build), True)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to upgrade.'))
return True
A |= upgrade_base(A)
ctx.ui.debug('A = %s' % str(A))
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_upgrade(A)
else:
G_f = None
order = list(A)
componentdb = pisi.db.componentdb.ComponentDB()
# Bug 4211
if componentdb.has_component('system.base'):
order = operations.helper.reorder_base_packages(order)
if not ctx.get_option('ignore_package_conflicts'):
conflicts = operations.helper.check_conflicts(order, packagedb)
ctx.ui.info(_('The following packages will be upgraded: ') +
util.strlist(order))
total_size, cached_size = operations.helper.calculate_download_sizes(order)
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if set(order) - A_0 - set(replaces.values()):
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)
paths = []
for x in order:
ctx.ui.info(util.colorize(_("Downloading %d / %d") % (order.index(x)+1, len(order)), "yellow"))
install_op = atomicoperations.Install.from_name(x)
paths.append(install_op.package_fname)
# fetch to be upgraded packages but do not install them.
if ctx.get_option('fetch_only'):
return
if not ctx.get_option('ignore_package_conflicts'):
if conflicts:
operations.remove.remove_conflicting_packages(conflicts)
if replaces:
operations.remove.remove_replaced_packages(order, replaces)
operations.remove.remove_obsoleted_packages()
for path in paths:
ctx.ui.info(util.colorize(_("Installing %d / %d") % (paths.index(path)+1, len(paths)), "yellow"))
install_op = atomicoperations.Install(path, ignore_file_conflicts = True)
install_op.install(True)
def plan_upgrade(A):
# try to construct a pisi graph of packages to
# install / reinstall
packagedb = pisi.db.packagedb.PackageDB()
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
installdb = pisi.db.installdb.InstallDB()
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
# add packages that can be upgraded
if installdb.has_package(dep.package) and dependency.installed_satisfies_dep(dep):
continue
if dependency.repo_satisfies_dep(dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
else:
ctx.ui.error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
raise Exception(_("Upgrade is not possible."))
B = Bp
# now, search reverse dependencies to see if anything
# should be upgraded
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
rev_deps = packagedb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
# add only installed but unsatisfied reverse dependencies
if installdb.has_package(rev_dep) and \
(not dependency.installed_satisfies_dep(depinfo)):
if not dependency.repo_satisfies_dep(depinfo):
raise Exception(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
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()
order.reverse()
return G_f, order
def upgrade_base(A = set(), ignore_package_conflicts = False):
installdb = pisi.db.installdb.InstallDB()
componentdb = pisi.db.componentdb.ComponentDB()
ignore_build = ctx.get_option('ignore_build_no')
if not ctx.get_option('ignore_safety'):
if componentdb.has_component('system.base'):
systembase = set(componentdb.get_union_component('system.base').packages)
extra_installs = filter(lambda x: not installdb.has_package(x), systembase - set(A))
if extra_installs:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
util.strlist(extra_installs))
G_f, install_order = operations.install.plan_install_pkg_names(extra_installs, ignore_package_conflicts)
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(install_order))
upgrade_order = []
if extra_upgrades:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
util.strlist(extra_upgrades))
G_f, upgrade_order = plan_upgrade(extra_upgrades)
# return packages that must be added to any installation
return set(install_order + upgrade_order)
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
return set()
def is_upgradable(name, ignore_build = False):
installdb = pisi.db.installdb.InstallDB()
packagedb = pisi.db.packagedb.PackageDB()
if not installdb.has_package(name):
return False
(version, release, build) = installdb.get_version(name)
try:
pkg_version, pkg_release, pkg_build = packagedb.get_version(name, packagedb.which_repo(name))
except KeyboardInterrupt:
raise
except Exception: #FIXME: what exception could we catch here, replace with that.
return False
if ignore_build or (not build) or (not pkg_build):
return pisi.version.Version(release) < pisi.version.Version(pkg_release)
else:
return build < pkg_build
+2 -2
View File
@@ -44,7 +44,7 @@ class Package:
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
def fetch_remote_file(self, url):
dest = ctx.config.packages_dir()
dest = ctx.config.cached_packages_dir()
self.filepath = os.path.join(dest, url.filename())
if not os.path.exists(self.filepath):
@@ -145,7 +145,7 @@ class Package:
+ self.metadata.package.version + '-' \
+ self.metadata.package.release
return os.path.join( ctx.config.lib_dir(), 'package', packageDir)
return os.path.join(ctx.config.packages_dir(), packageDir)
def comar_dir(self):
return os.path.join(self.pkg_dir(), ctx.const.comar_dir)
+12 -11
View File
@@ -15,29 +15,30 @@
import string
import pisi
import pisi.db
import pisi.context as ctx
import graph
# Cache the results from packagedb queries in a graph
class PGraph(graph.Digraph):
def __init__(self, packagedb, repo = pisi.db.itembyrepodb.repos):
def __init__(self, packagedb):
super(PGraph, self).__init__()
self.packagedb = packagedb
self.repo = repo
def add_package(self, pkg):
pkg1 = self.packagedb.get_package(pkg, self.repo)
pkg1 = self.packagedb.get_package(pkg)
self.add_vertex(str(pkg), (pkg1.version, pkg1.release))
def add_plain_dep(self, pkg1name, pkg2name):
pkg1data = None
if not pkg1name in self.vertices():
pkg1 = self.packagedb.get_package(pkg1name, self.repo)
pkg1 = self.packagedb.get_package(pkg1name)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg2name in self.vertices():
pkg2 = self.packagedb.get_package(pkg2name, self.repo)
pkg2 = self.packagedb.get_package(pkg2name)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
pkg1data, pkg2data )
@@ -45,11 +46,11 @@ class PGraph(graph.Digraph):
def add_dep(self, pkg, depinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg, self.repo)
pkg1 = self.packagedb.get_package(pkg)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(depinfo.package, self.repo)
pkg2 = self.packagedb.get_package(depinfo.package)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
pkg1data, pkg2data )
@@ -57,11 +58,11 @@ class PGraph(graph.Digraph):
def add_rev_dep(self, depinfo, pkg):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(depinfo.package, self.repo)
pkg1 = self.packagedb.get_package(depinfo.package)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(pkg, self.repo)
pkg2 = self.packagedb.get_package(pkg)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo),
pkg1data, pkg2data )
@@ -69,11 +70,11 @@ class PGraph(graph.Digraph):
def add_conflict(self, pkg, conflinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg, self.repo)
pkg1 = self.packagedb.get_package(pkg)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg in self.vertices():
pkg2 = self.packagedb.get_package(conflinfo.package, self.repo)
pkg2 = self.packagedb.get_package(conflinfo.package)
pkg2data = (pkg2.version, pkg2.release)
# FIXME: WTF? /usr/lib/pardus/pisi/pgraph.py:80: Invalid arguments to (add_biedge), got 5, expected between 2 and 3 / caglar
+5 -1
View File
@@ -438,10 +438,14 @@ class autoxml(oo.autosuper, oo.autoprop):
errs.append(_("autoxml.parse: String '%s' has errors") % xml)
def read(self, uri, keepDoc = False, tmpDir = '/tmp',
sha1sum = False, compress = None, sign = None, copylocal = False):
sha1sum = False, compress = None, sign = None, copylocal = False, nodecode = False):
"read XML file and decode it into a python object"
self.readxml(uri, tmpDir, sha1sum=sha1sum,
compress=compress, sign=sign, copylocal=copylocal)
if nodecode:
return
errs = []
self.decode(self.rootNode(), errs)
if errs:
+7 -6
View File
@@ -14,10 +14,10 @@ import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
import pisi
import pisi.version
import pisi.db
import pisi.pxml.autoxml as autoxml
import pisi.db.itembyrepodb
class Relation:
@@ -31,7 +31,7 @@ class Relation:
a_releaseFrom = [autoxml.String, autoxml.optional]
a_releaseTo = [autoxml.String, autoxml.optional]
def satisfies_relation(self, pkg_name, version, release):
def satisfies_relation(self, version, release):
ret = True
v = pisi.version.Version(version)
if self.version:
@@ -50,10 +50,11 @@ class Relation:
return ret
def installed_package_satisfies(relation):
installdb = pisi.db.installdb.InstallDB()
pkg_name = relation.package
if not ctx.installdb.is_installed(pkg_name):
if not installdb.has_package(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name, pisi.db.itembyrepodb.installed)
pkg = installdb.get_package(pkg_name)
(version, release) = (pkg.version, pkg.release)
return relation.satisfies_relation(pkg_name, version, release)
return relation.satisfies_relation(version, release)
+4 -3
View File
@@ -33,7 +33,7 @@ import pisi.replace
import pisi.conflict
import pisi.component as component
import pisi.util as util
import pisi.db
class Error(pisi.Error):
pass
@@ -182,8 +182,9 @@ class Package:
debug_package = False
def runtimeDependencies(self):
componentdb = pisi.db.componentdb.ComponentDB()
deps = self.packageDependencies
deps += [ ctx.componentdb.get_component[x].packages for x in self.componentDependencies ]
deps += [ componentdb.get_component[x].packages for x in self.componentDependencies ]
return deps
def pkg_dir(self):
@@ -191,7 +192,7 @@ class Package:
+ self.version + '-' \
+ self.release
return util.join_path( ctx.config.lib_dir(), 'package', packageDir)
return util.join_path(ctx.config.packages_dir(), packageDir)
def installable(self):
"""calculate if pkg is installable currently"""
+1 -1
View File
@@ -74,7 +74,7 @@ class UI(object):
"ask a yes/no question"
pass
def display_progress(self, pd):
def display_progress(self, **ka):
"display progress"
pass
-2
View File
@@ -15,9 +15,7 @@ import sys
import pisi
def show_info(filename):
pisi.api.init(database=False, comar=False)
metadata, files = pisi.api.info_file(filename)
pisi.api.finalize()
paths = [fileinfo.path for fileinfo in files.list]
paths.sort()
-3
View File
@@ -34,8 +34,6 @@ def main():
if len(sys.argv) < 2:
usage("PiSi package required..")
pisi.api.init(database=False, options='')
try:
arc = ArchiveZip(sys.argv[1], 'zip', 'r')
except BadZipfile, e:
@@ -53,7 +51,6 @@ def main():
os.unlink(tar_file)
os.unlink(tar_file.rstrip('.lzma'))
pisi.api.finalize()
return 0
if __name__ == "__main__":
+1 -1
View File
@@ -93,7 +93,7 @@ setup(name="pisi",
author_email="pisi@pardus.org.tr",
url="http://www.pardus.org.tr/eng/pisi/",
package_dir = {'': ''},
packages = ['pisi', 'pisi.cli', 'pisi.actionsapi', 'pisi.pxml', 'pisi.scenarioapi', 'pisi.db'],
packages = ['pisi', 'pisi.cli', 'pisi.operations', 'pisi.actionsapi', 'pisi.pxml', 'pisi.scenarioapi', 'pisi.db'],
scripts = ['pisi-cli', 'scripts/lspisi', 'scripts/unpisi', 'scripts/check-newconfigs.py', 'scripts/revdep-rebuild'],
cmdclass = {'install' : Install}
)
+8 -6
View File
@@ -1,11 +1,13 @@
PISI test suite
---------------
PiSi UnitTests
##############
There are python unit tests and shell scripts in this directory.
Before running unittests you need to first go to repos directory and
create test repositories.
Run the python tests with
>>> pisi@pardus tests/repos# python createrepos.py
$ tests/run.py
Now you can return to tests folder and run tests.
>>> pisi@pardus tests # python runtests.py
Shell scripts use the CLIs to perform package operations.
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<A href="http://www.cs.bilkent.edu.tr/~erayo">
cıvık mantardır bu
<Name>Eray Ozkural</Name>
<Number>868</Number>
<Description xml:lang="en">Lazy Tin</Description>
<Description xml:lang="tr">Tembel Teneke</Description>
<Project>pisi</Project>
<Project>noatun</Project>
<Project>kdevelop</Project>
<OtherInfo>
<BirthDate>18071976</BirthDate>
<Interest>AI</Interest>
<CodesWith>
<Person>Baris</Person>
<Person>Gurer</Person>
<Person>Caglar</Person>
<Person>Meren</Person>
</CodesWith>
</OtherInfo>
</A>
-338
View File
@@ -1,338 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
import unittest
import zipfile
import shutil
import os
import pwd
import grp
import time
import testcase
class ActionsAPITestCase(testcase.TestCase):
def setUp(self):
from pisi.actionsapi.variables import initVariables
testcase.TestCase.setUp(self)
initVariables()
#FIXME: test incomplete
return
self.f = zipfile.ZipFile("helloworld-0.1-1.pisi", "r")
self.filelist = []
for file in self.f.namelist():
self.filelist.append(file)
def testFileList(self):
#FIXME: test incomplete
return
fileContent = ["files.xml", \
"install/bin/helloworld", \
"install/opt/PARDUS", \
"install/opt/helloworld/helloworld", \
"install/opt/uludag", \
"install/sbin/helloworld", \
"install/sys/PARDUS", \
"install/sys/uludag", \
"install/usr/bin/goodbye", \
"install/usr/bin/helloworld", \
"install/usr/lib/helloworld.o", \
"install/usr/sbin/goodbye", \
"install/usr/sbin/helloworld", \
"install/usr/share/doc/helloworld-0.1-1/Makefile.am", \
"install/usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp", \
"install/usr/share/info/Makefile.am", \
"install/usr/share/info/Makefile.cvs", \
"install/usr/share/info/Makefile.in", \
"install/var/goodbye", \
"install/var/hello", \
"metadata.xml"]
'''check number of files in package'''
self.assertEqual(fileContent.__len__(), self.filelist.__len__())
'''check file content'''
for file in self.filelist:
self.assert_(fileContent.__contains__(file))
def testShelltoolsCanAccessFile(self):
from pisi.actionsapi.shelltools import can_access_file
self.assert_(can_access_file('tests/actionsapitests/file'))
self.assert_(not can_access_file('tests/actionsapitests/fileX'))
self.assert_(can_access_file('tests/actionsapitests/linktoafile'))
def testShelltoolsCanAccessDir(self):
from pisi.actionsapi.shelltools import can_access_directory
self.assert_(can_access_directory('tests/actionsapitests/adirectory'))
self.assert_(not can_access_directory('tests/actionsapitests/adirectoryX'))
self.assert_(can_access_directory('tests/actionsapitests/linktoadirectory'))
def testShelltoolsMakedirs(self):
from pisi.actionsapi.shelltools import makedirs
makedirs('tests/actionsapitests/testdirectory/into/a/directory')
self.assertEqual(os.path.exists('tests/actionsapitests/testdirectory/into/a/directory'), True)
shutil.rmtree('tests/actionsapitests/testdirectory')
def testShelltoolsEcho(self):
from pisi.actionsapi.shelltools import echo
echo('tests/actionsapitests/echo-file', 'hububat fiyatları')
self.assertEqual(os.path.exists('tests/actionsapitests/echo-file'), True)
self.assertEqual(open('tests/actionsapitests/echo-file').readlines()[0].strip(), "hububat fiyatları")
echo('tests/actionsapitests/echo-file', 'fiyat hububatları')
self.assertEqual(open('tests/actionsapitests/echo-file').readlines()[1].strip(), "fiyat hububatları")
os.remove('tests/actionsapitests/echo-file')
def testShelltoolsChmod(self):
from pisi.actionsapi.shelltools import chmod
chmod('tests/actionsapitests/file')
self.assertEqual(oct(os.stat('tests/actionsapitests/file').st_mode)[-3:], '755')
chmod('tests/actionsapitests/file', 0644)
self.assertEqual(oct(os.stat('tests/actionsapitests/file').st_mode)[-3:], '644')
def testShelltoolsChown(self):
from pisi.actionsapi.shelltools import chown
f = open('tests/actionsapitests/chowntest', 'w')
f.close()
chown('tests/actionsapitests/chowntest')
self.assertEqual(os.stat('tests/actionsapitests/chowntest').st_uid, pwd.getpwnam('root')[2])
self.assertEqual(os.stat('tests/actionsapitests/chowntest').st_gid, grp.getgrnam('root')[2])
chown('tests/actionsapitests/chowntest', 'daemon', 'wheel')
self.assertEqual(os.stat('tests/actionsapitests/chowntest').st_uid, pwd.getpwnam('daemon')[2])
self.assertEqual(os.stat('tests/actionsapitests/chowntest').st_gid, grp.getgrnam('wheel')[2])
os.remove('tests/actionsapitests/chowntest')
def testShelltoolsSym(self):
from pisi.actionsapi.shelltools import sym
sym('tests/actionsapitests/file', 'tests/actionsapitests/filelnk')
self.assert_(os.path.islink('tests/actionsapitests/filelnk'))
self.assertEqual(os.readlink('tests/actionsapitests/filelnk'), 'tests/actionsapitests/file')
os.remove('tests/actionsapitests/filelnk')
def testShelltoolsUnlink(self):
from pisi.actionsapi.shelltools import unlink
f = open('tests/actionsapitests/unlinktest', 'w')
f.close()
os.symlink('tests/actionsapitests/unlinktest', 'tests/actionsapitests/unlinktest-sym')
unlink('tests/actionsapitests/unlinktest')
self.assert_(not os.path.exists('tests/actionsapitests/unlinktest'))
unlink('tests/actionsapitests/unlinktest-sym')
self.assert_(not os.path.exists('tests/actionsapitests/unlinktest-sym'))
def testShelltoolsUnlinkDir(self):
from pisi.actionsapi.shelltools import unlinkDir
os.mkdir('tests/actionsapitests/unlinkdir')
f = open('tests/actionsapitests/unlinkdir/unlinktest', 'w')
f.close()
#FIXME: unlinkDir cannot unlink a link to a directory.
#Instead it deletes content of linked directory, then leaves directory and link
#unlinkDir('tests/actionsapitests/linktoadirectory')
#self.assert_(not os.path.exists('tests/actionsapitests/linktoadirectory'))
#os.symlink('tests/actionsapitests/linkeddir', 'tests/actionsapitests/linktoadirectory')
unlinkDir('tests/actionsapitests/unlinkdir')
self.assert_(not os.path.exists('tests/actionsapitests/unlinkdir'))
def testShelltoolsMove(self):
from pisi.actionsapi.shelltools import move
move('tests/actionsapitests/brokenlink', 'tests/actionsapitests/brokenlink-move')
self.assert_(os.path.islink('tests/actionsapitests/brokenlink-move'))
self.assertEqual(os.readlink('tests/actionsapitests/brokenlink-move'), '/no/such/place')
self.assert_(not os.path.exists('tests/actionsapitests/brokenlink'))
shutil.move('tests/actionsapitests/brokenlink-move', 'tests/actionsapitests/brokenlink')
move('tests/actionsapitests/brokenlink', 'tests/actionsapitests/adirectory/brokenlink-move')
self.assert_(os.path.islink('tests/actionsapitests/adirectory/brokenlink-move'))
self.assertEqual(os.readlink('tests/actionsapitests/adirectory/brokenlink-move'), '/no/such/place')
self.assert_(not os.path.exists('tests/actionsapitests/brokenlink'))
shutil.move('tests/actionsapitests/adirectory/brokenlink-move', 'tests/actionsapitests/brokenlink')
move('tests/actionsapitests/file', 'tests/actionsapitests/adirectory')
self.assert_(os.path.isfile('tests/actionsapitests/adirectory/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/file'), 321)
self.assert_(not os.path.exists('tests/actionsapitests/file'))
shutil.move('tests/actionsapitests/adirectory/file', 'tests/actionsapitests/')
move('tests/actionsapitests/file', 'tests/actionsapitests/file-move')
self.assert_(os.path.isfile('tests/actionsapitests/file-move'))
self.assertEqual(os.path.getsize('tests/actionsapitests/file-move'), 321)
self.assert_(not os.path.exists('tests/actionsapitests/file'))
shutil.move('tests/actionsapitests/file-move', 'tests/actionsapitests/file')
move('tests/actionsapitests/file', 'tests/actionsapitests/adirectory/filewithanothername')
self.assert_(os.path.exists('tests/actionsapitests/adirectory/filewithanothername'))
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/filewithanothername'), 321)
self.assert_(not os.path.exists('tests/actionsapitests/file'))
shutil.move('tests/actionsapitests/adirectory/filewithanothername', 'tests/actionsapitests/file')
#FIXME: this type of moving (without changing name) doesn't work
#And I'm not sure the right way is to use 'tests/actionsapitests/adirectory/linkeddir' as dest.
#move('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory')
#self.assert_(os.path.exists('tests/actionsapitests/adirectory/linkeddir/file'))
#self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/linkeddir/file'), 321)
#shutil.move('tests/actionsapitests/adirectory/linkeddir', 'tests/actionsapitests/linkeddir')
move('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory/withanothername')
self.assert_(os.path.exists('tests/actionsapitests/adirectory/withanothername/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/withanothername/file'), 321)
shutil.move('tests/actionsapitests/adirectory/withanothername', 'tests/actionsapitests/linkeddir')
def testShelltoolsCopy(self):
from pisi.actionsapi.shelltools import copy
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/brokenlink-copy')
self.assertEqual(os.path.islink('tests/actionsapitests/brokenlink-copy'), True)
os.remove('tests/actionsapitests/brokenlink-copy')
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.islink('tests/actionsapitests/adirectory/brokenlink'), True)
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/adirectory/brknlnk')
self.assertEqual(os.path.islink('tests/actionsapitests/adirectory/brknlnk'), True)
os.remove('tests/actionsapitests/adirectory/brknlnk')
self.assertEqual(os.readlink('tests/actionsapitests/adirectory/brokenlink'), '/no/such/place')
os.remove('tests/actionsapitests/adirectory/brokenlink')
copy('tests/actionsapitests/linktoadirectory', 'tests/actionsapitests/adirectory/', False)
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/linktoadirectory/file'), True)
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/linktoadirectory/file'), 321)
shutil.rmtree('tests/actionsapitests/adirectory/linktoadirectory')
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.isfile('tests/actionsapitests/adirectory/file'), True)
#overwrite..
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory')
os.remove('tests/actionsapitests/adirectory/file')
copy('tests/actionsapitests/linktoafile', 'tests/actionsapitests/adirectory', False)
ourguy = 'tests/actionsapitests/%s' % os.readlink('tests/actionsapitests/linktoafile')
self.assert_(os.path.exists(ourguy))
copy('tests/actionsapitests/file', 'tests/actionsapitests/file-copy')
self.assertEqual(os.path.exists('tests/actionsapitests/file-copy'), True)
os.remove('tests/actionsapitests/file-copy')
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory/filewithanothername')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/filewithanothername'), True)
os.remove('tests/actionsapitests/adirectory/filewithanothername')
copy('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/linkeddir/file'), True)
shutil.rmtree('tests/actionsapitests/adirectory/linkeddir')
copy('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory/withanothername')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/withanothername/file'), True)
shutil.rmtree('tests/actionsapitests/adirectory/withanothername')
def testShelltoolsCopyTree(self):
from pisi.actionsapi.shelltools import copytree
copytree('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory')
self.assert_(os.path.exists('tests/actionsapitests/linkeddir/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/linkeddir/file'), 321)
self.assert_(os.path.exists('tests/actionsapitests/adirectory/linkeddir/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/linkeddir/file'), 321)
shutil.rmtree('tests/actionsapitests/adirectory/linkeddir')
copytree('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory/withanothername')
self.assert_(os.path.exists('tests/actionsapitests/linkeddir/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/linkeddir/file'), 321)
self.assert_(os.path.exists('tests/actionsapitests/adirectory/withanothername/file'))
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/withanothername/file'), 321)
shutil.rmtree('tests/actionsapitests/adirectory/withanothername')
def testShelltoolsTouch(self):
from pisi.actionsapi.shelltools import touch
touch('tests/actionsapitests/file-touch')
self.assert_(os.path.exists('tests/actionsapitests/file-touch'))
os.remove('tests/actionsapitests/file-touch')
atime = int(time.time())
touch('tests/actionsapitests/file')
self.assertEqual(os.path.getsize('tests/actionsapitests/file'), 321)
self.assert_(atime <= os.stat('tests/actionsapitests/file').st_atime)
def testShelltoolsCd(self):
from pisi.actionsapi.shelltools import cd
current = os.getcwd()
cd('tests/actionsapitests')
self.assertEqual(os.path.getsize('file'), 321)
cd()
self.assertEqual(os.path.getsize('actionsapitests/file'), 321)
os.chdir(current)
def testShelltoolsLs(self):
from pisi.actionsapi.shelltools import ls
self.assertEqual(os.listdir('tests/actionsapitests'), ls('tests/actionsapitests'))
self.assertEqual(os.listdir('tests/actionsapitests/linkeddir'), ls('tests/actionsapitests/linktoadirectory'))
self.assertEqual(['tests/actionsapitests/file'], ls('tests/actionsapitests/f*'))
def testShelltoolsExport(self):
from pisi.actionsapi.shelltools import export
export('hububat', 'fiyatları')
self.assertEqual(os.environ['hububat'], 'fiyatları')
del(os.environ['hububat'])
def testShelltoolsIsLink(self):
from pisi.actionsapi.shelltools import isLink
self.assert_(isLink('tests/actionsapitests/linktoadirectory'))
self.assert_(isLink('tests/actionsapitests/linktoafile'))
self.assert_(isLink('tests/actionsapitests/brokenlink'))
self.assert_(not isLink('tests/actionsapitests/file'))
self.assert_(not isLink('tests/actionsapitests/adirectory'))
def testShelltoolsIsFile(self):
from pisi.actionsapi.shelltools import isFile
self.assert_(not isFile('tests/actionsapitests/linktoadirectory'))
self.assert_(not isFile('tests/actionsapitests/linktoafile'))
self.assert_(not isFile('tests/actionsapitests/brokenlink'))
self.assert_(isFile('tests/actionsapitests/file'))
self.assert_(not isFile('tests/actionsapitests/adirectory'))
def testShelltoolsIsDirectory(self):
from pisi.actionsapi.shelltools import isDirectory
self.assert_(not isDirectory('tests/actionsapitests/linktoadirectory'))
self.assert_(not isDirectory('tests/actionsapitests/linktoafile'))
self.assert_(not isDirectory('tests/actionsapitests/brokenlink'))
self.assert_(not isDirectory('tests/actionsapitests/file'))
self.assert_(isDirectory('tests/actionsapitests/adirectory'))
def testShelltoolsSystem(self):
from pisi.actionsapi.shelltools import system as s
self.assertEqual(os.path.exists('tests/actionsapitests/systest'), False)
s('touch tests/actionsapitests/systest')
self.assertEqual(os.path.exists('tests/actionsapitests/systest'), True)
os.remove('tests/actionsapitests/systest')
suite = unittest.makeSuite(ActionsAPITestCase)
-1
View File
@@ -1 +0,0 @@
/no/such/place
-6
View File
@@ -1,6 +0,0 @@
toplam 4
drwxr-xr-x 2 meren users 4096 Mar 10 12:55 adirectory
lrwxrwxrwx 1 meren users 14 Mar 10 12:41 brokenlink -> /no/such/place
-rw-r--r-- 1 meren users 0 Mar 10 12:57 file
lrwxrwxrwx 1 meren users 4 Mar 10 12:42 linktoadirectory -> /etc
lrwxrwxrwx 1 meren users 11 Mar 10 12:42 linktoafile -> /etc/passwd
-6
View File
@@ -1,6 +0,0 @@
toplam 4
drwxr-xr-x 2 meren users 4096 Mar 10 12:55 adirectory
lrwxrwxrwx 1 meren users 14 Mar 10 12:41 brokenlink -> /no/such/place
-rw-r--r-- 1 meren users 0 Mar 10 12:57 file
lrwxrwxrwx 1 meren users 4 Mar 10 12:42 linktoadirectory -> /etc
lrwxrwxrwx 1 meren users 11 Mar 10 12:42 linktoafile -> /etc/passwd
-1
View File
@@ -1 +0,0 @@
linkeddir/
-1
View File
@@ -1 +0,0 @@
file
-114
View File
@@ -1,114 +0,0 @@
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import unittest
import os
from os.path import exists as pathexists
from os.path import basename, islink, join
import pisi.context as ctx
import pisi.api
from pisi import archive
from pisi import sourcearchive
from pisi import fetcher
from pisi import util
from pisi.specfile import SpecFile
from pisi import uri
import testcase
class ArchiveFileTestCase(testcase.TestCase):
def testUnpackTar(self):
spec = SpecFile("tests/popt/pspec.xml")
targetDir = '/tmp/pisitest'
achv = sourcearchive.SourceArchive(spec, targetDir)
assert spec.source.archive.type == "targz"
# skip fetching and directly unpack the previously fetched (by
# fetchertests) archive
if not achv.is_cached(interactive=False):
achv.fetch(interactive=False)
achv.unpack()
# but testing is hard
# "var/tmp/pisi/popt-1.7-3/work" (targetDir)
assert pathexists(targetDir + "/popt-1.7")
testfile = targetDir + "/popt-1.7/Makefile.am"
assert pathexists(testfile)
# check file integrity
self.assertEqual(util.sha1_file(testfile),
"5af9dd7d754f788cf511c57ce0af3d555fed009d")
def testUnpackZip(self):
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp/pisitest'
assert spec.source.archive.type == "zip"
achv = sourcearchive.SourceArchive(spec, targetDir)
achv.fetch(interactive=False)
achv.unpack(clean_dir=True)
assert pathexists(targetDir + "/pccts")
testfile = targetDir + "/pccts/history.txt"
assert pathexists(testfile)
# check file integrity
self.assertEqual(util.sha1_file(testfile),
"f2be0f9783e84e98fe4e2b8201a8f506fcc07a4d")
# TODO: no link file in pccts package. Need to find a ZIP file
# containing a symlink
# check for symbolic links
# testfile = targetDir + "/sandbox/testdir/link1"
# assert islink(testfile)
def testMakeZip(self):
# first unpack our dear sandbox.zip
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp/pisitest'
achv = sourcearchive.SourceArchive(spec, targetDir)
achv.fetch(interactive=False)
achv.unpack(clean_dir=True)
del achv
newZip = targetDir + "/new.zip"
zip = archive.ArchiveZip(newZip, 'zip', 'w')
sourceDir = targetDir + "/pccts"
zip.add_to_archive(sourceDir)
zip.close()
#TODO: do some more work to test the integrity of new zip file
def testUnpackZipCond(self):
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp'
achv = sourcearchive.SourceArchive(spec, targetDir)
url = uri.URI(spec.source.archive.uri)
filePath = join(ctx.config.archives_dir(), url.filename())
# check cached
if util.sha1_file(filePath) != spec.source.archive.sha1sum:
fetch = fetcher.Fetcher(spec.source.archive.uri, targetDir)
fetch.fetch()
assert spec.source.archive.type == "zip"
achv = archive.Archive(filePath, spec.source.archive.type)
achv.unpack_files(["pccts/history.txt"], targetDir)
assert pathexists(targetDir + "/pccts")
testfile = targetDir + "/pccts/history.txt"
assert pathexists(testfile)
suite = unittest.makeSuite(ArchiveFileTestCase)
-102
View File
@@ -1,102 +0,0 @@
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import unittest
import os
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import types
import pisi
import pisi.api
from pisi.pxml import xmlfile
from pisi.pxml import autoxml
import pisi.util as util
class AutoXmlTestCase(unittest.TestCase):
def setUp(self):
class OtherInfo:
__metaclass__ = autoxml.autoxml
t_BirthDate = [types.StringType, autoxml.mandatory]
t_Interest = [types.StringType, autoxml.optional]
t_CodesWith = [ [types.StringType], autoxml.optional, 'CodesWith/Person']
class A(xmlfile.XmlFile):
__metaclass__ = autoxml.autoxml
t_Name = [types.StringType, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_Number = [types.IntType, autoxml.optional]
t_Email = [types.StringType, autoxml.optional]
a_href = [types.StringType, autoxml.mandatory]
t_Projects = [ [types.StringType], autoxml.mandatory, 'Project']
t_OtherInfo = [ OtherInfo, autoxml.optional ]
s_Comment = [ autoxml.Text, autoxml.mandatory]
self.A = A
def testDeclaration(self):
self.assertEqual(len(self.A.decoders), 8) # how many fields in A?
self.assert_(hasattr(self.A, 'encode'))
def testReadWrite(self):
a = self.A()
# test initializer
self.assertEqual(a.href, None)
# test read
a.read('tests/a.xml')
self.assert_(a.href.startswith('http'))
self.assertEqual(a.number, 868)
self.assertEqual(a.name, 'Eray Ozkural')
self.assertEqual(len(a.projects), 3)
self.assertEqual(len(a.otherInfo.codesWith), 4)
self.assert_(not a.errors())
a.print_text(file('/tmp/a', 'w'))
la = file('/tmp/a').readlines()
self.assert_( util.any(lambda x:x.find('18071976')!=-1, la) )
a.write('/tmp/a.xml')
def testWriteRead(self):
a = self.A()
a.name = "Baris Metin"
a.email = "baris@uludag.org.tr"
a.description['tr'] = u'Melek, melek'
a.comment = u'Bu da zibidi aslinda ama caktirmiyor'
a.href = 'http://cekirdek.uludag.org.tr/~baris'
a.otherInfo.birthDate = '30101979'
a.projects = [ 'pisi', 'tasma', 'plasma' ]
errs = a.errors()
if errs:
self.fail( 'We got a bunch of errors: ' + str(errs))
a.write('/tmp/a2.xml')
a2 = self.A()
a2.read('/tmp/a2.xml')
self.assertEqual(a, a2)
class LocalTextTestCase(unittest.TestCase):
def setUp(self):
a = autoxml.LocalText()
a['tr'] = u'Zibidi'
a['en'] = u'ingiliz hiyarlari ne anlar zibididen'
self.a = a
def testStr(self):
s = str(self.a)
self.assert_(s!= None and len(s)>=6)
suite1 = unittest.makeSuite(AutoXmlTestCase)
suite2 = unittest.makeSuite(LocalTextTestCase)
suite = unittest.TestSuite((suite1, suite2))
-41
View File
@@ -1,41 +0,0 @@
#!/bin/sh
echo "beta functionality test script"
echo "working directory:" `pwd`
echo "cleaning destination dir: tmp"
PATH=$PATH:.
set -x # xtrace
set -e # errexit
rm -rf tmp
#echo "*** build tests"
pisi-cli -Dtmp build http://svn.uludag.org.tr/pardus/devel/system/base/zip/pspec.xml http://svn.uludag.org.tr/pardus/devel/system/base/unzip/pspec.xml
#partial-builds
pisi-cli -Dtmp build --until=setup http://svn.uludag.org.tr/pardus/devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build --until=build http://svn.uludag.org.tr/pardus/devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build --until=install http://svn.uludag.org.tr/pardus/devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build --until=package http://svn.uludag.org.tr/pardus/devel/system/base/hdparm/pspec.xml
#echo "*** repository tests"
pisi-cli -Dtmp index .
pisi-cli -Dtmp add-repo repo1 pisi-index.xml
pisi-cli -Dtmp update-repo repo1
pisi-cli -Dtmp list-repo
#echo "*** package ops"
pisi-cli -Dtmp info *.pisi
# pisi-cli list-available
pisi-cli -Dtmp install --ignore-comar zip
pisi-cli -Dtmp list-installed
pisi-cli -Dtmp remove --ignore-comar unzip
pisi-cli -Dtmp install --ignore-comar zip*.pisi
pisi-cli -Dtmp install --ignore-comar hdparm*.pisi flex*.pisi grep*.pisi
pisi-cli -Dtmp remove-repo repo1
# pisi-cli list-available
echo "*** database contents"
for x in `find tmp -iname '*.bdb'`; do
echo "contents of database " $x;
tools/cat-db.py $x;
done
-59
View File
@@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
import unittest
import shutil
import glob
import os
import testcase
from pisi.build import *
class BuildTestCase(testcase.TestCase):
def cleanOutput(self):
for x in glob.glob('tmp/a*.pisi'):
os.unlink(x)
def setUp(self):
options = pisi.config.Options()
options.ignore_build_no = False
options.output_dir = 'tmp'
self.cleanOutput()
testcase.TestCase.setUp(self, options = options)
pisi.context.config.values.build.buildno = True
def testBasicBuild(self):
shutil.copy('tests/buildtests/a/actions.py-1', 'tests/buildtests/a/actions.py')
pspec = 'tests/buildtests/a/pspec.xml'
pb = Builder(pspec)
pb.build()
self.assert_(os.path.exists('tmp/a-1.0-1-1.pisi'))
def testBuildNumber(self):
self.cleanOutput()
self.testBasicBuild()
pspec = 'tests/buildtests/a/pspec.xml'
shutil.copy('tests/buildtests/a/actions.py-2', 'tests/buildtests/a/actions.py')
pb = Builder(pspec)
pb.build()
self.assert_(os.path.exists('tmp/a-1.0-1-2.pisi'))
pb = Builder(pspec)
pb.build()
# because nothing is changed
self.assert_(not os.path.exists('tmp/a-1.0-1-3.pisi'))
os.remove('tests/buildtests/a/actions.py')
os.remove('tmp/a-1.0-1-2.pisi')
suite = unittest.makeSuite(BuildTestCase)
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2005 TUBITAK/UEKAE
# Licensed under the GNU General Public License, version 2.
# See the file http://www.gnu.org/copyleft/gpl.txt.
#
# A. Murat Eren <meren at uludag.org.tr>
from pisi.actionsapi import pisitools
WorkDir = "merhaba-pisi-1.0"
def install():
pisitools.dobin("merhaba-pisi.py")
pisitools.rename("/usr/bin/merhaba-pisi.py", "merhaba-pisi")
pisitools.dosym("./merhaba-pisi", "/usr/bin/justasysmlink")
pisitools.dosym("/thre/is/no/such/place", "/usr/bin/justabrokensymlink")
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2005 TUBITAK/UEKAE
# Licensed under the GNU General Public License, version 2.
# See the file http://www.gnu.org/copyleft/gpl.txt.
#
# A. Murat Eren <meren at uludag.org.tr>
from pisi.actionsapi import pisitools
WorkDir = "merhaba-pisi-1.0"
def install():
pisitools.dobin("merhaba-pisi.py")
pisitools.rename("/usr/bin/merhaba-pisi.py", "merhaba-pisi")
pisitools.dosym("./merhaba-pisi", "/usr/bin/justasysmlink")
pisitools.dosym("/thre/is/no/such", "/usr/bin/justabrokensymlink")
-39
View File
@@ -1,39 +0,0 @@
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.uludag.org.tr/projeler/pisi/pisi-spec.dtd">
<PISI>
<Source>
<Name>a</Name>
<Homepage>http://cekirdek.uludag.org.tr/~meren/merhaba-pisi.php</Homepage>
<Packager>
<Name>A. Murat Eren</Name>
<Email>meren@uludag.org.tr</Email>
</Packager>
<License>As-Is</License>
<PartOf>None</PartOf>
<IsA>app:console</IsA>
<Summary>PiSi Hello World Application..</Summary>
<Summary xml:lang="tr">PiSi için merhaba dünya uygulaması, süper bir konsol uygulaması</Summary>
<Description>Just a basic application. Nothing to describe.</Description>
<Description xml:lang="tr">Sadece basit bir uygulama, açıklayacak bir şey yok</Description>
<Archive sha1sum="fc917dec7b9729de935698f273f8007b2223653d" type="targz">http://cekirdek.uludag.org.tr/~meren/merhaba-pisi-1.0.tar.gz</Archive>
</Source>
<Package>
<Name>a</Name>
<Files>
<Path fileType="executable">/usr/bin/</Path>
</Files>
</Package>
<History>
<Update release="1">
<Date>2006-02-01</Date>
<Version>1.0</Version>
<Comment>First release.</Comment>
<Name>A. Murat Eren</Name>
<Email>meren@uludag.org.tr</Email>
</Update>
</History>
</PISI>
-61
View File
@@ -1,61 +0,0 @@
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import unittest
from pisi.configfile import ConfigurationFile
# NB: no need for pisi testcase in these things that do well without pisi init
class ConfigFileTestCase(unittest.TestCase):
def setUp(self):
self.cf = ConfigurationFile("tests/pisi.conf")
def testSections(self):
cf = self.cf
if not cf.general:
self.fail("No 'general' section found in ConfigurationFile")
if not cf.build:
self.fail("No 'build' section found in ConfigurationFile")
if not cf.dirs:
self.fail("No 'dirs' section found in ConfigurationFile")
def testValues(self):
cf = self.cf
# test values from pisi.conf file
self.assertEqual(cf.general.destinationdirectory, "/testing")
self.assertEqual(cf.dirs.archives_dir, "/disk2/pisi/archives")
# test default values
self.assertEqual(cf.dirs.tmp_dir, "/var/pisi")
def testAccessMethods(self):
cf = self.cf
self.assertEqual(cf.build.host, cf.build["host"])
self.assertEqual(cf.dirs.index_dir, cf.dirs["index_dir"])
def testFlagsExists(self):
cf = self.cf
#build
self.assert_(cf.build.cflags)
self.assert_(cf.build.cxxflags)
#general
self.assert_(cf.general.destinationdirectory)
#dirs
self.assert_(cf.dirs.index_dir)
self.assert_(cf.dirs.tmp_dir)
self.assert_(cf.dirs.packages_dir)
suite = unittest.makeSuite(ConfigFileTestCase)
-67
View File
@@ -1,67 +0,0 @@
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import unittest
import pisi.context as ctx
class ContextTestCase(unittest.TestCase):
def testConstness(self):
const = ctx.const
# test if we can get a const attribute?
try:
test = const.package_suffix
self.assertNotEqual(test, "")
except AttributeError:
self.fail("Couldn't get const attribute")
# test binding a new constant
const.test = "test binding"
# test re-binding (which is illegal)
try:
const.test = "test rebinding"
# we shouldn't reach here
self.fail("Rebinding a constant works. Something is wrong!")
except:
# we achived our goal with this error. infact, this is a
# ConstError but we can't catch it directly here
pass
# test unbinding (which is also illegal)
try:
del const.test
# we shouldn't reach here
self.fail("Unbinding a constant works. Something is wrong!")
except:
# we achived our goal with this error. infact, this is a
# ConstError but we can't catch it directly here
pass
def testConstValues(self):
const = ctx.const
constDict = {
"actions_file": "actions.py",
"setup_func": "setup",
"metadata_xml": "metadata.xml"
}
for k in constDict.keys():
if hasattr(const, k):
value = getattr(const, k)
self.assertEqual(value, constDict[k])
else:
self.fail("Constants does not have an attribute named %s" % k)
suite = unittest.makeSuite(ContextTestCase)
+1
View File
@@ -0,0 +1 @@
*.pyc
+11
View File
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
+83
View File
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import testcase
import pisi
class ComponentDBTestCase(testcase.TestCase):
componentdb = pisi.db.componentdb.ComponentDB()
def testHasComponent(self):
assert self.componentdb.has_component("system.base", "pardus-2007")
assert not self.componentdb.has_component("hede.hodo", "pardus-2007")
assert self.componentdb.has_component("applications.network", "contrib-2007")
assert not self.componentdb.has_component("hede.hodo", "contrib-2007")
assert self.componentdb.has_component("applications.network")
def testListComponents(self):
assert set(self.componentdb.list_components("pardus-2007")) == set(["system", "system.base",
"applications", "applications.network"])
assert set(self.componentdb.list_components("contrib-2007")) == set(["applications", "applications.util",
"applications.network"])
assert set(self.componentdb.list_components()) == set(["system", "system.base",
"applications", "applications.network",
"applications.util"])
def testGetComponent(self):
component = self.componentdb.get_component("applications.network")
assert component.name == "applications.network"
assert "ncftp" in component.packages
assert "lynx" not in component.packages
component = self.componentdb.get_component("applications.network", "contrib-2007")
assert component.name == "applications.network"
assert "lynx" in component.packages
assert "ncftp" not in component.packages
def testGetUnionComponent(self):
component = self.componentdb.get_union_component("applications.network")
assert component.name == "applications.network"
assert "lynx" in component.packages
assert "ncftp" in component.packages
def testGetPackages(self):
packages = self.componentdb.get_packages("applications.network")
assert "ncftp" in packages
assert "lynx" not in packages
packages = self.componentdb.get_packages("applications.network", "contrib-2007")
assert "lynx" in packages
assert "ncftp" not in packages
packages = self.componentdb.get_packages("applications", "contrib-2007", walk = True)
assert "cpulimit" and "lynx" in packages
assert "ncftp" not in packages
def testGetUnionPackages(self):
packages = self.componentdb.get_union_packages("applications.network")
assert "ncftp" in packages
assert "lynx" in packages
assert "cpulimit" not in packages
packages = self.componentdb.get_union_packages("applications", walk = True)
assert "ncftp" and "lynx" and "cpulimit" in packages
def testSearchComponent(self):
packages = self.componentdb.search_component(["applic"])
assert set(packages) == set(['applications', 'applications.network', 'applications.util'])
packages = self.componentdb.search_component(["system", "base"], repo="pardus-2007")
assert set(packages) == set(["system.base"])
packages = self.componentdb.search_component(["system", "base"], repo="contrib-2007")
assert not packages

Some files were not shown because too many files have changed in this diff Show More