repos.recursive_build_report: Sort summary topologically

... if possible. If not, summary is sorted alphabetically
and the first cycle found is reported to logs.

Dependencies for sorting use very strong model: A should
be rebuild before B if any packages built from A are
in the build chroot for B. Of course, our rudimentary
implementation of chroot_for does not mimic real
apt packages selecting logic, so it gives us just an
approximation, but hopefully a useful one.
This commit is contained in:
Ivan A. Melnikov 2021-09-08 12:35:05 +04:00
parent bfce854471
commit 048ed5fe6c

View File

@ -1,8 +1,9 @@
# This is Python3
# This is Python3.9
# This module does not use str: use bytes everywhere.
import collections
import graphlib
import itertools
import logging
@ -269,7 +270,7 @@ def have_same_source(repoA, repoB, source_name):
_BUILDREQ = '0000-BR'
def _raw_build_report(from_repo, to_repo, source_name):
def _raw_build_report(from_repo, to_repo, source_name, ignore=()):
"""Build report for one source, by name
Returns an iterable over tuples (kind, dep, provider).
@ -284,7 +285,8 @@ def _raw_build_report(from_repo, to_repo, source_name):
if not any(to_repo.providers(dep)))
for dep in missing_buidreqs:
for _dep, provider in from_repo.providers(dep):
result.add((_BUILDREQ, dep, provider))
if provider.source_name not in ignore:
result.add((_BUILDREQ, dep, provider))
for b in from_repo.binaries_from(source_name):
for dep in b.requires:
@ -308,7 +310,8 @@ def _raw_build_report(from_repo, to_repo, source_name):
continue
# ok, it's true missing dependency
for _dep, provider in from_repo.providers(dep):
result.add((b.name.decode(), dep, provider))
if provider.source_name not in ignore:
result.add((b.name.decode(), dep, provider))
return result
@ -347,7 +350,8 @@ def _src_name(source):
return source
def recursive_build_report(from_repo, to_repo, *source_names):
def recursive_build_report(from_repo, to_repo, *source_names,
ignore=(), ignore_sort=()):
reports = []
build_source_deps = {}
pkg_source_deps = {}
@ -357,7 +361,7 @@ def recursive_build_report(from_repo, to_repo, *source_names):
while stack:
cur = stack.pop()
report = _raw_build_report(from_repo, to_repo, cur)
report = _raw_build_report(from_repo, to_repo, cur, ignore)
reports.append(format_triplet_report(report, cur.decode()))
# find all sources in the report
@ -372,12 +376,55 @@ def recursive_build_report(from_repo, to_repo, *source_names):
seen.update(unseen)
stack.extend(unseen)
# expand the build requires with the pkg reqs of dependencies:
full_req = {}
self_dependent = set()
for source in build_source_deps:
_u, bins = from_repo.chroot_for(from_repo.sources[source].requires)
dep_sources = set()
for b in bins:
if b.source_name not in build_source_deps:
continue
if b.source_name == source:
self_dependent.add(source)
continue
if (b.source_name, source) in ignore_sort:
continue
dep_sources.add(b.source_name)
full_req[source] = dep_sources
try:
order = list(graphlib.TopologicalSorter(full_req).static_order())
except graphlib.CycleError as ex:
LOG.error("Cycle detected: %s", ex)
order = sorted(build_source_deps)
reports.append('\n== SUMMARY ==\n')
for source in sorted(build_source_deps):
reports.append('{}\t{}: {} :: {}'.format(
'' if source in requested_source_names else 'N', source.decode(),
for source in order:
reports.append('{}\t{:50} {} :: {}'.format(
'' if source in requested_source_names else 'N',
from_repo.sources[source].source_rpm.decode(),
b' '.join(build_source_deps[source]).decode(),
b' '.join(pkg_source_deps[source]).decode()))
if self_dependent:
reports.append('\nSelf-dependant packages:')
reports.extend('\t %s' % source.decode()
for source in sorted(self_dependent))
special = set()
for source_name in order:
source = to_repo.sources.get(source_name)
if not source:
continue
if b'.mips' in source.release or b'.rv64' in source.release:
special.add(source.source_rpm)
if special:
reports.append('\nThe followng packages are special:')
reports.extend('\t %s' % source.decode()
for source in sorted(special))
return '\n'.join(reports)