mirror of
https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
synced 2025-04-02 13:51:12 +00:00
inst-builder: Replace RelationBuilder with a set_relations method
Signed-off-by: Niels Thykier <niels@thykier.net>
This commit is contained in:
parent
ab6b2ef953
commit
0446ead9e8
@ -14,7 +14,6 @@
|
||||
|
||||
import apt_pkg
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from itertools import product
|
||||
|
||||
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 = {}
|
||||
|
||||
# We do not differentiate between depends and pre-depends
|
||||
if pkgdata.depends:
|
||||
depends.extend(apt_pkg.parse_depends(pkgdata.depends, False))
|
||||
|
||||
if pkgdata.conflicts:
|
||||
conflicts = apt_pkg.parse_depends(pkgdata.conflicts, False)
|
||||
|
||||
with builder.relation_builder(pkg_id) as relations:
|
||||
|
||||
conflicts_parsed = apt_pkg.parse_depends(pkgdata.conflicts, False)
|
||||
# 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.
|
||||
if conflicts:
|
||||
rels = []
|
||||
for dep_suite in suite_info:
|
||||
dep_binaries_s_a, dep_provides_s_a = dep_suite.binaries[arch]
|
||||
for block in (relation for relation in conflicts):
|
||||
# if a package satisfies its own conflicts relation, then it is using §7.6.2
|
||||
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 rels:
|
||||
relations.add_breaks(rels)
|
||||
for dep_suite in suite_info:
|
||||
dep_binaries_s_a, dep_provides_s_a = dep_suite.binaries[arch]
|
||||
for block in (relation for relation in conflicts_parsed):
|
||||
# if a package satisfies its own conflicts relation, then it is using §7.6.2
|
||||
conflicts.extend(s.pkg_id for s in solvers(block, dep_binaries_s_a, dep_provides_s_a)
|
||||
if s.pkg_id != pkg_id)
|
||||
|
||||
dep_relations = []
|
||||
for block in depends:
|
||||
if pkgdata.depends:
|
||||
for block in apt_pkg.parse_depends(pkgdata.depends, False):
|
||||
sat = set()
|
||||
|
||||
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))
|
||||
|
||||
if len(block) != 1:
|
||||
dep_relations.append(sat)
|
||||
depends.append(sat)
|
||||
else:
|
||||
# This dependency might be a part
|
||||
# of a version-range a la:
|
||||
@ -100,81 +90,13 @@ def build_installability_tester(suite_info, archs):
|
||||
possible_dep_ranges[key] = sat
|
||||
|
||||
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()
|
||||
|
||||
|
||||
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):
|
||||
"""Builder to create instances of InstallabilityTester"""
|
||||
|
||||
@ -185,7 +107,7 @@ class InstallabilityTesterBuilder(object):
|
||||
self._testing = set()
|
||||
self._internmap = {}
|
||||
self._broken = set()
|
||||
|
||||
self._empty_set = self._intern_set(frozenset())
|
||||
|
||||
def add_binary(self, binary, essential=False, in_testing=False,
|
||||
frozenset=frozenset):
|
||||
@ -226,31 +148,43 @@ class InstallabilityTesterBuilder(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_relations(self, pkg_id, dependency_clauses, breaks):
|
||||
"""The dependency and breaks realtions for a given package
|
||||
|
||||
@contextmanager
|
||||
def relation_builder(self, binary):
|
||||
"""Returns a _RelationBuilder for a given binary [context]
|
||||
|
||||
This method returns a context-managed _RelationBuilder for a
|
||||
given binary. So it should be used in a "with"-statment,
|
||||
like:
|
||||
|
||||
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.
|
||||
:param pkg_id: BinaryPackageID determining which package will have its relations set
|
||||
:param dependency_clauses: A list/set of OR clauses (i.e. CNF with each element in
|
||||
dependency_clauses being a disjunction). Each OR cause (disjunction) should be a
|
||||
set/list of BinaryPackageIDs that satisfy that relation.
|
||||
:param breaks: An list/set of BinaryPackageIDs that has a Breaks/Conflicts relation
|
||||
on the current package. Can be None
|
||||
:return: No return value
|
||||
"""
|
||||
if binary not in self._package_table: # pragma: no cover
|
||||
raise ValueError("Binary %s/%s/%s does not exist" % binary)
|
||||
rel = _RelationBuilder(self, binary)
|
||||
yield rel
|
||||
rel._commit()
|
||||
if dependency_clauses is not None:
|
||||
interned_or_clauses = self._intern_set(self._intern_set(c) for c in dependency_clauses)
|
||||
satisfiable = True
|
||||
for or_clause in interned_or_clauses:
|
||||
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):
|
||||
"""Freeze and intern a given sequence (set variant of intern())
|
||||
@ -273,7 +207,6 @@ class InstallabilityTesterBuilder(object):
|
||||
self._internmap[fset] = fset
|
||||
return fset
|
||||
|
||||
|
||||
def _reverse_relations(self, binary, set=set):
|
||||
"""Return the reverse relations for a binary
|
||||
|
||||
@ -312,7 +245,6 @@ class InstallabilityTesterBuilder(object):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Merge reverse conflicts with conflicts - this saves some
|
||||
# operations in _check_loop since we only have to check one
|
||||
# set (instead of two) and we remove a few duplicates here
|
||||
@ -410,7 +342,6 @@ class InstallabilityTesterBuilder(object):
|
||||
self._essentials, safe_set,
|
||||
eqv_table)
|
||||
|
||||
|
||||
def _build_eqv_packages_table(self, package_table,
|
||||
reverse_package_table,
|
||||
frozenset=frozenset):
|
||||
|
@ -131,9 +131,8 @@ class UniverseBuilder(object):
|
||||
essential=pkg_builder._is_essential,
|
||||
in_testing=pkg_builder._in_testing,
|
||||
)
|
||||
with builder.relation_builder(pkg_id) as rel:
|
||||
rel.add_dependency_clauses(pkg_builder._dependencies)
|
||||
rel.add_breaks(pkg_builder._conflicts)
|
||||
|
||||
builder.set_relations(pkg_id, pkg_builder._dependencies, pkg_builder._conflicts)
|
||||
return builder.build()
|
||||
|
||||
def pkg_id(self, pkgish):
|
||||
|
Loading…
x
Reference in New Issue
Block a user