inst-builder: Replace RelationBuilder with a set_relations method

Signed-off-by: Niels Thykier <niels@thykier.net>
ubuntu/rebased
Niels Thykier 6 years ago
parent ab6b2ef953
commit 0446ead9e8

@ -14,7 +14,6 @@
import apt_pkg import apt_pkg
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager
from itertools import product from itertools import product
from britney2.utils import ifilter_except, iter_except, get_dependency_solvers from britney2.utils import ifilter_except, iter_except, get_dependency_solvers
@ -42,29 +41,20 @@ def build_installability_tester(suite_info, archs):
possible_dep_ranges = {} possible_dep_ranges = {}
# We do not differentiate between depends and pre-depends # We do not differentiate between depends and pre-depends
if pkgdata.depends:
depends.extend(apt_pkg.parse_depends(pkgdata.depends, False))
if pkgdata.conflicts: if pkgdata.conflicts:
conflicts = apt_pkg.parse_depends(pkgdata.conflicts, False) conflicts_parsed = apt_pkg.parse_depends(pkgdata.conflicts, False)
with builder.relation_builder(pkg_id) as relations:
# Breaks/Conflicts are so simple that we do not need to keep align the relation # Breaks/Conflicts are so simple that we do not need to keep align the relation
# with the suite. This enables us to do a few optimizations. # with the suite. This enables us to do a few optimizations.
if conflicts: for dep_suite in suite_info:
rels = [] dep_binaries_s_a, dep_provides_s_a = dep_suite.binaries[arch]
for dep_suite in suite_info: for block in (relation for relation in conflicts_parsed):
dep_binaries_s_a, dep_provides_s_a = dep_suite.binaries[arch] # if a package satisfies its own conflicts relation, then it is using §7.6.2
for block in (relation for relation in conflicts): conflicts.extend(s.pkg_id for s in solvers(block, dep_binaries_s_a, dep_provides_s_a)
# if a package satisfies its own conflicts relation, then it is using §7.6.2 if s.pkg_id != pkg_id)
rels.extend(s.pkg_id for s in solvers(block, dep_binaries_s_a, dep_provides_s_a)
if s.pkg_id != pkg_id) if pkgdata.depends:
if rels: for block in apt_pkg.parse_depends(pkgdata.depends, False):
relations.add_breaks(rels)
dep_relations = []
for block in depends:
sat = set() sat = set()
for dep_suite in suite_info: for dep_suite in suite_info:
@ -72,7 +62,7 @@ def build_installability_tester(suite_info, archs):
sat.update(s.pkg_id for s in solvers(block, dep_binaries_s_a, dep_provides_s_a)) sat.update(s.pkg_id for s in solvers(block, dep_binaries_s_a, dep_provides_s_a))
if len(block) != 1: if len(block) != 1:
dep_relations.append(sat) depends.append(sat)
else: else:
# This dependency might be a part # This dependency might be a part
# of a version-range a la: # of a version-range a la:
@ -100,81 +90,13 @@ def build_installability_tester(suite_info, archs):
possible_dep_ranges[key] = sat possible_dep_ranges[key] = sat
if possible_dep_ranges: if possible_dep_ranges:
dep_relations.extend(possible_dep_ranges.values()) depends.extend(possible_dep_ranges.values())
relations.add_dependency_clauses(dep_relations) builder.set_relations(pkg_id, depends, conflicts)
return builder.build() return builder.build()
class _RelationBuilder(object):
"""Private helper class to "build" relations"""
def __init__(self, itbuilder, binary):
self._itbuilder = itbuilder
self._binary = binary
binary_data = itbuilder._package_table[binary]
self._new_deps = set(binary_data[0])
self._new_breaks = set(binary_data[1])
def add_dependency_clauses(self, or_clauses):
"""Add a dependency clauses
Each clause must be a sequence BinaryPackageIDs. The clause
is an OR clause, i.e. any BinaryPackageID in the
sequence can satisfy the relation. It is irrelevant if the
dependency is from the "Depends" or the "Pre-Depends" field.
Note that is the sequence is empty, the dependency is assumed
to be unsatisfiable.
The binaries in the clause are not required to have been added
to the InstallabilityTesterBuilder when this method is called.
However, they must be added before the "build()" method is
called.
"""
itbuilder = self._itbuilder
binary = self._binary
interned_or_clauses = [itbuilder._intern_set(c) for c in or_clauses]
okay = True
for or_clause in interned_or_clauses:
if not or_clause:
okay = False
for dep_tuple in or_clause:
rdeps, _, rdep_relations = itbuilder._reverse_relations(dep_tuple)
rdeps.add(binary)
rdep_relations.add(or_clause)
self._new_deps.update(interned_or_clauses)
if not okay:
itbuilder._broken.add(binary)
def add_breaks(self, breaks_relations):
"""Add a Breaks/Conflict-clauses
Marks the given binary as being broken by any of the packages.
That is, the given package satisfies a relation
in either the "Breaks" or the "Conflicts" field for any of the
listed packages.
:param breaks_relations: An list/set of BinaryPackageIDs that has a Breaks/Conflicts relation
on the current package
:return: None
"""
itbuilder = self._itbuilder
self._new_breaks.update(breaks_relations)
this_package = self._binary
for broken_binary in breaks_relations:
reverse_relations = itbuilder._reverse_relations(broken_binary)
reverse_relations[1].add(this_package)
def _commit(self):
itbuilder = self._itbuilder
data = (itbuilder._intern_set(self._new_deps),
itbuilder._intern_set(self._new_breaks))
itbuilder._package_table[self._binary] = data
class InstallabilityTesterBuilder(object): class InstallabilityTesterBuilder(object):
"""Builder to create instances of InstallabilityTester""" """Builder to create instances of InstallabilityTester"""
@ -185,7 +107,7 @@ class InstallabilityTesterBuilder(object):
self._testing = set() self._testing = set()
self._internmap = {} self._internmap = {}
self._broken = set() self._broken = set()
self._empty_set = self._intern_set(frozenset())
def add_binary(self, binary, essential=False, in_testing=False, def add_binary(self, binary, essential=False, in_testing=False,
frozenset=frozenset): frozenset=frozenset):
@ -226,31 +148,43 @@ class InstallabilityTesterBuilder(object):
return True return True
return False return False
def set_relations(self, pkg_id, dependency_clauses, breaks):
"""The dependency and breaks realtions for a given package
@contextmanager :param pkg_id: BinaryPackageID determining which package will have its relations set
def relation_builder(self, binary): :param dependency_clauses: A list/set of OR clauses (i.e. CNF with each element in
"""Returns a _RelationBuilder for a given binary [context] dependency_clauses being a disjunction). Each OR cause (disjunction) should be a
set/list of BinaryPackageIDs that satisfy that relation.
This method returns a context-managed _RelationBuilder for a :param breaks: An list/set of BinaryPackageIDs that has a Breaks/Conflicts relation
given binary. So it should be used in a "with"-statment, on the current package. Can be None
like: :return: No return value
with it.relation_builder(binary) as rel:
rel.add_dependency_clause(dependency_clause)
rel.add_breaks(pkgtuple)
...
The binary given must be a (name, version, architecture)-tuple.
Note, this method is optimised to be called at most once per
binary.
""" """
if binary not in self._package_table: # pragma: no cover if dependency_clauses is not None:
raise ValueError("Binary %s/%s/%s does not exist" % binary) interned_or_clauses = self._intern_set(self._intern_set(c) for c in dependency_clauses)
rel = _RelationBuilder(self, binary) satisfiable = True
yield rel for or_clause in interned_or_clauses:
rel._commit() if not or_clause:
satisfiable = False
for dep_tuple in or_clause:
rdeps, _, rdep_relations = self._reverse_relations(dep_tuple)
rdeps.add(pkg_id)
rdep_relations.add(or_clause)
if not satisfiable:
self._broken.add(pkg_id)
else:
interned_or_clauses = self._empty_set
if breaks is not None:
# Breaks
breaks_relations = self._intern_set(breaks)
for broken_binary in breaks_relations:
reverse_relations = self._reverse_relations(broken_binary)
reverse_relations[1].add(pkg_id)
else:
breaks_relations = self._empty_set
self._package_table[pkg_id] = (interned_or_clauses, breaks_relations)
def _intern_set(self, s, frozenset=frozenset): def _intern_set(self, s, frozenset=frozenset):
"""Freeze and intern a given sequence (set variant of intern()) """Freeze and intern a given sequence (set variant of intern())
@ -273,7 +207,6 @@ class InstallabilityTesterBuilder(object):
self._internmap[fset] = fset self._internmap[fset] = fset
return fset return fset
def _reverse_relations(self, binary, set=set): def _reverse_relations(self, binary, set=set):
"""Return the reverse relations for a binary """Return the reverse relations for a binary
@ -312,7 +245,6 @@ class InstallabilityTesterBuilder(object):
return False return False
return True return True
# Merge reverse conflicts with conflicts - this saves some # Merge reverse conflicts with conflicts - this saves some
# operations in _check_loop since we only have to check one # operations in _check_loop since we only have to check one
# set (instead of two) and we remove a few duplicates here # set (instead of two) and we remove a few duplicates here
@ -410,7 +342,6 @@ class InstallabilityTesterBuilder(object):
self._essentials, safe_set, self._essentials, safe_set,
eqv_table) eqv_table)
def _build_eqv_packages_table(self, package_table, def _build_eqv_packages_table(self, package_table,
reverse_package_table, reverse_package_table,
frozenset=frozenset): frozenset=frozenset):

@ -131,9 +131,8 @@ class UniverseBuilder(object):
essential=pkg_builder._is_essential, essential=pkg_builder._is_essential,
in_testing=pkg_builder._in_testing, in_testing=pkg_builder._in_testing,
) )
with builder.relation_builder(pkg_id) as rel:
rel.add_dependency_clauses(pkg_builder._dependencies) builder.set_relations(pkg_id, pkg_builder._dependencies, pkg_builder._conflicts)
rel.add_breaks(pkg_builder._conflicts)
return builder.build() return builder.build()
def pkg_id(self, pkgish): def pkg_id(self, pkgish):

Loading…
Cancel
Save