More refactoring.

This commit is contained in:
Jelmer Vernooij 2021-03-02 02:18:36 +00:00
parent ea32f33f90
commit 52e119022b
No known key found for this signature in database
GPG key ID: 579C160D4C9E23E8
10 changed files with 135 additions and 66 deletions

View file

@ -67,8 +67,8 @@ STAGE_MAP = {
def determine_fixers(session, resolver): def determine_fixers(session, resolver):
from .buildlog import RequirementFixer from .buildlog import InstallFixer
return [RequirementFixer(resolver)] return [InstallFixer(resolver)]
def main(): # noqa: C901 def main(): # noqa: C901

View file

@ -81,6 +81,7 @@ from .requirements import (
PythonModuleRequirement, PythonModuleRequirement,
PythonPackageRequirement, PythonPackageRequirement,
) )
from .resolver import UnsatisfiedRequirements
def problem_to_upstream_requirement(problem): # noqa: C901 def problem_to_upstream_requirement(problem): # noqa: C901
@ -166,7 +167,7 @@ def problem_to_upstream_requirement(problem): # noqa: C901
return None return None
class RequirementFixer(BuildFixer): class InstallFixer(BuildFixer):
def __init__(self, resolver): def __init__(self, resolver):
self.resolver = resolver self.resolver = resolver
@ -188,11 +189,8 @@ class RequirementFixer(BuildFixer):
if not isinstance(reqs, list): if not isinstance(reqs, list):
reqs = [reqs] reqs = [reqs]
changed = False try:
for req in reqs: self.resolver.install(reqs)
package = self.resolver.resolve(req) except UnsatisfiedRequirements:
if package is None: return False
return False return True
if context.add_dependency(package):
changed = True
return changed

View file

@ -457,7 +457,7 @@ class Make(BuildSystem):
run_with_build_fixers(session, ["autoreconf", "-i"], fixers) run_with_build_fixers(session, ["autoreconf", "-i"], fixers)
if not makefile_exists() and session.exists("configure"): if not makefile_exists() and session.exists("configure"):
session.check_call(["./configure"]) run_with_build_fixers(session, ["./configure"], fixers)
def build(self, session, resolver, fixers): def build(self, session, resolver, fixers):
self.setup(session, resolver, fixers) self.setup(session, resolver, fixers)
@ -553,9 +553,14 @@ class Cargo(BuildSystem):
name = "cargo" name = "cargo"
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.path)
def __init__(self, path): def __init__(self, path):
from toml.decoder import load from toml.decoder import load
self.path = path
with open(path, "r") as f: with open(path, "r") as f:
self.cargo = load(f) self.cargo = load(f)
@ -568,12 +573,30 @@ class Cargo(BuildSystem):
def test(self, session, resolver, fixers): def test(self, session, resolver, fixers):
run_with_build_fixers(session, ["cargo", "test"], fixers) run_with_build_fixers(session, ["cargo", "test"], fixers)
def clean(self, session, resolver, fixers):
run_with_build_fixers(session, ["cargo", "clean"], fixers)
def build(self, session, resolver, fixers):
run_with_build_fixers(session, ["cargo", "build"], fixers)
class Golang(BuildSystem): class Golang(BuildSystem):
"""Go builds.""" """Go builds."""
name = "golang" name = "golang"
def __repr__(self):
return "%s()" % (type(self).__name__)
def test(self, session, resolver, fixers):
session.check_call(["go", "test"])
def build(self, session, resolver, fixers):
session.check_call(["go", "build"])
def clean(self, session, resolver, fixers):
session.check_call(["go", "clean"])
class Maven(BuildSystem): class Maven(BuildSystem):
@ -664,6 +687,7 @@ def detect_buildsystems(path, trust_package=False): # noqa: C901
"GNUmakefile", "GNUmakefile",
"makefile", "makefile",
"Makefile.PL", "Makefile.PL",
"CMakeLists.txt",
"autogen.sh", "autogen.sh",
"configure.ac", "configure.ac",
"configure.in", "configure.in",
@ -672,6 +696,7 @@ def detect_buildsystems(path, trust_package=False): # noqa: C901
): ):
yield Make() yield Make()
seen_golang = False
if os.path.exists(os.path.join(path, ".travis.yml")): if os.path.exists(os.path.join(path, ".travis.yml")):
import ruamel.yaml.reader import ruamel.yaml.reader
@ -684,11 +709,13 @@ def detect_buildsystems(path, trust_package=False): # noqa: C901
language = data.get("language") language = data.get("language")
if language == "go": if language == "go":
yield Golang() yield Golang()
seen_golang = True
for entry in os.scandir(path): if not seen_golang:
if entry.name.endswith(".go"): for entry in os.scandir(path):
yield Golang() if entry.name.endswith(".go"):
break yield Golang()
break
def get_buildsystem(path, trust_package=False): def get_buildsystem(path, trust_package=False):

