Merge branch 'pisi-db'
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
+38
-5
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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'))
|
||||
@@ -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)
|
||||
@@ -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'))
|
||||
@@ -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)
|
||||
@@ -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'))
|
||||
@@ -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)))
|
||||
@@ -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)))
|
||||
@@ -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)))
|
||||
@@ -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'))
|
||||
@@ -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()
|
||||
@@ -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)))
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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'))
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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'))
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
|
||||
+108
-161
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"))
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
@@ -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 +0,0 @@
|
||||
/no/such/place
|
||||
@@ -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,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 +0,0 @@
|
||||
linkeddir/
|
||||
@@ -1 +0,0 @@
|
||||
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)
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
@@ -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.
|
||||
#
|
||||
@@ -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
Reference in New Issue
Block a user