Rewrite the main run to use the solver and allow combined migrations

Signed-off-by: Niels Thykier <niels@thykier.net>
This commit is contained in:
Niels Thykier 2012-05-11 16:31:50 +02:00
parent 65f74c226d
commit 326cc0d98b

View File

@ -2233,7 +2233,7 @@ class Britney(object):
if len(actions) == 1: if len(actions) == 1:
item = actions[0] item = actions[0]
# apply the changes # apply the changes
affected, undo = self.doop_source(item, lundo) affected, undo = self.doop_source(item, hint_undo=lundo)
undo_list = [(undo, item)] undo_list = [(undo, item)]
if item.architecture == 'source': if item.architecture == 'source':
affected_architectures = set(self.options.architectures) affected_architectures = set(self.options.architectures)
@ -2256,6 +2256,7 @@ class Britney(object):
for item in actions: for item in actions:
item_affected, undo = self.doop_source(item, item_affected, undo = self.doop_source(item,
hint_undo=lundo,
removals=removals) removals=removals)
affected.update(item_affected) affected.update(item_affected)
undo_list.append((undo, item)) undo_list.append((undo, item))
@ -2301,82 +2302,66 @@ class Britney(object):
final result is successful, otherwise (None, None). final result is successful, otherwise (None, None).
""" """
extra = [] extra = []
deferred = [] groups = set()
skipped = [] for y in sorted((y for y in packages), key=attrgetter('uvname')):
mark_passed = False updates, rms, _ = self._compute_groups(y.package, y.suite, y.architecture, y.is_removal)
position = len(packages) groups.add((y, frozenset(updates), frozenset(rms)))
if selected is None:
selected = []
if nuninst: if nuninst:
nuninst_comp = nuninst nuninst_orig = nuninst
else: else:
nuninst_comp = self.nuninst_orig nuninst_orig = self.nuninst_orig
nuninst_last_accepted = nuninst_orig
# local copies for better performance maybe_reschuled = []
dependencies = self.dependencies worklist = self._inst_tester.solve_groups(groups)
worklist.reverse()
self.output_write("recur: [] %s %d/%d\n" % (",".join(x.uvname for x in selected), len(packages), len(extra))) self.output_write("recur: [] %s %d/%d\n" % (",".join(x.uvname for x in selected), len(packages), len(extra)))
# loop on the packages (or better, actions) while worklist:
while packages: comp = worklist.pop()
item = packages.pop(0) comp_name = ' '.join(item.uvname for item in comp)
self.output_write("trying: %s\n" % comp_name)
# this is the marker for the first loop accepted, nuninst_after, comp_undo, failed_arch = self.try_migration(comp, nuninst_last_accepted, lundo)
if not mark_passed and position < 0: if accepted:
mark_passed = True nuninst_last_accepted = nuninst_after
packages.extend(deferred) selected.extend(comp)
del deferred
else: position -= 1
# defer packages if their dependency has been already skipped
if not mark_passed:
defer = False
for p in dependencies.get(item, []):
if p in skipped:
deferred.append(item)
skipped.append(item)
defer = True
break
if defer: continue
self.output_write("trying: %s\n" % (item.uvname))
(better, nuninst, undo_list, arch) = self.try_migration([item], nuninst_comp, lundo=lundo)
# check if the action improved the uninstallability counters
if better:
if lundo is not None: if lundo is not None:
lundo.extend(undo_list) lundo.extend(comp_undo)
selected.append(item) self.output_write("accepted: %s\n" % comp_name)
packages.extend(extra) self.output_write(" ori: %s\n" % (self.eval_nuninst(nuninst_orig)))
extra = [] self.output_write(" pre: %s\n" % (self.eval_nuninst(nuninst_last_accepted)))
self.output_write("accepted: %s\n" % (item.uvname)) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_after)))
self.output_write(" ori: %s\n" % (self.eval_nuninst(self.nuninst_orig)))
self.output_write(" pre: %s\n" % (self.eval_nuninst(nuninst_comp)))
self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst, nuninst_comp)))
if len(selected) <= 20: if len(selected) <= 20:
self.output_write(" all: %s\n" % (" ".join( x.uvname for x in selected ))) self.output_write(" all: %s\n" % (" ".join(x.uvname for x in selected)))
else: else:
self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join(x.uvname for x in selected[-20:]))) self.output_write(" most: (%d) .. %s\n" % (len(selected), " ".join(x.uvname for x in selected[-20:])))
nuninst_comp = nuninst
else: else:
broken = sorted(b for b in nuninst_after[failed_arch]
if b not in nuninst_last_accepted[failed_arch])
compare_nuninst = None
if any(item for item in comp if item.architecture != 'source'):
compare_nuninst = nuninst_last_accepted
# NB: try_migration already reverted this for us, so just print the results and move on # NB: try_migration already reverted this for us, so just print the results and move on
self.output_write("skipped: %s (%d <- %d)\n" % (item.uvname, len(extra), len(packages))) self.output_write("skipped: %s (%d, %d)\n" % (comp_name, len(maybe_reschuled), len(worklist)))
self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst, item.architecture != 'source' and nuninst_comp or None))) self.output_write(" got: %s\n" % (self.eval_nuninst(nuninst_after, compare_nuninst)))
self.output_write(" * %s: %s\n" % (arch, ", ".join(sorted(b for b in nuninst[arch] if b not in nuninst_comp[arch])))) self.output_write(" * %s: %s\n" % (failed_arch, ", ".join(broken)))
extra.append(item)
if not mark_passed:
skipped.append(item)
if len(comp) > 1:
self.output_write(" - splitting the component into single items and retrying them\n")
worklist.extend([item] for item in comp)
else:
extra.extend(comp)
self.output_write(" finish: [%s]\n" % ",".join( x.uvname for x in selected )) self.output_write(" finish: [%s]\n" % ",".join( x.uvname for x in selected ))
self.output_write("endloop: %s\n" % (self.eval_nuninst(self.nuninst_orig))) self.output_write("endloop: %s\n" % (self.eval_nuninst(self.nuninst_orig)))
self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_comp))) self.output_write(" now: %s\n" % (self.eval_nuninst(nuninst_last_accepted)))
self.output_write(eval_uninst(self.options.architectures, self.output_write(eval_uninst(self.options.architectures,
newly_uninst(self.nuninst_orig, nuninst_comp))) newly_uninst(self.nuninst_orig, nuninst_last_accepted)))
self.output_write("\n") self.output_write("\n")
return (nuninst_comp, extra) return (nuninst_last_accepted, extra)
def do_all(self, hinttype=None, init=None, actions=None): def do_all(self, hinttype=None, init=None, actions=None):
@ -2807,7 +2792,6 @@ class Britney(object):
self.upgrade_me = [ make_migrationitem(x, self.sources) for x in upgrade_me ] self.upgrade_me = [ make_migrationitem(x, self.sources) for x in upgrade_me ]
self.upgrade_me.extend(make_migrationitem(x, self.sources) for x in removals) self.upgrade_me.extend(make_migrationitem(x, self.sources) for x in removals)
def auto_hinter(self): def auto_hinter(self):
"""Auto-generate "easy" hints. """Auto-generate "easy" hints.
@ -2824,7 +2808,7 @@ class Britney(object):
excuses relationships. If they build a circular dependency, which we already excuses relationships. If they build a circular dependency, which we already
know as not-working with the standard do_all algorithm, try to `easy` them. know as not-working with the standard do_all algorithm, try to `easy` them.
""" """
self.__log("> Processing hints from the auto hinter [Partial-ordering]", self.__log("> Processing hints from the auto hinter",
type="I") type="I")
# consider only excuses which are valid candidates # consider only excuses which are valid candidates
@ -2833,28 +2817,6 @@ class Britney(object):
excuses_deps = dict((name, set(excuse.deps)) for name, excuse in excuses.items()) excuses_deps = dict((name, set(excuse.deps)) for name, excuse in excuses.items())
sources_t = self.sources['testing'] sources_t = self.sources['testing']
groups = set()
for y in sorted((y for y in self.upgrade_me if y.uvname in excuses), key=attrgetter('uvname')):
if y.is_removal and y.package not in sources_t:
# Already removed
continue
if not y.is_removal:
excuse = excuses[y.uvname]
if y.architecture == 'source' and y.uvname in sources_t and sources_t[y.uvname][VERSION] == excuse.ver[1]:
# Already migrated
continue
adds, rms, _ = self._compute_groups(y.package, y.suite,
y.architecture, y.is_removal,
include_hijacked=True)
groups.add((y, frozenset(adds), frozenset(rms)))
for comp in self._inst_tester.solve_groups(groups):
if len(comp) > 1:
self.do_hint("easy", "autohinter", comp)
self.__log("> Processing hints from the auto hinter [Original]",
type="I")
def find_related(e, hint, circular_first=False): def find_related(e, hint, circular_first=False):
if e not in excuses: if e not in excuses:
return False return False