View file

@ -79,8 +79,8 @@ from buildlog_consultant.sbuild import (
SbuildFailure, SbuildFailure,
) )
from ..buildlog import problem_to_upstream_requirement
from ..fix_build import BuildFixer, resolve_error, DependencyContext from ..fix_build import BuildFixer, resolve_error, DependencyContext
from ..buildlog import RequirementFixer
from ..resolver.apt import ( from ..resolver.apt import (
AptRequirement, AptRequirement,
get_package_for_python_module, get_package_for_python_module,
@ -98,7 +98,66 @@ class CircularDependency(Exception):
self.package = package self.package = package
class PackageDependencyFixer(BuildFixer):
def __init__(self, apt_resolver):
self.apt_resolver = apt_resolver
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.apt_resolver)
def __str__(self):
return "upstream requirement fixer(%s)" % self.apt_resolver
def can_fix(self, error):
req = problem_to_upstream_requirement(error)
return req is not None
def fix(self, error, context):
reqs = problem_to_upstream_requirement(error)
if reqs is None:
return False
if not isinstance(reqs, list):
reqs = [reqs]
changed = False
for req in reqs:
package = self.apt_resolver.resolve(req)
if package is None:
return False
if context.phase[0] == "autopkgtest":
return add_test_dependency(
context.tree,
context.phase[1],
package,
committer=context.committer,
subpath=context.subpath,
update_changelog=context.update_changelog,
)
elif context.phase[0] == "build":
return add_build_dependency(
context.tree,
package,
committer=context.committer,
subpath=context.subpath,
update_changelog=context.update_changelog,
)
else:
logging.warning('Unknown phase %r', context.phase)
return False
return changed
class BuildDependencyContext(DependencyContext): class BuildDependencyContext(DependencyContext):
def __init__(
self, phase, tree, apt, subpath="", committer=None, update_changelog=True
):
self.phase = phase
super(BuildDependencyContext, self).__init__(
tree, apt, subpath, committer, update_changelog
)
def add_dependency(self, requirement: AptRequirement): def add_dependency(self, requirement: AptRequirement):
return add_build_dependency( return add_build_dependency(
self.tree, self.tree,
@ -111,9 +170,9 @@ class BuildDependencyContext(DependencyContext):
class AutopkgtestDependencyContext(DependencyContext): class AutopkgtestDependencyContext(DependencyContext):
def __init__( def __init__(
self, testname, tree, apt, subpath="", committer=None, update_changelog=True self, phase, tree, apt, subpath="", committer=None, update_changelog=True
): ):
self.testname = testname self.phase = phase
super(AutopkgtestDependencyContext, self).__init__( super(AutopkgtestDependencyContext, self).__init__(
tree, apt, subpath, committer, update_changelog tree, apt, subpath, committer, update_changelog
) )
@ -498,13 +557,12 @@ def versioned_package_fixers(session):
def apt_fixers(apt) -> List[BuildFixer]: def apt_fixers(apt) -> List[BuildFixer]:
from ..resolver.apt import AptResolver from ..resolver.apt import AptResolver
resolver = AptResolver(apt) resolver = AptResolver(apt)
return [ return [
SimpleBuildFixer(MissingPythonModule, fix_missing_python_module), SimpleBuildFixer(MissingPythonModule, fix_missing_python_module),
SimpleBuildFixer(MissingPythonDistribution, fix_missing_python_distribution), SimpleBuildFixer(MissingPythonDistribution, fix_missing_python_distribution),
SimpleBuildFixer(AptFetchFailure, retry_apt_failure), SimpleBuildFixer(AptFetchFailure, retry_apt_failure),
RequirementFixer(resolver), PackageDependencyFixer(resolver),
] ]
@ -553,6 +611,7 @@ def build_incrementally(
reset_tree(local_tree, local_tree.basis_tree(), subpath=subpath) reset_tree(local_tree, local_tree.basis_tree(), subpath=subpath)
if e.phase[0] == "build": if e.phase[0] == "build":
context = BuildDependencyContext( context = BuildDependencyContext(
e.phase,
local_tree, local_tree,
apt, apt,
subpath=subpath, subpath=subpath,
@ -561,7 +620,7 @@ def build_incrementally(
) )
elif e.phase[0] == "autopkgtest": elif e.phase[0] == "autopkgtest":
context = AutopkgtestDependencyContext( context = AutopkgtestDependencyContext(
e.phase[1], e.phase,
local_tree, local_tree,
apt, apt,
subpath=subpath, subpath=subpath,

View file

@ -133,7 +133,7 @@ def create_dist_schroot(
) -> str: ) -> str:
from .buildsystem import detect_buildsystems from .buildsystem import detect_buildsystems
from .resolver.apt import AptResolver from .resolver.apt import AptResolver
from .buildlog import RequirementFixer from .buildlog import InstallFixer
if subdir is None: if subdir is None:
subdir = "package" subdir = "package"
@ -159,7 +159,7 @@ def create_dist_schroot(
buildsystems = list(detect_buildsystems(export_directory)) buildsystems = list(detect_buildsystems(export_directory))
resolver = AptResolver.from_session(session) resolver = AptResolver.from_session(session)
fixers = [RequirementFixer(resolver)] fixers = [InstallFixer(resolver)]
with DistCatcher(export_directory) as dc: with DistCatcher(export_directory) as dc:
oldcwd = os.getcwd() oldcwd = os.getcwd()

View file

@ -58,23 +58,10 @@ class DependencyContext(object):
self.committer = committer self.committer = committer
self.update_changelog = update_changelog self.update_changelog = update_changelog
def add_dependency( def add_dependency(self, package) -> bool:
self, package: str, minimum_version=None
) -> bool:
raise NotImplementedError(self.add_dependency) raise NotImplementedError(self.add_dependency)
class SchrootDependencyContext(DependencyContext):
def __init__(self, session):
self.session = session
self.apt = AptManager(session)
def add_dependency(self, package, minimum_version=None):
# TODO(jelmer): Handle minimum_version
self.apt.install([package])
return True
def run_with_build_fixers(session: Session, args: List[str], fixers: List[BuildFixer]): def run_with_build_fixers(session: Session, args: List[str], fixers: List[BuildFixer]):
logging.info("Running %r", args) logging.info("Running %r", args)
fixed_errors = [] fixed_errors = []
@ -99,7 +86,7 @@ def run_with_build_fixers(session: Session, args: List[str], fixers: List[BuildF
raise DetailedFailure(retcode, args, error) raise DetailedFailure(retcode, args, error)
if not resolve_error( if not resolve_error(
error, error,
SchrootDependencyContext(session), None,
fixers=fixers, fixers=fixers,
): ):
logging.warning("Failed to find resolution for error %r. Giving up.", error) logging.warning("Failed to find resolution for error %r. Giving up.", error)

View file

@ -98,6 +98,15 @@ class CargoCrateRequirement(Requirement):
super(CargoCrateRequirement, self).__init__("cargo-crate") super(CargoCrateRequirement, self).__init__("cargo-crate")
self.crate = crate self.crate = crate
def __repr__(self):
return "%s(%r)" % (
type(self).__name__,
self.crate,
)
def __str__(self):
return "cargo crate: %s" % self.crate
class PkgConfigRequirement(Requirement): class PkgConfigRequirement(Requirement):

View file

@ -42,6 +42,9 @@ class CPANResolver(Resolver):
def __str__(self): def __str__(self):
return "cpan" return "cpan"
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.session)
def install(self, requirements): def install(self, requirements):
from ..requirements import PerlModuleRequirement from ..requirements import PerlModuleRequirement
@ -70,6 +73,9 @@ class HackageResolver(Resolver):
def __str__(self): def __str__(self):
return "hackage" return "hackage"
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.session)
def install(self, requirements): def install(self, requirements):
from ..requirements import HaskellPackageRequirement from ..requirements import HaskellPackageRequirement
@ -88,31 +94,6 @@ class HackageResolver(Resolver):
raise NotImplementedError(self.explain) raise NotImplementedError(self.explain)
class CargoResolver(Resolver):
def __init__(self, session):
self.session = session
def __str__(self):
return "cargo"
def install(self, requirements):
from ..requirements import CargoCrateRequirement
missing = []
for requirement in requirements:
if not isinstance(requirement, CargoCrateRequirement):
missing.append(requirement)
continue
self.session.check_call(
["cargo", "install", requirement.crate], user="root"
)
if missing:
raise UnsatisfiedRequirements(missing)
def explain(self, requirements):
raise NotImplementedError(self.explain)
class PypiResolver(Resolver): class PypiResolver(Resolver):
def __init__(self, session): def __init__(self, session):
self.session = session self.session = session
@ -120,6 +101,9 @@ class PypiResolver(Resolver):
def __str__(self): def __str__(self):
return "pypi" return "pypi"
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.session)
def install(self, requirements): def install(self, requirements):
from ..requirements import PythonPackageRequirement from ..requirements import PythonPackageRequirement
@ -148,6 +132,9 @@ class NpmResolver(Resolver):
def __str__(self): def __str__(self):
return "npm" return "npm"
def __repr__(self):
return "%s(%r)" % (type(self).__name__, self.session)
def install(self, requirements): def install(self, requirements):
from ..requirements import NodePackageRequirement from ..requirements import NodePackageRequirement
@ -195,7 +182,6 @@ def native_resolvers(session):
CPANResolver(session), CPANResolver(session),
PypiResolver(session), PypiResolver(session),
NpmResolver(session), NpmResolver(session),
CargoResolver(session),
HackageResolver(session), HackageResolver(session),
] ]
) )
@ -227,7 +213,6 @@ def auto_resolver(session):
CPANResolver(session), CPANResolver(session),
PypiResolver(session), PypiResolver(session),
NpmResolver(session), NpmResolver(session),
CargoResolver(session),
HackageResolver(session), HackageResolver(session),
] ]
) )

View file

@ -27,6 +27,9 @@ class PlainSession(Session):
location = "/" location = "/"
def __repr__(self):
return "%s()" % (type(self).__name__, )
def create_home(self): def create_home(self):
pass pass

View file

@ -98,6 +98,7 @@ blah (0.1) UNRELEASED; urgency=medium
apt = AptManager(session) apt = AptManager(session)
apt._searchers = [DummyAptSearcher(self._apt_files)] apt._searchers = [DummyAptSearcher(self._apt_files)]
context = BuildDependencyContext( context = BuildDependencyContext(
("build", ),
self.tree, self.tree,
apt, apt,
subpath="", subpath="",