diff --git a/repos_cmp/repos.py b/repos_cmp/repos.py index f613ec0..4c4f72c 100644 --- a/repos_cmp/repos.py +++ b/repos_cmp/repos.py @@ -227,7 +227,7 @@ class Repository: del self.binaries[name] self.update_indexes() - def chroot_for(self, requires): + def chroot_for(self, requires, prefer=()): stack = list(reversed(list(requires))) chroot_provides = collections.defaultdict(set) chroot_binaries = [] @@ -242,9 +242,16 @@ class Repository: if not providers: unmets.append(dep) continue + if len(providers) > 1: + preferred = [p for p in providers + if p.source_name in prefer] + if preferred and len(preferred) < len(providers): + LOG.info("Using preferred providers: %s %s", + dep, preferred) + providers = preferred if len(providers) > 1: LOG.warning('Ambiguous provide: %s (%s)', dep, providers) - # that's almost random if ambigous: + # choose an (almost) random provider p = providers.pop() LOG.debug(" installing %s", p) chroot_binaries.append(p) @@ -290,16 +297,7 @@ def have_same_source(repoA, repoB, source_name): _BUILDREQ = '0000-BR' -def _missing_requires(from_repo, to_repo, requires, kind, ignore): - missing_buidreqs = (dep for dep in requires - if not any(to_repo.providers(dep))) - for dep in missing_buidreqs: - for _dep, provider in from_repo.providers(dep): - if provider.source_name not in ignore: - yield kind, dep, provider - - -def _raw_build_report(from_repo, to_repo, source_name, ignore=()): +def _raw_build_report(from_repo, to_repo, source_name, ignore=(), prefer=()): """Build report for one source, by name Returns an iterable over tuples (kind, dep, provider). @@ -308,36 +306,50 @@ def _raw_build_report(from_repo, to_repo, source_name, ignore=()): assert from_repo.bits == 64 translate = (to_repo.bits != 64) - result = set(_missing_requires( - from_repo, to_repo, from_repo.sources[source_name].requires, - _BUILDREQ, ignore)) + missing_buildreqs = ((_BUILDREQ, dep) + for dep in from_repo.sources[source_name].requires) + missing_binreqs = itertools.chain.from_iterable( + ((b.name.decode(), dep) for dep in b.requires) + for b in from_repo.binaries_from(source_name)) + result = set() - for b in from_repo.binaries_from(source_name): - for dep in b.requires: - # skip some platform-specific stuff - if any(x in dep.name for x in _SPECIAL_DEPS): - continue - # if needed, try to translate from 64 to 32 bits - the_dep = _from_64bit_dep(dep) if translate else dep - # skip dependencies already present in to_repo - if any(to_repo.providers(the_dep)): - continue - # skip inter-sub-package dependencies - if any(p.source_name == source_name + for kind, dep in itertools.chain(missing_buildreqs, missing_binreqs): + # skip some platform-specific stuff + if any(x in dep.name for x in _SPECIAL_DEPS): + continue + # if needed, try to translate from 64 to 32 bits + the_dep = _from_64bit_dep(dep) if translate else dep + # skip dependencies already present in to_repo + if any(to_repo.providers(the_dep)): + continue + # skip inter-sub-package dependencies + if any(p.source_name == source_name + for _d, p in from_repo.providers(dep)): + continue + # set-versions may be platform-dependent. + # double-check that if have the same source + if (dep.is_setversion() + or _GHC_HASHLIB.match(dep.name) + or _GHC_HASHDEP.fullmatch(dep.name)): + if any(have_same_source(from_repo, to_repo, p.source_name) for _d, p in from_repo.providers(dep)): continue - # set-versions may be platform-dependent. - # double-check that if have the same source - if (dep.is_setversion() - or _GHC_HASHLIB.match(dep.name) - or _GHC_HASHDEP.fullmatch(dep.name)): - if any(have_same_source(from_repo, to_repo, p.source_name) - for _d, p in from_repo.providers(dep)): - continue - # ok, it's true missing dependency - for _dep, provider in from_repo.providers(dep): - if provider.source_name not in ignore: - result.add((b.name.decode(), dep, provider)) + # ok, it's true missing dependency + # let's look up providers + providers = set(provider + for _dep, provider in from_repo.providers(dep) + if provider.source_name not in ignore) + if not providers: + LOG.warning("No providers for %s", dep) + elif len(providers) > 1: + LOG.warning("%d provider(s) for %s: %s", + len(providers), dep, list(providers)) + preferred = [p for p in providers + if p.source_name in prefer] + if preferred and len(preferred) < len(providers): + LOG.info("Using preferred providers: %s", preferred) + providers = preferred + result.update((kind, dep, provider) for provider in providers) return result @@ -387,7 +399,8 @@ def recursive_build_report(from_repo, to_repo, *source_names, while stack: cur = stack.pop() - report = _raw_build_report(from_repo, to_repo, cur, ignore) + report = _raw_build_report(from_repo, to_repo, cur, + ignore, prefer=seen) cur_in_to = to_repo.sources.get(cur) cur_source_srpm = cur_in_to.source_rpm if cur_in_to else b'NONE' reports.append('\n== %s ==\n%s has %s\n' % ( @@ -408,8 +421,10 @@ def recursive_build_report(from_repo, to_repo, *source_names, # expand the build requires with the pkg reqs of dependencies: full_req = {} + names = set(build_source_deps) for source in build_source_deps: - _u, bins = from_repo.chroot_for(from_repo.sources[source].requires) + _u, bins = from_repo.chroot_for(from_repo.sources[source].requires, + prefer=names) dep_sources = set() for b in bins: if b.source_name not in build_source_deps: @@ -472,20 +487,6 @@ def recursive_build_report(from_repo, to_repo, *source_names, return '\n'.join(reports) -def requires_build_report(from_repo, to_repo, *requires, - ignore=(), ignore_sort=()): - missing_reqs = list(_missing_requires( - from_repo, to_repo, - [Dependency.wrap(r) for r in requires], - ignore, ignore_sort)) - reports = [format_triplet_report(missing_reqs, 'Requested')] - source_names = set(p.source_name for _k, _d, p in missing_reqs) - reports.append(recursive_build_report( - from_repo, to_repo, *source_names, - ignore=ignore, ignore_sort=ignore_sort)) - return '\n'.join(reports) - - def _unmets_without(repo, kind, *source_names): next_repo = repo.copy('removal_test_' + repo.name) next_repo.delete_sources(*source_names)