Support passing in tie breaking functions to apt resolver.
This commit is contained in:
parent
94e0b4f99d
commit
0b6cc8d8cc
5 changed files with 154 additions and 290 deletions
|
@ -26,7 +26,7 @@ from buildlog_consultant.apt import (
|
|||
|
||||
from .. import DetailedFailure, UnidentifiedError
|
||||
from ..session import Session, run_with_tee, get_user
|
||||
from .file_search import FileSearcher, AptCachedContentsFileSearcher, GENERATED_FILE_SEARCHER, get_package_for_paths
|
||||
from .file_search import FileSearcher, AptCachedContentsFileSearcher, GENERATED_FILE_SEARCHER, get_packages_for_paths
|
||||
|
||||
|
||||
def run_apt(session: Session, args: List[str], prefix: Optional[List[str]] = None) -> None:
|
||||
|
@ -81,10 +81,10 @@ class AptManager(object):
|
|||
self._apt_cache = apt.Cache(rootdir=self.session.location)
|
||||
return package in self._apt_cache
|
||||
|
||||
def get_package_for_paths(self, paths, regex=False):
|
||||
def get_packages_for_paths(self, paths, regex=False):
|
||||
logging.debug("Searching for packages containing %r", paths)
|
||||
# TODO(jelmer): Make sure we use whatever is configured in self.session
|
||||
return get_package_for_paths(paths, self.searchers(), regex=regex)
|
||||
return get_packages_for_paths(paths, self.searchers(), regex=regex)
|
||||
|
||||
def missing(self, packages):
|
||||
root = getattr(self.session, "location", "/")
|
||||
|
|
|
@ -264,40 +264,14 @@ GENERATED_FILE_SEARCHER = GeneratedFileSearcher(
|
|||
)
|
||||
|
||||
|
||||
def get_package_for_paths(
|
||||
def get_packages_for_paths(
|
||||
paths: List[str], searchers: List[FileSearcher], regex: bool = False
|
||||
) -> Optional[str]:
|
||||
candidates: Set[str] = set()
|
||||
) -> List[str]:
|
||||
candidates: List[str] = list()
|
||||
for path in paths:
|
||||
for searcher in searchers:
|
||||
candidates.update(searcher.search_files(path, regex=regex))
|
||||
if candidates:
|
||||
break
|
||||
if len(candidates) == 0:
|
||||
logging.debug("No packages found that contain %r", paths)
|
||||
return None
|
||||
if len(candidates) > 1:
|
||||
logging.warning(
|
||||
"More than 1 packages found that contain %r: %r", path, candidates
|
||||
)
|
||||
# TODO(jelmer): Pick package based on what appears most commonly in
|
||||
# build-depends{-indep,-arch}
|
||||
try:
|
||||
from .udd import UDD
|
||||
except ModuleNotFoundError:
|
||||
logging.warning('Unable to import UDD, not ranking by popcon')
|
||||
return sorted(candidates, key=len)[0]
|
||||
udd = UDD()
|
||||
udd.connect()
|
||||
winner = udd.get_most_popular(candidates)
|
||||
if winner is None:
|
||||
logging.warning(
|
||||
'No relevant popcon information found, not ranking by popcon')
|
||||
return sorted(candidates, key=len)[0]
|
||||
logging.info('Picked winner using popcon')
|
||||
return winner
|
||||
else:
|
||||
return candidates.pop()
|
||||
candidates.extend(searcher.search_files(path, regex=regex))
|
||||
return candidates
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
@ -317,7 +291,8 @@ def main(argv):
|
|||
main_searcher.load_local()
|
||||
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
||||
|
||||
package = get_package_for_paths(args.path, searchers=searchers, regex=args.regex)
|
||||
packages = get_packages_for_paths(args.path, searchers=searchers, regex=args.regex)
|
||||
for package in packages:
|
||||
print(package)
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ __all__ = [
|
|||
]
|
||||
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
@ -131,11 +132,12 @@ class CircularDependency(Exception):
|
|||
|
||||
class DebianPackagingContext(object):
|
||||
|
||||
def __init__(self, tree, subpath, committer, update_changelog):
|
||||
def __init__(self, tree, subpath, committer, update_changelog, commit_reporter=None):
|
||||
self.tree = tree
|
||||
self.subpath = subpath
|
||||
self.committer = committer
|
||||
self.update_changelog = update_changelog
|
||||
self.commit_reporter = commit_reporter
|
||||
|
||||
def commit(self, summary: str, update_changelog: Optional[bool] = None) -> bool:
|
||||
if update_changelog is None:
|
||||
|
@ -149,7 +151,8 @@ class DebianPackagingContext(object):
|
|||
debcommit(self.tree, committer=self.committer, subpath=self.subpath)
|
||||
else:
|
||||
self.tree.commit(
|
||||
message=summary, committer=self.committer, specific_files=[self.subpath]
|
||||
message=summary, committer=self.committer, specific_files=[self.subpath],
|
||||
reporter=self.commit_reporter
|
||||
)
|
||||
except PointlessCommit:
|
||||
return False
|
||||
|
@ -268,134 +271,36 @@ def add_test_dependency(context, testname, requirement):
|
|||
)
|
||||
|
||||
|
||||
def targeted_python_versions(tree: Tree, subpath: str) -> Set[str]:
|
||||
def targeted_python_versions(tree: Tree, subpath: str) -> List[str]:
|
||||
with tree.get_file(os.path.join(subpath, "debian/control")) as f:
|
||||
control = Deb822(f)
|
||||
build_depends = PkgRelation.parse_relations(control.get("Build-Depends", ""))
|
||||
all_build_deps: Set[str] = set()
|
||||
for or_deps in build_depends:
|
||||
all_build_deps.update(or_dep["name"] for or_dep in or_deps)
|
||||
targeted = set()
|
||||
if any(x.startswith("pypy") for x in all_build_deps):
|
||||
targeted.add("pypy")
|
||||
if any(x.startswith("python-") for x in all_build_deps):
|
||||
targeted.add("cpython2")
|
||||
targeted = []
|
||||
if any(x.startswith("python3-") for x in all_build_deps):
|
||||
targeted.add("cpython3")
|
||||
targeted.append("python3")
|
||||
if any(x.startswith("pypy") for x in all_build_deps):
|
||||
targeted.append("pypy")
|
||||
if any(x.startswith("python-") for x in all_build_deps):
|
||||
targeted.append("python")
|
||||
return targeted
|
||||
|
||||
|
||||
def fix_missing_python_distribution(error, phase, apt, context): # noqa: C901
|
||||
targeted = targeted_python_versions(context.tree, context.subpath)
|
||||
default = not targeted
|
||||
|
||||
pypy_pkg = apt.get_package_for_paths(
|
||||
["/usr/lib/pypy/dist-packages/%s-.*.egg-info" % error.distribution], regex=True
|
||||
)
|
||||
if pypy_pkg is None:
|
||||
pypy_pkg = "pypy-%s" % error.distribution
|
||||
if not apt.package_exists(pypy_pkg):
|
||||
pypy_pkg = None
|
||||
|
||||
py2_pkg = apt.get_package_for_paths(
|
||||
["/usr/lib/python2\\.[0-9]/dist-packages/%s-.*.egg-info" % error.distribution],
|
||||
regex=True,
|
||||
)
|
||||
if py2_pkg is None:
|
||||
py2_pkg = "python-%s" % error.distribution
|
||||
if not apt.package_exists(py2_pkg):
|
||||
py2_pkg = None
|
||||
|
||||
py3_pkg = apt.get_package_for_paths(
|
||||
["/usr/lib/python3/dist-packages/%s-.*.egg-info" % error.distribution],
|
||||
regex=True,
|
||||
)
|
||||
if py3_pkg is None:
|
||||
py3_pkg = "python3-%s" % error.distribution
|
||||
if not apt.package_exists(py3_pkg):
|
||||
py3_pkg = None
|
||||
|
||||
extra_build_deps = []
|
||||
if error.python_version == 2:
|
||||
if "pypy" in targeted:
|
||||
if not pypy_pkg:
|
||||
logging.warning("no pypy package found for %s", error.module)
|
||||
else:
|
||||
extra_build_deps.append(pypy_pkg)
|
||||
if "cpython2" in targeted or default:
|
||||
if not py2_pkg:
|
||||
logging.warning("no python 2 package found for %s", error.module)
|
||||
return False
|
||||
extra_build_deps.append(py2_pkg)
|
||||
elif error.python_version == 3:
|
||||
if not py3_pkg:
|
||||
logging.warning("no python 3 package found for %s", error.module)
|
||||
return False
|
||||
extra_build_deps.append(py3_pkg)
|
||||
else:
|
||||
if py3_pkg and ("cpython3" in targeted or default):
|
||||
extra_build_deps.append(py3_pkg)
|
||||
if py2_pkg and ("cpython2" in targeted or default):
|
||||
extra_build_deps.append(py2_pkg)
|
||||
if pypy_pkg and "pypy" in targeted:
|
||||
extra_build_deps.append(pypy_pkg)
|
||||
|
||||
if not extra_build_deps:
|
||||
return False
|
||||
|
||||
for dep_pkg in extra_build_deps:
|
||||
assert dep_pkg is not None
|
||||
if not add_dependency(context, phase, dep_pkg):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def fix_missing_python_module(error, phase, apt, context):
|
||||
targeted = targeted_python_versions(context.tree, context.subpath)
|
||||
default = not targeted
|
||||
|
||||
if error.minimum_version:
|
||||
specs = [(">=", error.minimum_version)]
|
||||
else:
|
||||
specs = []
|
||||
|
||||
pypy_pkg = get_package_for_python_module(apt, error.module, "pypy", specs)
|
||||
py2_pkg = get_package_for_python_module(apt, error.module, "cpython2", specs)
|
||||
py3_pkg = get_package_for_python_module(apt, error.module, "cpython3", specs)
|
||||
|
||||
extra_build_deps = []
|
||||
if error.python_version == 2:
|
||||
if "pypy" in targeted:
|
||||
if not pypy_pkg:
|
||||
logging.warning("no pypy package found for %s", error.module)
|
||||
else:
|
||||
extra_build_deps.append(pypy_pkg)
|
||||
if "cpython2" in targeted or default:
|
||||
if not py2_pkg:
|
||||
logging.warning("no python 2 package found for %s", error.module)
|
||||
return False
|
||||
extra_build_deps.append(py2_pkg)
|
||||
elif error.python_version == 3:
|
||||
if not py3_pkg:
|
||||
logging.warning("no python 3 package found for %s", error.module)
|
||||
return False
|
||||
extra_build_deps.append(py3_pkg)
|
||||
else:
|
||||
if py3_pkg and ("cpython3" in targeted or default):
|
||||
extra_build_deps.append(py3_pkg)
|
||||
if py2_pkg and ("cpython2" in targeted or default):
|
||||
extra_build_deps.append(py2_pkg)
|
||||
if pypy_pkg and "pypy" in targeted:
|
||||
extra_build_deps.append(pypy_pkg)
|
||||
|
||||
if not extra_build_deps:
|
||||
return False
|
||||
|
||||
for dep_pkg in extra_build_deps:
|
||||
assert dep_pkg is not None
|
||||
if not add_dependency(context, phase, dep_pkg):
|
||||
return False
|
||||
return True
|
||||
def python_tie_breaker(tree, subpath, reqs):
|
||||
targeted = targeted_python_versions(tree, subpath)
|
||||
if not targeted:
|
||||
return None
|
||||
for prefix in targeted:
|
||||
for req in reqs:
|
||||
if any(name.startswith(prefix + '-') for name in req.package_names()):
|
||||
logging.info(
|
||||
'Breaking tie between %r to %r, since package already '
|
||||
'has %r build-dependencies', [str(req) for req in reqs],
|
||||
str(req), prefix)
|
||||
return req
|
||||
return None
|
||||
|
||||
|
||||
def retry_apt_failure(error, phase, apt, context):
|
||||
|
@ -536,12 +441,34 @@ def versioned_package_fixers(session, packaging_context):
|
|||
]
|
||||
|
||||
|
||||
def udd_tie_breaker(candidates):
|
||||
# TODO(jelmer): Pick package based on what appears most commonly in
|
||||
# build-depends{-indep,-arch}
|
||||
try:
|
||||
from .udd import UDD
|
||||
except ModuleNotFoundError:
|
||||
logging.warning('Unable to import UDD, not ranking by popcon')
|
||||
return sorted(candidates, key=len)[0]
|
||||
udd = UDD()
|
||||
udd.connect()
|
||||
names = {list(c.package_names())[0]: c for c in candidates}
|
||||
winner = udd.get_most_popular(list(names.keys()))
|
||||
if winner is None:
|
||||
logging.warning(
|
||||
'No relevant popcon information found, not ranking by popcon')
|
||||
return None
|
||||
logging.info('Picked winner using popcon')
|
||||
return names[winner]
|
||||
|
||||
|
||||
def apt_fixers(apt, packaging_context) -> List[BuildFixer]:
|
||||
from ..resolver.apt import AptResolver
|
||||
resolver = AptResolver(apt)
|
||||
apt_tie_breakers = [
|
||||
partial(python_tie_breaker, packaging_context.tree, packaging_context.subpath),
|
||||
udd_tie_breaker,
|
||||
]
|
||||
resolver = AptResolver(apt, apt_tie_breakers)
|
||||
return [
|
||||
DependencyBuildFixer(packaging_context, apt, MissingPythonModule, fix_missing_python_module),
|
||||
DependencyBuildFixer(packaging_context, apt, MissingPythonDistribution, fix_missing_python_distribution),
|
||||
DependencyBuildFixer(packaging_context, apt, AptFetchFailure, retry_apt_failure),
|
||||
PackageDependencyFixer(packaging_context, resolver),
|
||||
]
|
||||
|
|
|
@ -20,6 +20,7 @@ import logging
|
|||
import os
|
||||
import posixpath
|
||||
import re
|
||||
from typing import Optional, List
|
||||
|
||||
from debian.changelog import Version
|
||||
from debian.deb822 import PkgRelation
|
||||
|
@ -84,14 +85,30 @@ class AptRequirement(Requirement):
|
|||
def __str__(self):
|
||||
return "apt requirement: %s" % self.pkg_relation_str()
|
||||
|
||||
def touches_package(self, package):
|
||||
def package_names(self):
|
||||
for rel in self.relations:
|
||||
for entry in rel:
|
||||
if entry["name"] == package:
|
||||
yield entry["name"]
|
||||
|
||||
def touches_package(self, package):
|
||||
for name in self.package_names():
|
||||
if name == package:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def find_package_names(apt_mgr: AptManager, paths: List[str], regex: bool = False) -> List[str]:
|
||||
if not isinstance(paths, list):
|
||||
raise TypeError(paths)
|
||||
return apt_mgr.get_packages_for_paths(paths, regex)
|
||||
|
||||
|
||||
def find_reqs_simple(apt_mgr: AptManager, paths: List[str], regex: bool = False, minimum_version=None) -> List[str]:
|
||||
if not isinstance(paths, list):
|
||||
raise TypeError(paths)
|
||||
return [AptRequirement.simple(package, minimum_version=minimum_version) for package in find_package_names(apt_mgr, paths, regex)]
|
||||
|
||||
|
||||
def python_spec_to_apt_rels(pkg_name, specs):
|
||||
# TODO(jelmer): Dealing with epoch, etc?
|
||||
if not specs:
|
||||
|
@ -133,11 +150,8 @@ def get_package_for_python_package(apt_mgr, package, python_version: Optional[st
|
|||
paths = [cpython3_regex, cpython2_regex, pypy_regex]
|
||||
else:
|
||||
raise NotImplementedError('unsupported python version %d' % python_version)
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is None:
|
||||
return None
|
||||
rels = python_spec_to_apt_rels(pkg_name, specs)
|
||||
return AptRequirement(rels)
|
||||
names = find_package_names(apt_mgr, paths, regex=True)
|
||||
return [AptRequirement(python_spec_to_apt_rels(name, specs)) for name in names]
|
||||
|
||||
|
||||
def get_package_for_python_module(apt_mgr, module, python_version, specs):
|
||||
|
@ -198,11 +212,8 @@ def get_package_for_python_module(apt_mgr, module, python_version, specs):
|
|||
paths = cpython3_regexes + cpython2_regexes + pypy_regexes
|
||||
else:
|
||||
raise AssertionError("unknown python version %r" % python_version)
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is None:
|
||||
return None
|
||||
rels = python_spec_to_apt_rels(pkg_name, specs)
|
||||
return AptRequirement(rels)
|
||||
names = find_package_names(apt_mgr, paths, regex=True)
|
||||
return [AptRequirement(python_spec_to_apt_rels(name, specs)) for name in names]
|
||||
|
||||
|
||||
def resolve_binary_req(apt_mgr, req):
|
||||
|
@ -212,61 +223,42 @@ def resolve_binary_req(apt_mgr, req):
|
|||
paths = [
|
||||
posixpath.join(dirname, req.binary_name) for dirname in ["/usr/bin", "/bin"]
|
||||
]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths)
|
||||
|
||||
|
||||
def resolve_pkg_config_req(apt_mgr, req):
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
[posixpath.join("/usr/lib/pkgconfig", req.module + ".pc")],
|
||||
)
|
||||
if package is None:
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
names = find_package_names(apt_mgr,
|
||||
[posixpath.join("/usr/lib", ".*", "pkgconfig", re.escape(req.module) + "\\.pc")],
|
||||
regex=True,
|
||||
)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package, minimum_version=req.minimum_version)
|
||||
return None
|
||||
regex=True)
|
||||
if not names:
|
||||
names = find_package_names(
|
||||
apt_mgr, [posixpath.join("/usr/lib/pkgconfig", req.module + ".pc")])
|
||||
return [AptRequirement.simple(name, minimum_version=req.minimum_version) for name in names]
|
||||
|
||||
|
||||
def resolve_path_req(apt_mgr, req):
|
||||
package = apt_mgr.get_package_for_paths([req.path])
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [req.path])
|
||||
|
||||
|
||||
def resolve_c_header_req(apt_mgr, req):
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
[posixpath.join("/usr/include", req.header)], regex=False
|
||||
)
|
||||
if package is None:
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
reqs = find_reqs_simple(
|
||||
apt_mgr,
|
||||
[posixpath.join("/usr/include", req.header)], regex=False)
|
||||
if not reqs:
|
||||
reqs = find_package_names(
|
||||
apt_mgr,
|
||||
[posixpath.join("/usr/include", ".*", re.escape(req.header))], regex=True
|
||||
)
|
||||
if package is None:
|
||||
return None
|
||||
return AptRequirement.simple(package)
|
||||
return reqs
|
||||
|
||||
|
||||
def resolve_js_runtime_req(apt_mgr, req):
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
["/usr/bin/node", "/usr/bin/duk"], regex=False
|
||||
)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, ["/usr/bin/node", "/usr/bin/duk"])
|
||||
|
||||
|
||||
def resolve_vala_package_req(apt_mgr, req):
|
||||
path = "/usr/share/vala-[0-9.]+/vapi/%s\\.vapi" % re.escape(req.package)
|
||||
package = apt_mgr.get_package_for_paths([path], regex=True)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path], regex=True)
|
||||
|
||||
|
||||
def resolve_ruby_gem_req(apt_mgr, req):
|
||||
|
@ -276,43 +268,29 @@ def resolve_ruby_gem_req(apt_mgr, req):
|
|||
"specifications/%s-.*\\.gemspec" % re.escape(req.gem)
|
||||
)
|
||||
]
|
||||
package = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package, minimum_version=req.minimum_version)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=True, minimum_version=req.minimum_version)
|
||||
|
||||
|
||||
def resolve_go_package_req(apt_mgr, req):
|
||||
package = apt_mgr.get_package_for_paths(
|
||||
return find_reqs_simple(
|
||||
apt_mgr,
|
||||
[posixpath.join("/usr/share/gocode/src", re.escape(req.package), ".*")], regex=True
|
||||
)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
|
||||
|
||||
def resolve_dh_addon_req(apt_mgr, req):
|
||||
paths = [posixpath.join("/usr/share/perl5", req.path)]
|
||||
package = apt_mgr.get_package_for_paths(paths)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths)
|
||||
|
||||
|
||||
def resolve_php_class_req(apt_mgr, req):
|
||||
path = "/usr/share/php/%s.php" % req.php_class.replace("\\", "/")
|
||||
package = apt_mgr.get_package_for_paths([path])
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path])
|
||||
|
||||
|
||||
def resolve_r_package_req(apt_mgr, req):
|
||||
paths = [posixpath.join("/usr/lib/R/site-library/.*/R/%s$" % re.escape(req.package))]
|
||||
package = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=True)
|
||||
|
||||
|
||||
def resolve_node_package_req(apt_mgr, req):
|
||||
|
@ -321,10 +299,7 @@ def resolve_node_package_req(apt_mgr, req):
|
|||
"/usr/lib/nodejs/%s/package\\.json" % re.escape(req.package),
|
||||
"/usr/share/nodejs/%s/package\\.json" % re.escape(req.package),
|
||||
]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=True)
|
||||
|
||||
|
||||
def resolve_library_req(apt_mgr, req):
|
||||
|
@ -334,27 +309,21 @@ def resolve_library_req(apt_mgr, req):
|
|||
posixpath.join("/usr/lib/lib%s.a$" % re.escape(req.library)),
|
||||
posixpath.join("/usr/lib/.*/lib%s.a$" % re.escape(req.library)),
|
||||
]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths)
|
||||
|
||||
|
||||
def resolve_ruby_file_req(apt_mgr, req):
|
||||
paths = [posixpath.join("/usr/lib/ruby/vendor_ruby/%s.rb" % req.filename)]
|
||||
package = apt_mgr.get_package_for_paths(paths)
|
||||
if package is not None:
|
||||
return AptRequirement.simple(package)
|
||||
reqs = find_reqs_simple(apt_mgr, paths, regex=False)
|
||||
if reqs:
|
||||
return reqs
|
||||
paths = [
|
||||
posixpath.join(
|
||||
r"/usr/share/rubygems-integration/all/gems/([^/]+)/"
|
||||
"lib/%s\\.rb" % re.escape(req.filename)
|
||||
)
|
||||
]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=True)
|
||||
|
||||
|
||||
def resolve_xml_entity_req(apt_mgr, req):
|
||||
|
@ -370,10 +339,7 @@ def resolve_xml_entity_req(apt_mgr, req):
|
|||
else:
|
||||
return None
|
||||
|
||||
pkg_name = apt_mgr.get_package_for_paths([search_path], regex=False)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [search_path], regex=False)
|
||||
|
||||
|
||||
def resolve_sprockets_file_req(apt_mgr, req):
|
||||
|
@ -382,10 +348,7 @@ def resolve_sprockets_file_req(apt_mgr, req):
|
|||
else:
|
||||
logging.warning("unable to handle content type %s", req.content_type)
|
||||
return None
|
||||
pkg_name = apt_mgr.get_package_for_paths([path], regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path], regex=True)
|
||||
|
||||
|
||||
def resolve_java_class_req(apt_mgr, req):
|
||||
|
@ -399,19 +362,12 @@ def resolve_java_class_req(apt_mgr, req):
|
|||
logging.warning("unable to find classpath for %s", req.classname)
|
||||
return False
|
||||
logging.info("Classpath for %s: %r", req.classname, classpath)
|
||||
package = apt_mgr.get_package_for_paths(classpath)
|
||||
if package is None:
|
||||
logging.warning("no package for files in %r", classpath)
|
||||
return None
|
||||
return AptRequirement.simple(package)
|
||||
return find_reqs_simple(apt_mgr, [classpath])
|
||||
|
||||
|
||||
def resolve_haskell_package_req(apt_mgr, req):
|
||||
path = "/var/lib/ghc/package\\.conf\\.d/%s-.*\\.conf" % re.escape(req.deps[0][0])
|
||||
pkg_name = apt_mgr.get_package_for_paths([path], regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path], regex=True)
|
||||
|
||||
|
||||
def resolve_maven_artifact_req(apt_mgr, req):
|
||||
|
@ -440,10 +396,7 @@ def resolve_maven_artifact_req(apt_mgr, req):
|
|||
"%s-%s.%s" % (artifact_id, version, kind),
|
||||
)
|
||||
]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=regex)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=regex)
|
||||
|
||||
|
||||
def resolve_gnome_common_req(apt_mgr, req):
|
||||
|
@ -452,10 +405,7 @@ def resolve_gnome_common_req(apt_mgr, req):
|
|||
|
||||
def resolve_jdk_file_req(apt_mgr, req):
|
||||
path = re.escape(req.jdk_path) + ".*/" + re.escape(req.filename)
|
||||
pkg_name = apt_mgr.get_package_for_paths([path], regex=True)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path], regex=True)
|
||||
|
||||
|
||||
def resolve_jdk_req(apt_mgr, req):
|
||||
|
@ -478,17 +428,11 @@ def resolve_perl_module_req(apt_mgr, req):
|
|||
paths = [req.filename]
|
||||
else:
|
||||
paths = [posixpath.join(inc, req.filename) for inc in req.inc]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=False)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, paths, regex=False)
|
||||
|
||||
|
||||
def resolve_perl_file_req(apt_mgr, req):
|
||||
pkg_name = apt_mgr.get_package_for_paths([req.filename], regex=False)
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [req.filename], regex=False)
|
||||
|
||||
|
||||
def _find_aclocal_fun(macro):
|
||||
|
@ -512,10 +456,7 @@ def resolve_autoconf_macro_req(apt_mgr, req):
|
|||
except KeyError:
|
||||
logging.info("No local m4 file found defining %s", req.macro)
|
||||
return None
|
||||
pkg_name = apt_mgr.get_package_for_paths([path])
|
||||
if pkg_name is not None:
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return None
|
||||
return find_reqs_simple(apt_mgr, [path])
|
||||
|
||||
|
||||
def resolve_python_module_req(apt_mgr, req):
|
||||
|
@ -547,10 +488,7 @@ def resolve_python_package_req(apt_mgr, req):
|
|||
def resolve_cargo_crate_req(apt_mgr, req):
|
||||
paths = [
|
||||
'/usr/share/cargo/registry/%s-[0-9]+.*/Cargo.toml' % re.escape(req.crate)]
|
||||
pkg_name = apt_mgr.get_package_for_paths(paths, regex=True)
|
||||
if pkg_name is None:
|
||||
return None
|
||||
return AptRequirement.simple(pkg_name)
|
||||
return find_reqs_simple(apt_mgr, paths, regex=True)
|
||||
|
||||
|
||||
def resolve_ca_req(apt_mgr, req):
|
||||
|
@ -591,16 +529,24 @@ APT_REQUIREMENT_RESOLVERS = [
|
|||
]
|
||||
|
||||
|
||||
def resolve_requirement_apt(apt_mgr, req: Requirement) -> AptRequirement:
|
||||
def resolve_requirement_apt(apt_mgr, req: Requirement) -> List[AptRequirement]:
|
||||
for rr_class, rr_fn in APT_REQUIREMENT_RESOLVERS:
|
||||
if isinstance(req, rr_class):
|
||||
return rr_fn(apt_mgr, req)
|
||||
ret = rr_fn(apt_mgr, req)
|
||||
if not ret:
|
||||
return []
|
||||
if not isinstance(ret, list):
|
||||
raise TypeError(ret)
|
||||
return ret
|
||||
raise NotImplementedError(type(req))
|
||||
|
||||
|
||||
class AptResolver(Resolver):
|
||||
def __init__(self, apt):
|
||||
def __init__(self, apt, tie_breakers=None):
|
||||
self.apt = apt
|
||||
if tie_breakers is None:
|
||||
tie_breakers = []
|
||||
self.tie_breakers = tie_breakers
|
||||
|
||||
def __str__(self):
|
||||
return "apt"
|
||||
|
@ -647,4 +593,18 @@ class AptResolver(Resolver):
|
|||
yield (self.apt.satisfy_command([PkgRelation.str(chain(*[r.relations for o, r in apt_requirements]))]), [o for o, r in apt_requirements])
|
||||
|
||||
def resolve(self, req: Requirement):
|
||||
return resolve_requirement_apt(self.apt, req)
|
||||
ret = resolve_requirement_apt(self.apt, req)
|
||||
if not ret:
|
||||
return None
|
||||
if len(ret) == 1:
|
||||
return ret[0]
|
||||
for tie_breaker in self.tie_breakers:
|
||||
winner = tie_breaker(ret)
|
||||
if winner is not None:
|
||||
if not isinstance(winner, AptRequirement):
|
||||
raise TypeError(winner)
|
||||
return winner
|
||||
logging.info(
|
||||
'Unable to break tie over %r, picking first: %r',
|
||||
ret, ret[0])
|
||||
return ret[0]
|
||||
|
|
|
@ -37,6 +37,7 @@ from ..debian.fix_build import (
|
|||
apt_fixers,
|
||||
DebianPackagingContext,
|
||||
)
|
||||
from breezy.commit import NullCommitReporter
|
||||
from breezy.tests import TestCaseWithTransport
|
||||
|
||||
|
||||
|
@ -99,7 +100,8 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
apt._searchers = [DummyAptSearcher(self._apt_files)]
|
||||
context = DebianPackagingContext(
|
||||
self.tree, subpath="", committer="ognibuild <ognibuild@jelmer.uk>",
|
||||
update_changelog=True)
|
||||
update_changelog=True,
|
||||
commit_reporter=NullCommitReporter())
|
||||
fixers = versioned_package_fixers(session, context) + apt_fixers(apt, context)
|
||||
return resolve_error(error, ("build", ), fixers)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue