Merge remote-tracking branch 'coolo/create_unsorted'

This commit is contained in:
Ludwig Nussel 2017-11-09 15:03:03 +01:00
commit 48a572ed59

View File

@ -45,7 +45,7 @@ from osclib.memoize import CACHEDIR
logger = logging.getLogger()
ARCHITECTURES = ('x86_64', 'ppc64le', 's390x', 'aarch64')
ARCHITECTURES = ['x86_64', 'ppc64le', 's390x', 'aarch64']
DEFAULT_REPOS = ("openSUSE:Factory/standard")
class Group(object):
@ -62,6 +62,7 @@ class Group(object):
self.solved = False
self.not_found = dict()
self.unresolvable = dict()
self.unwanted = set()
for a in self.architectures:
self.packages[a] = []
self.unresolvable[a] = dict()
@ -69,6 +70,8 @@ class Group(object):
self.srcpkgs = None
self.develpkgs = []
self.silents = set()
self.ignored = set()
self.recommended = set()
pkglist.groups[self.safe_name] = self
@ -91,13 +94,18 @@ class Group(object):
continue
name = package.keys()[0]
for rel in package[name]:
arch = None
if rel == 'locked':
self.locked.add(name)
continue
elif rel == 'silent':
self._add_to_packages(name)
self.silents.add(name)
elif rel == 'recommended':
self.recommended.add(name)
else:
self._add_to_packages(name, rel)
arch = rel
self._add_to_packages(name, arch)
def _verify_solved(self):
if not self.solved:
@ -109,6 +117,7 @@ class Group(object):
self.locked.update(group.locked)
self.silents.update(group.silents)
self.recommended.update(group.recommended)
# do not repeat packages
def ignore(self, without):
@ -123,6 +132,9 @@ class Group(object):
self.not_found[p] -= without.not_found[p]
if not self.not_found[p]:
self.not_found.pop(p)
for g in without.ignored:
self.ignore(g)
self.ignored.add(without)
def solve(self, ignore_recommended=False):
""" base: list of base groups or None """
@ -140,7 +152,9 @@ class Group(object):
pool = self.pkglist._prepare_pool(arch)
# pool.set_debuglevel(10)
for n, group in self.packages[arch]:
tosolv = self.packages[arch]
while tosolv:
n, group = tosolv.pop(0)
jobs = list(self.pkglist.lockjobs[arch])
sel = pool.select(str(n), solv.Selection.SELECTION_NAME)
if sel.isempty():
@ -148,12 +162,22 @@ class Group(object):
self.not_found.setdefault(n, set()).add(arch)
continue
else:
if n in self.recommended:
for s in sel.solvables():
for dep in s.lookup_deparray(solv.SOLVABLE_RECOMMENDS):
# only add recommends that exist as packages
rec = pool.select(dep.str(), solv.Selection.SELECTION_NAME)
if not rec.isempty():
tosolv.append([dep.str(), group + ":recommended:" + n])
jobs += sel.jobs(solv.Job.SOLVER_INSTALL)
for l in self.locked:
locked = self.locked | self.pkglist.unwanted
for l in locked:
sel = pool.select(str(l), solv.Selection.SELECTION_NAME)
if sel.isempty():
logger.warn('{}.{}: locked package {} not found'.format(self.name, arch, l))
# if we can't find it, it probably is not as important
logger.debug('{}.{}: locked package {} not found'.format(self.name, arch, l))
else:
jobs += sel.jobs(solv.Job.SOLVER_LOCK)
@ -171,7 +195,11 @@ class Group(object):
problems = solver.solve(jobs)
if problems:
for problem in problems:
logger.error('unresolvable: %s.%s: %s', self.name, arch, problem)
msg = 'unresolvable: %s.%s: %s', self.name, arch, problem
if self.pkglist.ignore_broken:
logger.debug(msg)
else:
logger.debug(msg)
self.unresolvable[arch][n] = str(problem)
continue
@ -182,6 +210,8 @@ class Group(object):
if 'get_recommended' in dir(solver):
for s in solver.get_recommended():
if s.name in locked:
continue
self.recommends.setdefault(s.name, group + ':' + n)
for s in solver.get_suggested():
self.recommends.setdefault(s.name, group + ':' + n)
@ -220,6 +250,23 @@ class Group(object):
self.solved_packages = solved
self.solved = True
def check_dups(self, modules):
packages = set(self.solved_packages['*'])
for arch in ARCHITECTURES:
packages.update(self.solved_packages[arch])
for m in modules:
# do not check with ourselves and only once for the rest
if m.name <= self.name: continue
if self.name in m.conflicts or m.name in self.conflicts:
continue
mp = set(m.solved_packages['*'])
for arch in ARCHITECTURES:
mp.update(m.solved_packages[arch])
if len(packages & mp):
print 'overlap_between_' + self.name + '_and_' + m.name + ':'
for p in sorted(packages & mp):
print ' - ' + p
def collect_devel_packages(self, modules):
develpkgs = set()
for arch in self.architectures:
@ -255,7 +302,6 @@ class Group(object):
for m in modules:
for arch in ['*'] + self.architectures:
already_present = already_present or (p in m.solved_packages[arch])
already_present = already_present or (p in m.recommends)
if not already_present:
self.recommends[p] = recommends[p]
@ -292,12 +338,12 @@ class Group(object):
name = msg
if name in unresolvable:
msg = ' {} uninstallable: {}'.format(name, unresolvable[name])
logger.error(msg)
if ignore_broken:
c = ET.Comment(msg)
packagelist.append(c)
continue
else:
logger.error(msg)
name = msg
status = self.pkglist.supportstatus(name)
attrs = { 'name': name }
@ -377,23 +423,30 @@ class PkgListGen(ToolBase.ToolBase):
def _load_group_file(self, fn):
output = None
unwanted = None
with open(fn, 'r') as fh:
logger.debug("reading %s", fn)
for groupname, group in yaml.safe_load(fh).items():
if groupname == 'OUTPUT':
output = group
continue
if groupname == 'UNWANTED':
unwanted = set(group)
continue
g = Group(groupname, self)
g.parse_yml(group)
return output
return output, unwanted
def load_all_groups(self):
output = None
unwanted = set()
for fn in glob.glob(os.path.join(self.input_dir, 'group*.yml')):
o = self._load_group_file(fn)
o, u = self._load_group_file(fn)
if not output:
output = o
return output
if not unwanted:
unwanted = u
return output, unwanted
def _write_all_groups(self):
self._check_supplements()
@ -481,41 +534,34 @@ class PkgListGen(ToolBase.ToolBase):
return pool
def _collect_unsorted_packages(self):
return
def _collect_unsorted_packages(self, modules):
packages = dict()
for arch in self.architectures:
pool = self._prepare_pool(arch)
sel = pool.Selection()
p = set([s.name for s in
pool.solvables_iter() if not
(s.name.endswith('-debuginfo') or
(s.name.endswith('-32bit') or
s.name.endswith('-debuginfo') or
s.name.endswith('-debugsource'))])
for g in self.groups.values():
if g.solved:
for a in ('*', arch):
p -= g.solved_packages[a]
packages[arch] = p
common = None
# compute common packages across all architectures
for arch in packages.keys():
if common is None:
common = set(packages[arch])
continue
common &= packages[arch]
# reduce arch specific set by common ones
for arch in packages.keys():
packages[arch] -= common
packages['*'] = common
g = Group('unsorted', self)
g.solved_packages = packages
g.solved = True
p -= self.unwanted
for g in modules:
for a in ('*', arch):
p -= set(g.solved_packages[a].keys())
for package in p:
packages.setdefault(package, []).append(arch)
with open(os.path.join(self.output_dir, 'unsorted.yml'), 'w') as fh:
fh.write("unsorted:\n")
for p in sorted(packages.keys()):
fh.write(" - ")
fh.write(p)
if len(packages[p]) != len(ARCHITECTURES):
fh.write(": [")
fh.write(','.join(sorted(packages[p])))
fh.write("]")
fh.write(" \n")
class CommandLineInterface(ToolBase.CommandLineInterface):
@ -597,6 +643,7 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
# only there to parse the repos
bs_mirrorfull = os.path.join(os.path.dirname(__file__), 'bs_mirrorfull')
global_update = False
for prp in self.tool.repos:
project, repo = prp.split('/')
for arch in self.tool.architectures:
@ -608,8 +655,18 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
apiurl = 'https://api.opensuse.org/public'
else:
apiurl = 'https://api.suse.de/public'
subprocess.call(
[bs_mirrorfull, '--nodebug', '{}/build/{}/{}/{}'.format(apiurl, project, repo, arch), d])
args = [bs_mirrorfull]
args.append('--nodebug')
args.append('{}/build/{}/{}/{}'.format(apiurl, project, repo, arch))
args.append(d)
p = subprocess.Popen(args, stdout=subprocess.PIPE)
repo_update = False
for line in p.stdout:
print(line.rstrip())
global_update = True
repo_update = True
if not repo_update:
continue
files = [os.path.join(d, f)
for f in os.listdir(d) if f.endswith('.rpm')]
fh = open(d + '.solv', 'w')
@ -618,6 +675,7 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
p.communicate('\0'.join(files))
p.wait()
fh.close()
return global_update
@cmdln.option('--ignore-unresolvable', action='store_true', help='ignore unresolvable and missing packges')
@ -629,7 +687,7 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
${cmd_option_list}
"""
output = self.tool.load_all_groups()
output, self.tool.unwanted = self.tool.load_all_groups()
if not output:
return
@ -647,13 +705,16 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
includes = settings.get('includes', [])
excludes = settings.get('excludes', [])
self.tool.solve_module(groupname, includes, excludes)
modules.append(self.tool.groups[groupname])
g = self.tool.groups[groupname]
g.conflicts = settings.get('conflicts', [])
modules.append(g)
for module in modules:
module.check_dups(modules)
module.collect_devel_packages(modules)
module.filter_duplicated_recommends(modules)
self.tool._collect_unsorted_packages()
self.tool._collect_unsorted_packages(modules)
self.tool._write_all_groups()