Distinct copyrights were left as I do not wish to track down commit history to ensure it properly documents the copyright holders. Also left non-GPLv2 licenses and left bs_copy untouched as a mirror from OBS. Already have a mix of with and without headers and even OBS does not place on majority of files. If SUSE lawyers have an issue it will come up in legal review for Factory.
373 lines
14 KiB
Python
Executable File
373 lines
14 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
from pprint import pprint
|
|
import os
|
|
import sys
|
|
import re
|
|
import logging
|
|
import cmdln
|
|
|
|
from fnmatch import fnmatch
|
|
from ConfigParser import SafeConfigParser
|
|
import solv
|
|
import rpm
|
|
|
|
logger = None
|
|
|
|
REASONS = dict([(getattr(solv.Solver, i), i[14:]) for i in dir(solv.Solver) if i.startswith('SOLVER_REASON_')])
|
|
|
|
|
|
class DepTool(cmdln.Cmdln):
|
|
def __init__(self, *args, **kwargs):
|
|
cmdln.Cmdln.__init__(self, args, kwargs)
|
|
|
|
def get_optparser(self):
|
|
parser = cmdln.CmdlnOptionParser(self)
|
|
parser.add_option("--dry", action="store_true", help="dry run")
|
|
parser.add_option("--debug", action="store_true", help="debug output")
|
|
parser.add_option("--verbose", action="store_true", help="verbose")
|
|
parser.add_option("--system", action="store_true", help="with system repo")
|
|
parser.add_option("--arch", dest="arch", help="architecture", default='x86_64')
|
|
return parser
|
|
|
|
def postoptparse(self):
|
|
level = None
|
|
if self.options.debug:
|
|
level = logging.DEBUG
|
|
elif self.options.verbose:
|
|
level = logging.INFO
|
|
|
|
logging.basicConfig(level=level)
|
|
|
|
global logger
|
|
logger = logging.getLogger()
|
|
|
|
def prepare_pool(self, repos):
|
|
|
|
self.pool = solv.Pool()
|
|
self.pool.setarch(self.options.arch)
|
|
|
|
self._read_repos(repos)
|
|
|
|
if self.options.system:
|
|
self._add_system_repo()
|
|
|
|
self.pool.addfileprovides()
|
|
self.pool.createwhatprovides()
|
|
|
|
def _read_repos(self, repos):
|
|
repodir = '/etc/zypp/repos.d'
|
|
solvfile = '/var/cache/zypp/solv/%s/solv'
|
|
|
|
parser = SafeConfigParser()
|
|
|
|
if not repos:
|
|
repos = [f for f in os.listdir(repodir) if fnmatch(f, '*.repo')]
|
|
|
|
for r in repos:
|
|
if '/' in r or r.endswith('.solv'):
|
|
name = os.path.basename(os.path.splitext(r)[0])
|
|
repo = self.pool.add_repo(name)
|
|
repo.add_solv(r)
|
|
logger.debug("add repo %s" % name)
|
|
else:
|
|
try:
|
|
if r.endswith('.repo'):
|
|
name = os.path.splitext(r)[0]
|
|
else:
|
|
name = r
|
|
r += '.repo'
|
|
parser.read('/'.join((repodir, r)))
|
|
if parser.get(name, 'enabled') == '1':
|
|
repo = self.pool.add_repo(name)
|
|
repo.add_solv(solvfile % name)
|
|
if parser.has_option(name, 'priority'):
|
|
repo.priority = parser.getint(name, 'priority')
|
|
logger.debug("add repo %s" % name)
|
|
except Exception, e:
|
|
logger.error(e)
|
|
|
|
def _add_system_repo(self):
|
|
solvfile = '/var/cache/zypp/solv/@System/solv'
|
|
repo = self.pool.add_repo('system')
|
|
repo.add_solv(solvfile)
|
|
|
|
@cmdln.option("-s", "--single", action="store_true",
|
|
help="single step all requires/recommends")
|
|
@cmdln.option("--size", action="store_true",
|
|
help="print installed size")
|
|
@cmdln.option("-l", "--lock", dest="lock", action="append",
|
|
help="packages to lock")
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
@cmdln.option("--explain", dest="explain", action="append",
|
|
help="rule to explain")
|
|
@cmdln.option("--solver-debug", action="store_true",
|
|
help="debug solver")
|
|
@cmdln.option("--ignore-recommended", action="store_true",
|
|
help="ignore recommended")
|
|
def do_install(self, subcmd, opts, *args):
|
|
"""${cmd_name}: generate pot file for patterns
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
locked = []
|
|
if opts.lock:
|
|
for l in opts.lock:
|
|
for i in l.split(','):
|
|
locked.append(i)
|
|
|
|
good = True
|
|
|
|
self.prepare_pool(opts.repo)
|
|
if opts.solver_debug:
|
|
self.pool.set_debuglevel(3)
|
|
|
|
def solveit(packages):
|
|
jobs = []
|
|
for l in locked:
|
|
sel = self.pool.select(str(l), solv.Selection.SELECTION_NAME)
|
|
if sel.isempty():
|
|
# if we can't find it, it probably is not as important
|
|
logger.debug('locked package {} not found'.format(l))
|
|
else:
|
|
jobs += sel.jobs(solv.Job.SOLVER_LOCK)
|
|
|
|
for n in packages:
|
|
sel = self.pool.select(str(n), solv.Selection.SELECTION_NAME)
|
|
if sel.isempty():
|
|
logger.error('package {} not found'.format(n))
|
|
jobs += sel.jobs(solv.Job.SOLVER_INSTALL)
|
|
|
|
solver = self.pool.Solver()
|
|
|
|
if opts.ignore_recommended:
|
|
solver.set_flag(solver.SOLVER_FLAG_IGNORE_RECOMMENDED, 1)
|
|
|
|
problems = solver.solve(jobs)
|
|
if problems:
|
|
for problem in problems:
|
|
logger.error('%s', problem)
|
|
return False
|
|
|
|
trans = solver.transaction()
|
|
if trans.isempty():
|
|
logger.error('nothing to do')
|
|
return False
|
|
|
|
for s in trans.newsolvables():
|
|
print ','.join(packages), s.name
|
|
if opts.explain and s.name in opts.explain:
|
|
reason, rule = solver.describe_decision(s)
|
|
ruleinfo = None
|
|
if rule:
|
|
ruleinfo = rule.info().problemstr()
|
|
if reason == solv.Solver.SOLVER_REASON_WEAKDEP:
|
|
for v in solver.describe_weakdep_decision(s):
|
|
reason2, s2, dep = v
|
|
print("-> %s %s %s" % (s2.name, REASONS[reason2], dep))
|
|
else:
|
|
print("-> %s %s %s" % (s.name, REASONS[reason], ruleinfo))
|
|
|
|
if opts.size:
|
|
size = trans.calc_installsizechange()
|
|
print("SIZE %s" % (size))
|
|
|
|
return True
|
|
|
|
if opts.single:
|
|
for n in args:
|
|
sel = self.pool.select(str(n), solv.Selection.SELECTION_NAME)
|
|
for s in sel.solvables():
|
|
deps = s.lookup_deparray(solv.SOLVABLE_RECOMMENDS)
|
|
deps += s.lookup_deparray(solv.SOLVABLE_REQUIRES)
|
|
for dep in deps:
|
|
# only add recommends that exist as packages
|
|
rec = self.pool.select(dep.str(), solv.Selection.SELECTION_NAME)
|
|
if not rec.isempty():
|
|
if not solveit([dep.str()]):
|
|
good = False
|
|
else:
|
|
if not solveit(args):
|
|
good = False
|
|
|
|
if not good:
|
|
logger.error("solver errors encountered")
|
|
return 1
|
|
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_deps(self, subcmd, opts, *packages):
|
|
"""${cmd_name}: show package deps
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
for n in packages:
|
|
sel = self.pool.select(n, solv.Selection.SELECTION_NAME)
|
|
if sel.isempty():
|
|
logger.error("%s not found", n)
|
|
for s in sel.solvables():
|
|
print('- {}-{}@{}:'.format(s.name, s.evr, s.arch))
|
|
for kind in ('RECOMMENDS', 'REQUIRES', 'SUPPLEMENTS', 'ENHANCES', 'PROVIDES', 'SUGGESTS'):
|
|
deps = s.lookup_deparray(getattr(solv, 'SOLVABLE_'+kind), 0)
|
|
if deps:
|
|
print(' {}:'.format(kind))
|
|
for dep in deps:
|
|
print(' - {}'.format(dep))
|
|
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_whatprovides(self, subcmd, opts, *relation):
|
|
"""${cmd_name}: list packages providing given relations
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
for r in relation:
|
|
i = self.pool.str2id(r)
|
|
for s in self.pool.whatprovides(i):
|
|
print('- {}-{}@{}:'.format(s.name, s.evr, s.arch))
|
|
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_patterns(self, subcmd, opts, *relation):
|
|
"""${cmd_name}: list patterns
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
patternid = self.pool.str2id('pattern()')
|
|
for s in self.pool.whatprovides(patternid):
|
|
deps = s.lookup_deparray(solv.SOLVABLE_PROVIDES)
|
|
order = 0
|
|
for dep in deps:
|
|
name = str(dep)
|
|
if name.startswith('pattern-order()'):
|
|
# XXX: no function in bindings to do that properly
|
|
order = name[name.find('= ')+2:]
|
|
print("{} {}".format(order, s.name))
|
|
|
|
@cmdln.option("--providers", action="store_true",
|
|
help="also show other providers")
|
|
@cmdln.option("--relation", action="store_true",
|
|
help="arguments are relations rather than package names")
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_rdeps(self, subcmd, opts, *args):
|
|
"""${cmd_name}: list packages that require, recommend etc the given packages
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
kinds = ['RECOMMENDS', 'REQUIRES', 'SUPPLEMENTS', 'ENHANCES', 'SUGGESTS']
|
|
if opts.providers:
|
|
kinds.append('PROVIDES')
|
|
|
|
for kind in kinds:
|
|
kindid = getattr(solv, 'SOLVABLE_'+kind, 0)
|
|
kindprinted = False
|
|
if opts.relation:
|
|
# FIXME: doesnt work
|
|
for r in args:
|
|
sel = self.pool.matchdeps(r, solv.Selection.SELECTION_REL | solv.Selection.SELECTION_FLAT, kindid)
|
|
if sel.isempty():
|
|
logger.info('nothing %s %s', kind.lower(), r)
|
|
continue
|
|
for s in sel.solvables():
|
|
print(' {}: {}-{}@{}'.format(r, s.name, s.evr, s.arch))
|
|
else:
|
|
for n in args:
|
|
sel = self.pool.select(n, solv.Selection.SELECTION_NAME)
|
|
if sel.isempty():
|
|
logger.error("%s not found", n)
|
|
continue
|
|
for s in sel.solvables():
|
|
prov = s.lookup_deparray(solv.SOLVABLE_PROVIDES, 0)
|
|
if not prov:
|
|
logger.error("%s doesn't provide anything")
|
|
continue
|
|
for p in prov:
|
|
sel = self.pool.matchdepid(p, solv.Selection.SELECTION_REL | solv.Selection.SELECTION_FLAT, kindid)
|
|
if sel.isempty():
|
|
logger.debug('nothing %s %s', kind.lower(), p)
|
|
continue
|
|
for r in sel.solvables():
|
|
if kindid == solv.SOLVABLE_PROVIDES and r == s:
|
|
continue
|
|
if not kindprinted:
|
|
print kind
|
|
kindprinted = True
|
|
print(' {}: {}-{}@{}'.format(p, r.name, r.evr, r.arch))
|
|
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_what(self, subcmd, opts, *relation):
|
|
"""${cmd_name}: list packages that have dependencies on given relation
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
kinds = ['PROVIDES', 'RECOMMENDS', 'REQUIRES', 'SUPPLEMENTS', 'ENHANCES', 'SUGGESTS']
|
|
|
|
for r in relation:
|
|
p = self.pool.str2id(r)
|
|
for kind in kinds:
|
|
kindprinted = False
|
|
kindid = getattr(solv, 'SOLVABLE_'+kind, 0)
|
|
sel = self.pool.matchdepid(p, solv.Selection.SELECTION_REL | solv.Selection.SELECTION_FLAT, kindid)
|
|
if sel.isempty():
|
|
logger.debug('nothing %s %s', kind.lower(), p)
|
|
continue
|
|
for r in sel.solvables():
|
|
if not kindprinted:
|
|
print kind
|
|
kindprinted = True
|
|
print(' {}-{}@{}'.format(r.name, r.evr, r.arch))
|
|
|
|
@cmdln.option("-r", "--repo", dest="repo", action="append",
|
|
help="repo to use")
|
|
def do_info(self, subcmd, opts, *args):
|
|
"""${cmd_name}: show some info about a package
|
|
|
|
${cmd_usage}
|
|
${cmd_option_list}
|
|
"""
|
|
|
|
self.prepare_pool(opts.repo)
|
|
|
|
sattrs = [s for s in dir(solv) if s.startswith("SOLVABLE_")]
|
|
for n in args:
|
|
sel = self.pool.select(str(n), solv.Selection.SELECTION_NAME)
|
|
for s in sel.solvables():
|
|
for attr in sattrs:
|
|
sid = getattr(solv, attr, 0)
|
|
# pretty stupid, just lookup strings
|
|
value = s.lookup_str(sid)
|
|
if value:
|
|
print('{}: {}'.format(attr[len('SOLVABLE_'):], value))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = DepTool()
|
|
sys.exit(app.main())
|
|
else:
|
|
logger = logging.getLogger()
|