Merge branch 'master' into autopkgtest

ubuntu/rebased
Paul Gevers 7 years ago
commit fd03083ea5
No known key found for this signature in database
GPG Key ID: 9C5C99EB05BD750A

@ -197,7 +197,8 @@ from britney2.excuse import Excuse
from britney2.hints import HintParser from britney2.hints import HintParser
from britney2.installability.builder import build_installability_tester from britney2.installability.builder import build_installability_tester
from britney2.migrationitem import MigrationItem from britney2.migrationitem import MigrationItem
from britney2.policies.policy import AgePolicy, RCBugPolicy, PiupartsPolicy, PolicyVerdict from britney2.policies import PolicyVerdict
from britney2.policies.policy import AgePolicy, RCBugPolicy, PiupartsPolicy, BuildDependsPolicy
from britney2.policies.autopkgtest import AutopkgtestPolicy from britney2.policies.autopkgtest import AutopkgtestPolicy
from britney2.utils import (old_libraries_format, undo_changes, from britney2.utils import (old_libraries_format, undo_changes,
compute_reverse_tree, possibly_compressed, compute_reverse_tree, possibly_compressed,
@ -523,6 +524,7 @@ class Britney(object):
if getattr(self.options, 'adt_enable') == 'yes': if getattr(self.options, 'adt_enable') == 'yes':
self.policies.append(AutopkgtestPolicy(self.options, self.suite_info)) self.policies.append(AutopkgtestPolicy(self.options, self.suite_info))
self.policies.append(AgePolicy(self.options, self.suite_info, MINDAYS)) self.policies.append(AgePolicy(self.options, self.suite_info, MINDAYS))
self.policies.append(BuildDependsPolicy(self.options, self.suite_info))
for policy in self.policies: for policy in self.policies:
policy.register_hints(self._hint_parser) policy.register_hints(self._hint_parser)
@ -580,6 +582,7 @@ class Britney(object):
[], [],
None, None,
True, True,
None,
[], [],
[], [],
) )
@ -656,6 +659,7 @@ class Britney(object):
[], [],
None, None,
True, True,
None,
[], [],
[], [],
) )
@ -859,7 +863,7 @@ class Britney(object):
srcdist[source].binaries.append(pkg_id) srcdist[source].binaries.append(pkg_id)
# if the source package doesn't exist, create a fake one # if the source package doesn't exist, create a fake one
else: else:
srcdist[source] = SourcePackage(source_version, 'faux', [pkg_id], None, True, [], []) srcdist[source] = SourcePackage(source_version, 'faux', [pkg_id], None, True, None, [], [])
# add the resulting dictionary to the package list # add the resulting dictionary to the package list
packages[pkg] = dpkg packages[pkg] = dpkg
@ -1384,21 +1388,28 @@ class Britney(object):
# at this point, we check the status of the builds on all the supported architectures # at this point, we check the status of the builds on all the supported architectures
# to catch the out-of-date ones # to catch the out-of-date ones
pkgs = {src: ["source"]}
all_binaries = self.all_binaries all_binaries = self.all_binaries
for arch in self.options.architectures: archs_to_consider = list(self.options.architectures)
archs_to_consider.append('all')
for arch in archs_to_consider:
oodbins = {} oodbins = {}
uptodatebins = False uptodatebins = False
# for every binary package produced by this source in the suite for this architecture # for every binary package produced by this source in the suite for this architecture
for pkg_id in sorted(x for x in source_u.binaries if x.architecture == arch): if arch == 'all':
consider_binaries = source_u.binaries
else:
consider_binaries = sorted(x for x in source_u.binaries if x.architecture == arch)
for pkg_id in consider_binaries:
pkg = pkg_id.package_name pkg = pkg_id.package_name
if pkg not in pkgs: pkgs[pkg] = []
pkgs[pkg].append(arch)
# retrieve the binary package and its source version # retrieve the binary package and its source version
binary_u = all_binaries[pkg_id] binary_u = all_binaries[pkg_id]
pkgsv = binary_u.source_version pkgsv = binary_u.source_version
# arch:all packages are treated separately from arch:arch
if binary_u.architecture != arch:
continue
# if it wasn't built by the same source, it is out-of-date # if it wasn't built by the same source, it is out-of-date
# if there is at least one binary on this arch which is # if there is at least one binary on this arch which is
# up-to-date, there is a build on this arch # up-to-date, there is a build on this arch
@ -1409,9 +1420,6 @@ class Britney(object):
excuse.add_old_binary(pkg, pkgsv) excuse.add_old_binary(pkg, pkgsv)
continue continue
else: else:
# if the binary is arch all, it doesn't count as
# up-to-date for this arch
if binary_u.architecture == arch:
uptodatebins = True uptodatebins = True
# if the package is architecture-dependent or the current arch is `nobreakall' # if the package is architecture-dependent or the current arch is `nobreakall'
@ -1549,15 +1557,15 @@ class Britney(object):
# this list will contain the packages which are valid candidates; # this list will contain the packages which are valid candidates;
# if a package is going to be removed, it will have a "-" prefix # if a package is going to be removed, it will have a "-" prefix
upgrade_me = [] upgrade_me = set()
upgrade_me_append = upgrade_me.append # Every . in a loop slows it down upgrade_me_add = upgrade_me.add # Every . in a loop slows it down
excuses = self.excuses = {} excuses = self.excuses = {}
# for every source package in testing, check if it should be removed # for every source package in testing, check if it should be removed
for pkg in testing: for pkg in testing:
if should_remove_source(pkg): if should_remove_source(pkg):
upgrade_me_append("-" + pkg) upgrade_me_add("-" + pkg)
# for every source package in unstable check if it should be upgraded # for every source package in unstable check if it should be upgraded
for pkg in unstable: for pkg in unstable:
@ -1567,11 +1575,11 @@ class Britney(object):
if pkg in testing and not testing[pkg].is_fakesrc: if pkg in testing and not testing[pkg].is_fakesrc:
for arch in architectures: for arch in architectures:
if should_upgrade_srcarch(pkg, arch, 'unstable'): if should_upgrade_srcarch(pkg, arch, 'unstable'):
upgrade_me_append("%s/%s" % (pkg, arch)) upgrade_me_add("%s/%s" % (pkg, arch))
# check if the source package should be upgraded # check if the source package should be upgraded
if should_upgrade_src(pkg, 'unstable'): if should_upgrade_src(pkg, 'unstable'):
upgrade_me_append(pkg) upgrade_me_add(pkg)
# for every source package in *-proposed-updates, check if it should be upgraded # for every source package in *-proposed-updates, check if it should be upgraded
for suite in ['pu', 'tpu']: for suite in ['pu', 'tpu']:
@ -1581,11 +1589,11 @@ class Britney(object):
if pkg in testing: if pkg in testing:
for arch in architectures: for arch in architectures:
if should_upgrade_srcarch(pkg, arch, suite): if should_upgrade_srcarch(pkg, arch, suite):
upgrade_me_append("%s/%s_%s" % (pkg, arch, suite)) upgrade_me_add("%s/%s_%s" % (pkg, arch, suite))
# check if the source package should be upgraded # check if the source package should be upgraded
if should_upgrade_src(pkg, suite): if should_upgrade_src(pkg, suite):
upgrade_me_append("%s_%s" % (pkg, suite)) upgrade_me_add("%s_%s" % (pkg, suite))
# process the `remove' hints, if the given package is not yet in upgrade_me # process the `remove' hints, if the given package is not yet in upgrade_me
for hint in self.hints['remove']: for hint in self.hints['remove']:
@ -1600,7 +1608,7 @@ class Britney(object):
continue continue
# add the removal of the package to upgrade_me and build a new excuse # add the removal of the package to upgrade_me and build a new excuse
upgrade_me_append("-%s" % (src)) upgrade_me_add("-%s" % (src))
excuse = Excuse("-%s" % (src)) excuse = Excuse("-%s" % (src))
excuse.set_vers(tsrcv, None) excuse.set_vers(tsrcv, None)
excuse.addhtml("Removal request by %s" % (hint.user)) excuse.addhtml("Removal request by %s" % (hint.user))
@ -1613,7 +1621,7 @@ class Britney(object):
excuses[excuse.name] = excuse excuses[excuse.name] = excuse
# extract the not considered packages, which are in the excuses but not in upgrade_me # extract the not considered packages, which are in the excuses but not in upgrade_me
unconsidered = [ename for ename in excuses if ename not in upgrade_me] unconsidered = {ename for ename in excuses if ename not in upgrade_me}
# invalidate impossible excuses # invalidate impossible excuses
for e in excuses.values(): for e in excuses.values():
@ -2508,7 +2516,8 @@ class Britney(object):
# write HeidiResult # write HeidiResult
self.log("Writing Heidi results to %s" % self.options.heidi_output) self.log("Writing Heidi results to %s" % self.options.heidi_output)
write_heidi(self.options.heidi_output, self.sources["testing"], write_heidi(self.options.heidi_output, self.sources["testing"],
self.binaries["testing"]) self.binaries["testing"],
outofsync_arches=self.options.outofsync_arches)
self.log("Writing delta to %s" % self.options.heidi_delta_output) self.log("Writing delta to %s" % self.options.heidi_delta_output)
write_heidi_delta(self.options.heidi_delta_output, write_heidi_delta(self.options.heidi_delta_output,

@ -9,14 +9,15 @@ SuiteInfo = namedtuple('SuiteInfo', [
class SourcePackage(object): class SourcePackage(object):
__slots__ = ['version', 'section', 'binaries', 'maintainer', 'is_fakesrc', 'testsuite', 'testsuite_triggers'] __slots__ = ['version', 'section', 'binaries', 'maintainer', 'is_fakesrc', 'build_deps_arch', 'testsuite', 'testsuite_triggers']
def __init__(self, version, section, binaries, maintainer, is_fakesrc, testsuite, testsuite_triggers): def __init__(self, version, section, binaries, maintainer, is_fakesrc, build_deps_arch, testsuite, testsuite_triggers):
self.version = version self.version = version
self.section = section self.section = section
self.binaries = binaries self.binaries = binaries
self.maintainer = maintainer self.maintainer = maintainer
self.is_fakesrc = is_fakesrc self.is_fakesrc = is_fakesrc
self.build_deps_arch = build_deps_arch
self.testsuite = testsuite self.testsuite = testsuite
self.testsuite_triggers = testsuite_triggers self.testsuite_triggers = testsuite_triggers

@ -74,12 +74,13 @@ class Excuse(object):
self.forced = False self.forced = False
self._policy_verdict = PolicyVerdict.REJECTED_PERMANENTLY self._policy_verdict = PolicyVerdict.REJECTED_PERMANENTLY
self.invalid_deps = [] self.invalid_deps = set()
self.invalid_build_deps = set()
self.deps = {} self.deps = {}
self.arch_build_deps = {}
self.sane_deps = [] self.sane_deps = []
self.break_deps = [] self.break_deps = []
self.unsatisfiable_on_archs = [] self.unsatisfiable_on_archs = []
self.bugs = []
self.newbugs = set() self.newbugs = set()
self.oldbugs = set() self.oldbugs = set()
self.reason = {} self.reason = {}
@ -145,9 +146,18 @@ class Excuse(object):
if arch not in self.unsatisfiable_on_archs: if arch not in self.unsatisfiable_on_archs:
self.unsatisfiable_on_archs.append(arch) 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 invalidate_dep(self, name): def invalidate_dep(self, name):
"""Invalidate dependency""" """Invalidate dependency"""
if name not in self.invalid_deps: self.invalid_deps.append(name) self.invalid_deps.add(name)
def invalidate_build_dep(self, name):
"""Invalidate build-dependency"""
self.invalid_build_deps.add(name)
def setdaysold(self, daysold, mindays): def setdaysold(self, daysold, mindays):
"""Set the number of days from the upload and the minimum number of days for the update""" """Set the number of days from the upload and the minimum number of days for the update"""
@ -219,6 +229,17 @@ class Excuse(object):
for (n,a) in self.break_deps: for (n,a) in self.break_deps:
if n not in self.deps: if n not in self.deps:
res += "<li>Ignoring %s depends: <a href=\"#%s\">%s</a>\n" % (a, n, n) res += "<li>Ignoring %s depends: <a href=\"#%s\">%s</a>\n" % (a, n, n)
lastdep = ""
for x in sorted(self.arch_build_deps, key=lambda x: x.split('/')[0]):
dep = x.split('/')[0]
if dep == lastdep:
continue
lastdep = dep
if x in self.invalid_build_deps:
res = res + "<li>Build-Depends(-Arch): %s <a href=\"#%s\">%s</a> (not ready)\n" % (self.name, dep, dep)
else:
res = res + "<li>Build-Depends(-Arch): %s <a href=\"#%s\">%s</a>\n" % (self.name, dep, dep)
if self.is_valid: if self.is_valid:
res += "<li>Valid candidate\n" res += "<li>Valid candidate\n"
else: else:
@ -268,13 +289,14 @@ class Excuse(object):
'on-architectures': sorted(self.missing_builds), 'on-architectures': sorted(self.missing_builds),
'on-unimportant-architectures': sorted(self.missing_builds_ood_arch), 'on-unimportant-architectures': sorted(self.missing_builds_ood_arch),
} }
if self.deps or self.invalid_deps or self.break_deps: if self.deps or self.invalid_deps or self.arch_build_deps or self.invalid_build_deps or self.break_deps:
excusedata['dependencies'] = dep_data = {} excusedata['dependencies'] = dep_data = {}
migrate_after = sorted(x for x in self.deps if x not in self.invalid_deps) migrate_after = sorted((self.deps.keys() - self.invalid_deps)
| (self.arch_build_deps.keys() - self.invalid_build_deps))
break_deps = [x for x, _ in self.break_deps if x not in self.deps] break_deps = [x for x, _ in self.break_deps if x not in self.deps]
if self.invalid_deps: if self.invalid_deps or self.invalid_build_deps:
dep_data['blocked-by'] = sorted(self.invalid_deps) dep_data['blocked-by'] = sorted(self.invalid_deps | self.invalid_build_deps)
if migrate_after: if migrate_after:
dep_data['migrate-after'] = migrate_after dep_data['migrate-after'] = migrate_after
if break_deps: if break_deps:

@ -0,0 +1,53 @@
from enum import Enum, unique
@unique
class PolicyVerdict(Enum):
""""""
"""
The migration item passed the policy.
"""
PASS = 1
"""
The policy was completely overruled by a hint.
"""
PASS_HINTED = 2
"""
The migration item did not pass the policy, but the failure is believed
to be temporary
"""
REJECTED_TEMPORARILY = 3
"""
The migration item is temporarily unable to migrate due to another item. The other item is temporarily blocked.
"""
REJECTED_WAITING_FOR_ANOTHER_ITEM = 4
"""
The migration item is permanently unable to migrate due to another item. The other item is permanently blocked.
"""
REJECTED_BLOCKED_BY_ANOTHER_ITEM = 5
"""
The migration item needs approval to migrate
"""
REJECTED_NEEDS_APPROVAL = 6
"""
The migration item is blocked, but there is not enough information to determine
if this issue is permanent or temporary
"""
REJECTED_CANNOT_DETERMINE_IF_PERMANENT = 7
"""
The migration item did not pass the policy and the failure is believed
to be uncorrectable (i.e. a hint or a new version is needed)
"""
REJECTED_PERMANENTLY = 8
@property
def is_rejected(self):
return True if self.name.startswith('REJECTED') else False
def is_blocked(self):
"""Whether the item (probably) needs a fix or manual assistance to migrate"""
return self in {
PolicyVerdict.REJECTED_BLOCKED_BY_ANOTHER_ITEM,
PolicyVerdict.REJECTED_NEEDS_APPROVAL,
PolicyVerdict.REJECTED_CANNOT_DETERMINE_IF_PERMANENT, # Assuming the worst
PolicyVerdict.REJECTED_PERMANENTLY,
}

@ -2,65 +2,13 @@ import json
import os import os
import time import time
from abc import abstractmethod from abc import abstractmethod
from enum import Enum, unique
from urllib.parse import quote from urllib.parse import quote
import apt_pkg import apt_pkg
from britney2.hints import Hint, split_into_one_hint_per_package from britney2.hints import Hint, split_into_one_hint_per_package
from britney2.policies import PolicyVerdict
from britney2.utils import get_dependency_solvers
@unique
class PolicyVerdict(Enum):
""""""
"""
The migration item passed the policy.
"""
PASS = 1
"""
The policy was completely overruled by a hint.
"""
PASS_HINTED = 2
"""
The migration item did not pass the policy, but the failure is believed
to be temporary
"""
REJECTED_TEMPORARILY = 3
"""
The migration item is temporarily unable to migrate due to another item. The other item is temporarily blocked.
"""
REJECTED_WAITING_FOR_ANOTHER_ITEM = 4
"""
The migration item is permanently unable to migrate due to another item. The other item is permanently blocked.
"""
REJECTED_BLOCKED_BY_ANOTHER_ITEM = 5
"""
The migration item needs approval to migrate
"""
REJECTED_NEEDS_APPROVAL = 6
"""
The migration item is blocked, but there is not enough information to determine
if this issue is permanent or temporary
"""
REJECTED_CANNOT_DETERMINE_IF_PERMANENT = 7
"""
The migration item did not pass the policy and the failure is believed
to be uncorrectable (i.e. a hint or a new version is needed)
"""
REJECTED_PERMANENTLY = 8
@property
def is_rejected(self):
return True if self.name.startswith('REJECTED') else False
def is_blocked(self):
"""Whether the item (probably) needs a fix or manual assistance to migrate"""
return self in {
PolicyVerdict.REJECTED_BLOCKED_BY_ANOTHER_ITEM,
PolicyVerdict.REJECTED_NEEDS_APPROVAL,
PolicyVerdict.REJECTED_CANNOT_DETERMINE_IF_PERMANENT, # Assuming the worst
PolicyVerdict.REJECTED_PERMANENTLY,
}
class BasePolicy(object): class BasePolicy(object):
@ -695,7 +643,92 @@ class PiupartsPolicy(BasePolicy):
item = next(iter(suite_data.values())) item = next(iter(suite_data.values()))
state, _, url = item state, _, url = item
if not keep_url: if not keep_url:
keep_url = None url = None
summary[source] = (state, url) summary[source] = (state, url)
return summary return summary
class BuildDependsPolicy(BasePolicy):
def __init__(self, options, suite_info):
super().__init__('build-depends', options, suite_info, {'unstable', 'tpu', 'pu'})
self._britney = None
def initialise(self, britney):
super().initialise(britney)
self._britney = britney
def apply_policy_impl(self, build_deps_info, suite, source_name, source_data_tdist, source_data_srcdist, excuse,
get_dependency_solvers=get_dependency_solvers):
verdict = PolicyVerdict.PASS
britney = self._britney
# local copies for better performance
parse_src_depends = apt_pkg.parse_src_depends
# analyze the dependency fields (if present)
deps = source_data_srcdist.build_deps_arch
if not deps:
return verdict
sources_s = None
sources_t = None
unsat_bd = {}
relevant_archs = {binary.architecture for binary in source_data_srcdist.binaries
if britney.all_binaries[binary].architecture != 'all'}
for arch in (arch for arch in self.options.architectures if arch in relevant_archs):
# retrieve the binary package from the specified suite and arch
binaries_s_a, provides_s_a = britney.binaries[suite][arch]
binaries_t_a, provides_t_a = britney.binaries['testing'][arch]
# for every dependency block (formed as conjunction of disjunction)
for block_txt in deps.split(','):
block = parse_src_depends(block_txt, False, arch)
# Unlike regular dependencies, some clauses of the Build-Depends(-Arch|-Indep) can be
# filtered out by (e.g.) architecture restrictions. We need to cope with this while
# keeping block_txt and block aligned.
if not block:
# Relation is not relevant for this architecture.
continue
block = block[0]
# if the block is satisfied in testing, then skip the block
if get_dependency_solvers(block, binaries_t_a, provides_t_a, build_depends=True):
# Satisfied in testing; all ok.
continue
# check if the block can be satisfied in the source suite, and list the solving packages
packages = get_dependency_solvers(block, binaries_s_a, provides_s_a, build_depends=True)
packages = [binaries_s_a[p].source for p in packages]
# if the dependency can be satisfied by the same source package, skip the block:
# obviously both binary packages will enter testing together
if source_name in packages:
continue
# if no package can satisfy the dependency, add this information to the excuse
if not packages:
excuse.addhtml("%s unsatisfiable Build-Depends(-Arch) on %s: %s" % (source_name, arch, block_txt.strip()))
if arch not in unsat_bd:
unsat_bd[arch] = []
unsat_bd[arch].append(block_txt.strip())
if verdict.value < PolicyVerdict.REJECTED_PERMANENTLY.value:
verdict = PolicyVerdict.REJECTED_PERMANENTLY
continue
if not sources_t:
sources_t = britney.sources['testing']
sources_s = britney.sources[suite]
# for the solving packages, update the excuse to add the dependencies
for p in packages:
if arch not in self.options.break_arches:
if p in sources_t and sources_t[p].version == sources_s[p].version:
excuse.add_arch_build_dep("%s/%s" % (p, arch), arch)
else:
excuse.add_arch_build_dep(p, arch)
if unsat_bd:
build_deps_info['unsatisfiable-arch-build-depends'] = unsat_bd
return verdict

@ -39,7 +39,7 @@ from britney2.consts import (VERSION, PROVIDES, DEPENDS, CONFLICTS,
SOURCE, MAINTAINER, MULTIARCH, SOURCE, MAINTAINER, MULTIARCH,
ESSENTIAL) ESSENTIAL)
from britney2.migrationitem import MigrationItem, UnversionnedMigrationItem from britney2.migrationitem import MigrationItem, UnversionnedMigrationItem
from britney2.policies.policy import PolicyVerdict from britney2.policies import PolicyVerdict
def ifilter_except(container, iterable=None): def ifilter_except(container, iterable=None):
@ -258,7 +258,7 @@ def eval_uninst(architectures, nuninst):
return "".join(parts) return "".join(parts)
def write_heidi(filename, sources_t, packages_t, sorted=sorted): def write_heidi(filename, sources_t, packages_t, *, outofsync_arches=frozenset(), sorted=sorted):
"""Write the output HeidiResult """Write the output HeidiResult
This method write the output for Heidi, which contains all the This method write the output for Heidi, which contains all the
@ -271,6 +271,10 @@ def write_heidi(filename, sources_t, packages_t, sorted=sorted):
packages in "sources_t" and "packages_t" to be the packages in packages in "sources_t" and "packages_t" to be the packages in
"testing". "testing".
outofsync_arches: If given, it is a set of architectures marked
as "out of sync". The output file may exclude some out of date
arch:all packages for those architectures to reduce the noise.
The "X=X" parameters are optimizations to avoid "load global" in The "X=X" parameters are optimizations to avoid "load global" in
the loops. the loops.
""" """
@ -288,7 +292,8 @@ def write_heidi(filename, sources_t, packages_t, sorted=sorted):
# Faux package; not really a part of testing # Faux package; not really a part of testing
continue continue
if pkg.source_version and pkgarch == 'all' and \ if pkg.source_version and pkgarch == 'all' and \
pkg.source_version != sources_t[pkg.source].version: pkg.source_version != sources_t[pkg.source].version and \
arch in outofsync_arches:
# when architectures are marked as "outofsync", their binary # when architectures are marked as "outofsync", their binary
# versions may be lower than those of the associated # versions may be lower than those of the associated
# source package in testing. the binary package list for # source package in testing. the binary package list for
@ -721,27 +726,41 @@ def read_sources_file(filename, sources=None, intern=sys.intern):
section = get_field('Section') section = get_field('Section')
if section: if section:
section = intern(section.strip()) section = intern(section.strip())
build_deps_arch = ", ".join(x for x in (get_field('Build-Depends'), get_field('Build-Depends-Arch'))
if x is not None)
if build_deps_arch != '':
build_deps_arch = sys.intern(build_deps_arch)
else:
build_deps_arch = None
sources[intern(pkg)] = SourcePackage(intern(ver), sources[intern(pkg)] = SourcePackage(intern(ver),
section, section,
[], [],
maint, maint,
False, False,
build_deps_arch,
get_field('Testsuite', '').split(), get_field('Testsuite', '').split(),
get_field('Testsuite-Triggers', '').replace(',', '').split(), get_field('Testsuite-Triggers', '').replace(',', '').split(),
) )
return sources return sources
def get_dependency_solvers(block, binaries_s_a, provides_s_a, *, empty_set=frozenset()): def get_dependency_solvers(block, binaries_s_a, provides_s_a, *, build_depends=False, empty_set=frozenset()):
"""Find the packages which satisfy a dependency block """Find the packages which satisfy a dependency block
This method returns the list of packages which satisfy a dependency This method returns the list of packages which satisfy a dependency
block (as returned by apt_pkg.parse_depends) in a package table block (as returned by apt_pkg.parse_depends) in a package table
for a given suite and architecture (a la self.binaries[suite][arch]) for a given suite and architecture (a la self.binaries[suite][arch])
:param block: The dependency block as parsed by apt_pkg.parse_depends It can also handle build-dependency relations if the named parameter
"build_depends" is set to True. In this case, block should be based
on the return value from apt_pkg.parse_src_depends.
:param block: The dependency block as parsed by apt_pkg.parse_depends (or apt_pkg.parse_src_depends
if the "build_depends" is True)
:param binaries_s_a: A dict mapping package names to the relevant BinaryPackage :param binaries_s_a: A dict mapping package names to the relevant BinaryPackage
:param provides_s_a: A dict mapping package names to their providers (as generated by parse_provides) :param provides_s_a: A dict mapping package names to their providers (as generated by parse_provides)
:param build_depends: If True, treat the "block" parameter as a build-dependency relation rather than
a regular dependency relation.
:param empty_set: Internal implementation detail / optimisation :param empty_set: Internal implementation detail / optimisation
:return a list of package names solving the relation :return a list of package names solving the relation
""" """
@ -760,7 +779,17 @@ def get_dependency_solvers(block, binaries_s_a, provides_s_a, *, empty_set=froze
# check the versioned dependency and architecture qualifier # check the versioned dependency and architecture qualifier
# (if present) # (if present)
if (op == '' and version == '') or apt_pkg.check_dep(package.version, op, version): if (op == '' and version == '') or apt_pkg.check_dep(package.version, op, version):
if archqual is None or (archqual == 'any' and package.multi_arch == 'allowed'): if archqual is None:
packages.append(name)
elif build_depends:
# Multi-arch handling for build-dependencies
# - :native is ok iff the target is arch:any
if archqual == 'native' and package.architecture != 'all':
packages.append(name)
# Multi-arch handling for both build-dependencies and regular dependencies
# - :any is ok iff the target has "M-A: allowed"
if archqual == 'any' and package.multi_arch == 'allowed':
packages.append(name) packages.append(name)
# look for the package in the virtual packages list and loop on them # look for the package in the virtual packages list and loop on them
@ -785,20 +814,23 @@ def invalidate_excuses(excuses, valid, invalid):
"""Invalidate impossible excuses """Invalidate impossible excuses
This method invalidates the impossible excuses, which depend This method invalidates the impossible excuses, which depend
on invalid excuses. The two parameters contains the list of on invalid excuses. The two parameters contains the sets of
`valid' and `invalid' excuses. `valid' and `invalid' excuses.
""" """
# build the reverse dependencies # build the reverse dependencies
revdeps = defaultdict(list) revdeps = defaultdict(list)
revbuilddeps = defaultdict(list)
for exc in excuses.values(): for exc in excuses.values():
for d in exc.deps: for d in exc.deps:
revdeps[d].append(exc.name) revdeps[d].append(exc.name)
for d in exc.arch_build_deps:
revbuilddeps[d].append(exc.name)
# loop on the invalid excuses # loop on the invalid excuses
for i, ename in enumerate(invalid): for ename in iter_except(invalid.pop, KeyError):
# if there is no reverse dependency, skip the item # if there is no reverse dependency, skip the item
if ename not in revdeps: if ename not in revdeps and ename not in revbuilddeps:
continue continue
# if the dependency can be satisfied by a testing-proposed-updates excuse, skip the item # if the dependency can be satisfied by a testing-proposed-updates excuse, skip the item
if (ename + "_tpu") in valid: if (ename + "_tpu") in valid:
@ -809,22 +841,35 @@ def invalidate_excuses(excuses, valid, invalid):
rdep_verdict = PolicyVerdict.REJECTED_BLOCKED_BY_ANOTHER_ITEM rdep_verdict = PolicyVerdict.REJECTED_BLOCKED_BY_ANOTHER_ITEM
# loop on the reverse dependencies # loop on the reverse dependencies
if ename in revdeps:
for x in revdeps[ename]: for x in revdeps[ename]:
if x in valid: # if the item is valid and it is not marked as `forced', then we invalidate it
# if the item is valid and it is marked as `forced', skip the item if x in valid and not excuses[x].forced:
if excuses[x].forced:
continue
# otherwise, invalidate the dependency and mark as invalidated and # otherwise, invalidate the dependency and mark as invalidated and
# remove the depending excuses # remove the depending excuses
excuses[x].invalidate_dep(ename) excuses[x].invalidate_dep(ename)
p = valid.index(x) valid.discard(x)
invalid.append(valid.pop(p)) invalid.add(x)
excuses[x].addhtml("Invalidated by dependency") excuses[x].addhtml("Invalidated by dependency")
excuses[x].addreason("depends") excuses[x].addreason("depends")
if excuses[x].policy_verdict.value < rdep_verdict.value: if excuses[x].policy_verdict.value < rdep_verdict.value:
excuses[x].policy_verdict = rdep_verdict excuses[x].policy_verdict = rdep_verdict
if ename in revbuilddeps:
for x in revbuilddeps[ename]:
# if the item is valid and it is not marked as `forced', then we invalidate it
if x in valid and not excuses[x].forced:
# otherwise, invalidate the dependency and mark as invalidated and
# remove the depending excuses
excuses[x].invalidate_build_dep(ename)
valid.discard(x)
invalid.add(x)
excuses[x].addhtml("Invalidated by build-dependency")
if excuses[x].policy_verdict.value < rdep_verdict.value:
excuses[x].policy_verdict = rdep_verdict
def compile_nuninst(binaries_t, inst_tester, architectures, nobreakall_arches): def compile_nuninst(binaries_t, inst_tester, architectures, nobreakall_arches):
"""Compile a nuninst dict from the current testing """Compile a nuninst dict from the current testing

@ -39,7 +39,7 @@ def create_excuse(name):
def create_source_package(version, section='devel', binaries=None): def create_source_package(version, section='devel', binaries=None):
if binaries is None: if binaries is None:
binaries = [] binaries = []
return SourcePackage(version, section, binaries, 'Random tester', False, '', '') return SourcePackage(version, section, binaries, 'Random tester', False, None, '', '')
def create_policy_objects(source_name, target_version, source_version): def create_policy_objects(source_name, target_version, source_version):

Loading…
Cancel
Save