diff --git a/britney.py b/britney.py index c7f65e6..c454d9f 100755 --- a/britney.py +++ b/britney.py @@ -193,7 +193,7 @@ from urllib.parse import quote import apt_pkg -from britney2 import SourcePackage, BinaryPackageId, BinaryPackage +from britney2 import SourcePackage, BinaryPackageId, BinaryPackage, DependencyType from britney2.excuse import Excuse from britney2.hints import HintParser from britney2.inputs.suiteloader import DebMirrorLikeSuiteContentLoader, MissingRequiredConfigurationError @@ -806,9 +806,9 @@ class Britney(object): sources_s = source_suite.sources for p in packages: if p in sources_t and sources_t[p].version == sources_s[p].version: - excuse.add_dep("%s/%s" % (p, arch), arch) + excuse.add_dependency(DependencyType.DEPENDS, "%s/%s" % (p, arch), arch) else: - excuse.add_dep(p, arch) + excuse.add_dependency(DependencyType.DEPENDS, p, arch) else: for p in packages: excuse.add_break_dep(p, arch) @@ -1410,34 +1410,35 @@ class Britney(object): # parts[0] == package name # parts[1] == optional architecture parts = e.name.split('/') - for d in e.deps: - ok = False - # source -> source dependency; both packages must have - # valid excuses - if d in upgrade_me or d in unconsidered: - ok = True - # if the excuse is for a binNMU, also consider d/$arch as a - # valid excuse - elif len(parts) == 2: - bd = '%s/%s' % (d, parts[1]) - if bd in upgrade_me or bd in unconsidered: + for d in e.all_deps: + for deptype in e.all_deps[d]: + ok = False + # source -> source dependency; both packages must have + # valid excuses + if d in upgrade_me or d in unconsidered: ok = True - # if the excuse is for a source package, check each of the - # architectures on which the excuse lists a dependency on d, - # and consider the excuse valid if it is possible on each - # architecture - else: - arch_ok = True - for arch in e.deps[d]: - bd = '%s/%s' % (d, arch) - if bd not in upgrade_me and bd not in unconsidered: - arch_ok = False - break - if arch_ok: - ok = True - if not ok: - e.addhtml("Impossible dependency: %s -> %s" % (e.name, d)) - e.addreason("depends") + # if the excuse is for a binNMU, also consider d/$arch as a + # valid excuse + elif len(parts) == 2: + bd = '%s/%s' % (d, parts[1]) + if bd in upgrade_me or bd in unconsidered: + ok = True + # if the excuse is for a source package, check each of the + # architectures on which the excuse lists a dependency on d, + # and consider the excuse valid if it is possible on each + # architecture + else: + arch_ok = True + for arch in e.all_deps[d][deptype]: + bd = '%s/%s' % (d, arch) + if bd not in upgrade_me and bd not in unconsidered: + arch_ok = False + break + if arch_ok: + ok = True + if not ok: + e.addhtml("Impossible %s: %s -> %s" % (deptype, e.name, d)) + e.addreason(deptype.get_reason()) invalidate_excuses(excuses, upgrade_me, unconsidered) # sort the list of candidates @@ -2048,7 +2049,7 @@ class Britney(object): # consider only excuses which are valid candidates and still relevant. valid_excuses = frozenset(y.uvname for y in upgrade_me if y not in sources_t or sources_t[y].version != excuses[y].ver[1]) - excuses_deps = {name: valid_excuses.intersection(excuse.deps) + excuses_deps = {name: valid_excuses.intersection(excuse.get_deps()) for name, excuse in excuses.items() if name in valid_excuses} excuses_rdeps = defaultdict(set) for name, deps in excuses_deps.items(): @@ -2059,7 +2060,7 @@ class Britney(object): excuse = excuses[e] if not circular_first: hint[e] = excuse.ver[1] - if not excuse.deps: + if not excuse.get_deps(): return hint for p in excuses_deps[e]: if p in hint or p not in valid_excuses: @@ -2074,7 +2075,7 @@ class Britney(object): seen_hints = set() for e in valid_excuses: excuse = excuses[e] - if excuse.deps: + if excuse.get_deps(): hint = find_related(e, {}, True) if isinstance(hint, dict) and e in hint: h = frozenset(hint.items()) diff --git a/britney2/__init__.py b/britney2/__init__.py index 5e68ce0..46fbef1 100644 --- a/britney2/__init__.py +++ b/britney2/__init__.py @@ -1,6 +1,21 @@ from collections import namedtuple from enum import Enum, unique +class DependencyType(Enum): + DEPENDS = ('Depends', 'depends', 'dependency') + # BUILD_DEPENDS includes BUILD_DEPENDS_ARCH + BUILD_DEPENDS = ('Build-Depends(-Arch)', 'build-depends', 'build-dependency') + BUILD_DEPENDS_INDEP = ('Build-Depends-Indep', 'build-depends-indep', 'build-dependency (indep)') + + def __str__(self): + return self.value[0] + + def get_reason(self): + return self.value[1] + + def get_description(self): + return self.value[2] + @unique class SuiteClass(Enum): diff --git a/britney2/excuse.py b/britney2/excuse.py index 5487870..1617e3e 100644 --- a/britney2/excuse.py +++ b/britney2/excuse.py @@ -17,6 +17,7 @@ from collections import defaultdict import re +from britney2 import DependencyType from britney2.policies.policy import PolicyVerdict VERDICT2DESC = { @@ -74,11 +75,8 @@ class Excuse(object): self.forced = False self._policy_verdict = PolicyVerdict.REJECTED_PERMANENTLY - self.invalid_deps = set() - self.invalid_build_deps = set() - self.deps = {} - self.arch_build_deps = {} - self.indep_build_deps = {} + self.all_invalid_deps = set() + self.all_deps = {} self.sane_deps = [] self.break_deps = [] self.unsatisfiable_on_archs = [] @@ -133,11 +131,24 @@ class Excuse(object): """Set the section of the package""" self.section = section - def add_dep(self, name, arch): - """Add a dependency""" - if name not in self.deps: - self.deps[name]=[] - self.deps[name].append(arch) + def add_dependency(self, deptype, name, arch): + """Add a dependency of type deptype """ + if name not in self.all_deps: + self.all_deps[name]={} + if deptype not in self.all_deps[name]: + self.all_deps[name][deptype]=[] + self.all_deps[name][deptype].append(arch) + + def get_deps(self): + # the autohinter uses the excuses data to query dependencies between + # excuses. For now, we keep the current behaviour by just returning + # the data that was in the old deps set + """ Get the dependencies of type DEPENDS """ + deps = set() + for dep in self.all_deps: + if DependencyType.DEPENDS in self.all_deps[dep]: + deps.add(dep) + return deps def add_sane_dep(self, name): """Add a sane dependency""" @@ -153,27 +164,13 @@ class Excuse(object): if arch not in self.unsatisfiable_on_archs: self.unsatisfiable_on_archs.append(arch) - def add_arch_build_dep(self, name, arch): - if name not in self.arch_build_deps: - self.arch_build_deps[name] = [] - self.arch_build_deps[name].append(arch) - - def add_indep_build_dep(self, name, arch): - if name not in self.indep_build_deps: - self.indep_build_deps[name] = [] - self.indep_build_deps[name].append(arch) - def add_unsatisfiable_dep(self, signature, arch): """Add an unsatisfiable dependency""" self.unsat_deps[arch].add(signature) - def invalidate_dep(self, name): + def invalidate_dependency(self, name): """Invalidate dependency""" - self.invalid_deps.add(name) - - def invalidate_build_dep(self, name): - """Invalidate build-dependency""" - self.invalid_build_deps.add(name) + self.all_invalid_deps.add(name) def setdaysold(self, daysold, mindays): """Set the number of days from the upload and the minimum number of days for the update""" @@ -213,18 +210,23 @@ class Excuse(object): return VERDICT2DESC[verdict] return "UNKNOWN: Missing description for {0} - Please file a bug against Britney".format(verdict.name) - def _render_dep_issue(self, dep_issues, invalid_deps, field): + def _render_dep_issues(self, dep_issues, invalid_deps): lastdep = "" res = [] for x in sorted(dep_issues, key=lambda x: x.split('/')[0]): dep = x.split('/')[0] - if dep == lastdep: - continue + if dep != lastdep: + seen = {} lastdep = dep - if x in invalid_deps: - res.append("