More refactoring.
This commit is contained in:
parent
8a7ad4fdd8
commit
c184e01aef
9 changed files with 197 additions and 163 deletions
|
@ -13,14 +13,16 @@ requirements are met.
|
||||||
|
|
||||||
Then we attempt to build.
|
Then we attempt to build.
|
||||||
|
|
||||||
If any problems are found in the log, buildlog-consultant will report them.
|
If any Problems are found in the log, buildlog-consultant will report them.
|
||||||
|
|
||||||
ognibuild can then invoke "fixers" to address Problems.
|
ognibuild can then invoke "fixers" to address Problems. Fixers can do things
|
||||||
|
like e.g. upgrade configure.ac to a newer version, or invoke autoreconf.
|
||||||
|
|
||||||
|
A list of possible fixers can be provided. Each fixer will be called
|
||||||
|
(in order) until one of them claims to ahve fixed the issue.
|
||||||
|
|
||||||
Problems can be converted to UpstreamRequirements by UpstreamRequirementFixer
|
Problems can be converted to UpstreamRequirements by UpstreamRequirementFixer
|
||||||
|
|
||||||
Other Fixer can do things like e.g. upgrade configure.ac to a newer version.
|
|
||||||
|
|
||||||
UpstreamRequirementFixer uses a UpstreamRequirementResolver object that
|
UpstreamRequirementFixer uses a UpstreamRequirementResolver object that
|
||||||
can translate UpstreamRequirement objects into apt package names or
|
can translate UpstreamRequirement objects into apt package names or
|
||||||
e.g. cpan commands.
|
e.g. cpan commands.
|
||||||
|
@ -28,3 +30,22 @@ e.g. cpan commands.
|
||||||
ognibuild keeps finding problems, resolving them and rebuilding until it finds
|
ognibuild keeps finding problems, resolving them and rebuilding until it finds
|
||||||
a problem it can not resolve or that it thinks it has already resolved
|
a problem it can not resolve or that it thinks it has already resolved
|
||||||
(i.e. seen before).
|
(i.e. seen before).
|
||||||
|
|
||||||
|
Operations are run in a Session - this can represent a virtualized
|
||||||
|
environment of some sort (e.g. a chroot or virtualenv) or simply
|
||||||
|
on the host machine.
|
||||||
|
|
||||||
|
For e.g. PerlModuleRequirement, need to be able to:
|
||||||
|
|
||||||
|
* install from apt package
|
||||||
|
+ DebianInstallFixer(AptResolver()).fix(problem)
|
||||||
|
* update debian package (source, runtime, test) deps to include apt package
|
||||||
|
+ DebianPackageDepFixer(AptResolver()).fix(problem, ('test', 'foo'))
|
||||||
|
* suggest command to run to install from apt package
|
||||||
|
+ DebianInstallFixer(AptResolver()).command(problem)
|
||||||
|
* install from cpan
|
||||||
|
+ CpanInstallFixer().fix(problem)
|
||||||
|
* suggest command to run to install from cpan package
|
||||||
|
+ CpanInstallFixer().command(problem)
|
||||||
|
* update source package reqs to depend on perl module
|
||||||
|
+ PerlDepFixer().fix(problem)
|
||||||
|
|
|
@ -28,6 +28,15 @@ class DetailedFailure(Exception):
|
||||||
self.error = error
|
self.error = error
|
||||||
|
|
||||||
|
|
||||||
|
class UnidentifiedError(Exception):
|
||||||
|
|
||||||
|
def __init__(self, retcode, argv, lines, secondary=None):
|
||||||
|
self.retcode = retcode
|
||||||
|
self.argv = argv
|
||||||
|
self.lines = lines
|
||||||
|
self.secondary = secondary
|
||||||
|
|
||||||
|
|
||||||
def shebang_binary(p):
|
def shebang_binary(p):
|
||||||
if not (os.stat(p).st_mode & stat.S_IEXEC):
|
if not (os.stat(p).st_mode & stat.S_IEXEC):
|
||||||
return None
|
return None
|
||||||
|
@ -49,6 +58,9 @@ class UpstreamRequirement(object):
|
||||||
def __init__(self, family):
|
def __init__(self, family):
|
||||||
self.family = family
|
self.family = family
|
||||||
|
|
||||||
|
def possible_paths(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class UpstreamOutput(object):
|
class UpstreamOutput(object):
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from .apt import UnidentifiedError
|
from . import UnidentifiedError
|
||||||
from .buildsystem import NoBuildToolsFound, detect_buildsystems
|
from .buildsystem import NoBuildToolsFound, detect_buildsystems
|
||||||
from .build import run_build
|
from .build import run_build
|
||||||
from .clean import run_clean
|
from .clean import run_clean
|
||||||
|
@ -127,7 +127,8 @@ def main(): # noqa: C901
|
||||||
return 1
|
return 1
|
||||||
except MissingDependencies as e:
|
except MissingDependencies as e:
|
||||||
for req in e.requirements:
|
for req in e.requirements:
|
||||||
logging.info("Missing dependency (%s:%s)", (req.family, req.name))
|
logging.info("Missing dependency (%s:%s)",
|
||||||
|
req.family, req.name)
|
||||||
for resolver in [
|
for resolver in [
|
||||||
AptResolver.from_session(session),
|
AptResolver.from_session(session),
|
||||||
NativeResolver.from_session(session),
|
NativeResolver.from_session(session),
|
||||||
|
|
|
@ -22,7 +22,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from . import shebang_binary, UpstreamOutput
|
from . import shebang_binary, UpstreamOutput, UnidentifiedError
|
||||||
from .requirements import (
|
from .requirements import (
|
||||||
BinaryRequirement,
|
BinaryRequirement,
|
||||||
PythonPackageRequirement,
|
PythonPackageRequirement,
|
||||||
|
@ -30,7 +30,6 @@ from .requirements import (
|
||||||
NodePackageRequirement,
|
NodePackageRequirement,
|
||||||
CargoCrateRequirement,
|
CargoCrateRequirement,
|
||||||
)
|
)
|
||||||
from .apt import UnidentifiedError
|
|
||||||
from .fix_build import run_with_build_fixer
|
from .fix_build import run_with_build_fixer
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +135,10 @@ class SetupPy(BuildSystem):
|
||||||
self.setup(resolver)
|
self.setup(resolver)
|
||||||
self._run_setup(session, resolver, ["test"])
|
self._run_setup(session, resolver, ["test"])
|
||||||
|
|
||||||
|
def build(self, session, resolver):
|
||||||
|
self.setup(resolver)
|
||||||
|
self._run_setup(session, resolver, ["build"])
|
||||||
|
|
||||||
def dist(self, session, resolver):
|
def dist(self, session, resolver):
|
||||||
self.setup(resolver)
|
self.setup(resolver)
|
||||||
self._run_setup(session, resolver, ["sdist"])
|
self._run_setup(session, resolver, ["sdist"])
|
||||||
|
@ -370,6 +373,11 @@ class Make(BuildSystem):
|
||||||
if not session.exists("Makefile") and session.exists("configure"):
|
if not session.exists("Makefile") and session.exists("configure"):
|
||||||
session.check_call(["./configure"])
|
session.check_call(["./configure"])
|
||||||
|
|
||||||
|
def build(self, session, resolver):
|
||||||
|
self.setup(session, resolver)
|
||||||
|
resolver.install([BinaryRequirement("make")])
|
||||||
|
run_with_build_fixer(session, ["make", "all"])
|
||||||
|
|
||||||
def dist(self, session, resolver):
|
def dist(self, session, resolver):
|
||||||
self.setup(session, resolver)
|
self.setup(session, resolver)
|
||||||
resolver.install([BinaryRequirement("make")])
|
resolver.install([BinaryRequirement("make")])
|
||||||
|
|
|
@ -26,19 +26,10 @@ from buildlog_consultant.apt import (
|
||||||
)
|
)
|
||||||
from debian.deb822 import Release
|
from debian.deb822 import Release
|
||||||
|
|
||||||
from .. import DetailedFailure
|
from .. import DetailedFailure, UnidentifiedError
|
||||||
from ..session import Session, run_with_tee
|
from ..session import Session, run_with_tee
|
||||||
|
|
||||||
|
|
||||||
class UnidentifiedError(Exception):
|
|
||||||
|
|
||||||
def __init__(self, retcode, argv, lines, secondary=None):
|
|
||||||
self.retcode = retcode
|
|
||||||
self.argv = argv
|
|
||||||
self.lines = lines
|
|
||||||
self.secondary = secondary
|
|
||||||
|
|
||||||
|
|
||||||
def run_apt(session: Session, args: List[str]) -> None:
|
def run_apt(session: Session, args: List[str]) -> None:
|
||||||
"""Run apt."""
|
"""Run apt."""
|
||||||
args = ["apt", "-y"] + args
|
args = ["apt", "-y"] + args
|
||||||
|
|
|
@ -22,7 +22,7 @@ __all__ = [
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Callable, Type, Tuple, Set, Optional
|
from typing import List, Set, Optional
|
||||||
|
|
||||||
from debian.deb822 import (
|
from debian.deb822 import (
|
||||||
Deb822,
|
Deb822,
|
||||||
|
@ -105,10 +105,9 @@ from buildlog_consultant.sbuild import (
|
||||||
SbuildFailure,
|
SbuildFailure,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .apt import AptManager, LocalAptManager
|
from .apt import LocalAptManager
|
||||||
from ..fix_build import BuildFixer, SimpleBuildFixer
|
from ..fix_build import BuildFixer, SimpleBuildFixer, resolve_error, DependencyContext
|
||||||
from ..resolver.apt import (
|
from ..resolver.apt import (
|
||||||
AptResolver,
|
|
||||||
NoAptPackage,
|
NoAptPackage,
|
||||||
get_package_for_python_module,
|
get_package_for_python_module,
|
||||||
)
|
)
|
||||||
|
@ -151,31 +150,6 @@ class CircularDependency(Exception):
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
|
|
||||||
class DependencyContext(object):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
tree: MutableTree,
|
|
||||||
apt: AptManager,
|
|
||||||
subpath: str = "",
|
|
||||||
committer: Optional[str] = None,
|
|
||||||
update_changelog: bool = True,
|
|
||||||
):
|
|
||||||
self.tree = tree
|
|
||||||
self.apt = apt
|
|
||||||
self.resolver = AptResolver(apt)
|
|
||||||
self.subpath = subpath
|
|
||||||
self.committer = committer
|
|
||||||
self.update_changelog = update_changelog
|
|
||||||
|
|
||||||
def resolve_apt(self, req):
|
|
||||||
return self.resolver.resolve(req)
|
|
||||||
|
|
||||||
def add_dependency(
|
|
||||||
self, package: str, minimum_version: Optional[Version] = None
|
|
||||||
) -> bool:
|
|
||||||
raise NotImplementedError(self.add_dependency)
|
|
||||||
|
|
||||||
|
|
||||||
class BuildDependencyContext(DependencyContext):
|
class BuildDependencyContext(DependencyContext):
|
||||||
def add_dependency(self, package: str, minimum_version: Optional[Version] = None):
|
def add_dependency(self, package: str, minimum_version: Optional[Version] = None):
|
||||||
return add_build_dependency(
|
return add_build_dependency(
|
||||||
|
@ -462,90 +436,95 @@ def fix_missing_python_module(error, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def fix_missing_requirement(error, context):
|
def problem_to_upstream_requirement(problem, context):
|
||||||
if isinstance(error, MissingFile):
|
if isinstance(problem, MissingFile):
|
||||||
req = PathRequirement(error.path)
|
return PathRequirement(problem.path)
|
||||||
elif isinstance(error, MissingCommand):
|
elif isinstance(problem, MissingCommand):
|
||||||
req = BinaryRequirement(error.command)
|
return BinaryRequirement(problem.command)
|
||||||
elif isinstance(error, MissingPkgConfig):
|
elif isinstance(problem, MissingPkgConfig):
|
||||||
req = PkgConfigRequirement(
|
return PkgConfigRequirement(
|
||||||
error.module, error.minimum_version)
|
problem.module, problem.minimum_version)
|
||||||
elif isinstance(error, MissingCHeader):
|
elif isinstance(problem, MissingCHeader):
|
||||||
req = CHeaderRequirement(error.header)
|
return CHeaderRequirement(problem.header)
|
||||||
elif isinstance(error, MissingJavaScriptRuntime):
|
elif isinstance(problem, MissingJavaScriptRuntime):
|
||||||
req = JavaScriptRuntimeRequirement()
|
return JavaScriptRuntimeRequirement()
|
||||||
elif isinstance(error, MissingRubyGem):
|
elif isinstance(problem, MissingRubyGem):
|
||||||
req = RubyGemRequirement(error.gem, error.version)
|
return RubyGemRequirement(problem.gem, problem.version)
|
||||||
elif isinstance(error, MissingValaPackage):
|
elif isinstance(problem, MissingValaPackage):
|
||||||
req = ValaPackageRequirement(error.package)
|
return ValaPackageRequirement(problem.package)
|
||||||
elif isinstance(error, MissingGoPackage):
|
elif isinstance(problem, MissingGoPackage):
|
||||||
req = GoPackageRequirement(error.package)
|
return GoPackageRequirement(problem.package)
|
||||||
elif isinstance(error, DhAddonLoadFailure):
|
elif isinstance(problem, DhAddonLoadFailure):
|
||||||
req = DhAddonRequirement(error.path)
|
return DhAddonRequirement(problem.path)
|
||||||
elif isinstance(error, MissingPhpClass):
|
elif isinstance(problem, MissingPhpClass):
|
||||||
req = PhpClassRequirement(error.php_class)
|
return PhpClassRequirement(problem.php_class)
|
||||||
elif isinstance(error, MissingRPackage):
|
elif isinstance(problem, MissingRPackage):
|
||||||
req = RPackageRequirement(error.package, error.minimum_version)
|
return RPackageRequirement(problem.package, problem.minimum_version)
|
||||||
elif isinstance(error, MissingNodeModule):
|
elif isinstance(problem, MissingNodeModule):
|
||||||
req = NodePackageRequirement(error.module)
|
return NodePackageRequirement(problem.module)
|
||||||
elif isinstance(error, MissingLibrary):
|
elif isinstance(problem, MissingLibrary):
|
||||||
req = LibraryRequirement(error.library)
|
return LibraryRequirement(problem.library)
|
||||||
elif isinstance(error, MissingRubyFile):
|
elif isinstance(problem, MissingRubyFile):
|
||||||
req = RubyFileRequirement(error.filename)
|
return RubyFileRequirement(problem.filename)
|
||||||
elif isinstance(error, MissingXmlEntity):
|
elif isinstance(problem, MissingXmlEntity):
|
||||||
req = XmlEntityRequirement(error.url)
|
return XmlEntityRequirement(problem.url)
|
||||||
elif isinstance(error, MissingSprocketsFile):
|
elif isinstance(problem, MissingSprocketsFile):
|
||||||
req = SprocketsFileRequirement(error.content_type, error.name)
|
return SprocketsFileRequirement(problem.content_type, problem.name)
|
||||||
elif isinstance(error, MissingJavaClass):
|
elif isinstance(problem, MissingJavaClass):
|
||||||
req = JavaClassRequirement(error.classname)
|
return JavaClassRequirement(problem.classname)
|
||||||
elif isinstance(error, MissingHaskellDependencies):
|
elif isinstance(problem, MissingHaskellDependencies):
|
||||||
# TODO(jelmer): Create multiple HaskellPackageRequirement objects?
|
# TODO(jelmer): Create multiple HaskellPackageRequirement objects?
|
||||||
req = HaskellPackageRequirement(error.package)
|
return HaskellPackageRequirement(problem.package)
|
||||||
elif isinstance(error, MissingMavenArtifacts):
|
elif isinstance(problem, MissingMavenArtifacts):
|
||||||
# TODO(jelmer): Create multiple MavenArtifactRequirement objects?
|
# TODO(jelmer): Create multiple MavenArtifactRequirement objects?
|
||||||
req = MavenArtifactRequirement(error.artifacts)
|
return MavenArtifactRequirement(problem.artifacts)
|
||||||
elif isinstance(error, MissingCSharpCompiler):
|
elif isinstance(problem, MissingCSharpCompiler):
|
||||||
req = BinaryRequirement('msc')
|
return BinaryRequirement('msc')
|
||||||
elif isinstance(error, GnomeCommonMissing):
|
elif isinstance(problem, GnomeCommonMissing):
|
||||||
req = GnomeCommonRequirement()
|
return GnomeCommonRequirement()
|
||||||
elif isinstance(error, MissingJDKFile):
|
elif isinstance(problem, MissingJDKFile):
|
||||||
req = JDKFileRequirement(error.jdk_path, error.filename)
|
return JDKFileRequirement(problem.jdk_path, problem.filename)
|
||||||
elif isinstance(error, MissingGnomeCommonDependency):
|
elif isinstance(problem, MissingGnomeCommonDependency):
|
||||||
if error.package == "glib-gettext":
|
if problem.package == "glib-gettext":
|
||||||
req = BinaryRequirement('glib-gettextize')
|
return BinaryRequirement('glib-gettextize')
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"No known command for gnome-common dependency %s",
|
"No known command for gnome-common dependency %s",
|
||||||
error.package)
|
problem.package)
|
||||||
return None
|
return None
|
||||||
elif isinstance(error, MissingXfceDependency):
|
elif isinstance(problem, MissingXfceDependency):
|
||||||
if error.package == "gtk-doc":
|
if problem.package == "gtk-doc":
|
||||||
req = BinaryRequirement("gtkdocize")
|
return BinaryRequirement("gtkdocize")
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"No known command for xfce dependency %s",
|
"No known command for xfce dependency %s",
|
||||||
error.package)
|
problem.package)
|
||||||
return None
|
return None
|
||||||
elif isinstance(error, MissingPerlModule):
|
elif isinstance(problem, MissingPerlModule):
|
||||||
req = PerlModuleRequirement(
|
return PerlModuleRequirement(
|
||||||
module=error.module,
|
module=problem.module,
|
||||||
filename=error.filename,
|
filename=problem.filename,
|
||||||
inc=error.inc)
|
inc=problem.inc)
|
||||||
elif isinstance(error, MissingPerlFile):
|
elif isinstance(problem, MissingPerlFile):
|
||||||
req = PerlFileRequirement(filename=error.filename)
|
return PerlFileRequirement(filename=problem.filename)
|
||||||
elif isinstance(error, MissingAutoconfMacro):
|
elif isinstance(problem, MissingAutoconfMacro):
|
||||||
req = AutoconfMacroRequirement(error.macro)
|
return AutoconfMacroRequirement(problem.macro)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
|
||||||
package = context.resolve_apt(req)
|
|
||||||
except NoAptPackage:
|
|
||||||
return False
|
|
||||||
return context.add_dependency(package)
|
|
||||||
|
|
||||||
|
class UpstreamRequirementFixer(BuildFixer):
|
||||||
|
|
||||||
DEFAULT_PERL_PATHS = ["/usr/share/perl5"]
|
def fix_missing_requirement(self, error, context):
|
||||||
|
req = problem_to_upstream_requirement(error)
|
||||||
|
if req is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
package = context.resolver.resolve(req)
|
||||||
|
except NoAptPackage:
|
||||||
|
return False
|
||||||
|
return context.add_dependency(package)
|
||||||
|
|
||||||
|
|
||||||
def retry_apt_failure(error, context):
|
def retry_apt_failure(error, context):
|
||||||
|
@ -646,6 +625,7 @@ VERSIONED_PACKAGE_FIXERS: List[BuildFixer] = [
|
||||||
NeedPgBuildExtUpdateControl, run_pgbuildext_updatecontrol),
|
NeedPgBuildExtUpdateControl, run_pgbuildext_updatecontrol),
|
||||||
SimpleBuildFixer(MissingConfigure, fix_missing_configure),
|
SimpleBuildFixer(MissingConfigure, fix_missing_configure),
|
||||||
SimpleBuildFixer(MissingAutomakeInput, fix_missing_automake_input),
|
SimpleBuildFixer(MissingAutomakeInput, fix_missing_automake_input),
|
||||||
|
SimpleBuildFixer(MissingConfigStatusInput, fix_missing_config_status_input),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -653,36 +633,15 @@ APT_FIXERS: List[BuildFixer] = [
|
||||||
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),
|
||||||
SimpleBuildFixer(MissingPerlFile, fix_missing_makefile_pl),
|
UpstreamRequirementFixer(),
|
||||||
SimpleBuildFixer(Problem, fix_missing_requirement),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
GENERIC_FIXERS: List[BuildFixer] = [
|
GENERIC_FIXERS: List[BuildFixer] = [
|
||||||
SimpleBuildFixer(MissingConfigStatusInput, fix_missing_config_status_input),
|
SimpleBuildFixer(MissingPerlFile, fix_missing_makefile_pl),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def resolve_error(error, context, fixers):
|
|
||||||
relevant_fixers = []
|
|
||||||
for error_cls, fixer in fixers:
|
|
||||||
if isinstance(error, error_cls):
|
|
||||||
relevant_fixers.append(fixer)
|
|
||||||
if not relevant_fixers:
|
|
||||||
logging.warning("No fixer found for %r", error)
|
|
||||||
return False
|
|
||||||
for fixer in relevant_fixers:
|
|
||||||
logging.info("Attempting to use fixer %r to address %r", fixer, error)
|
|
||||||
try:
|
|
||||||
made_changes = fixer(error, context)
|
|
||||||
except GeneratedFile:
|
|
||||||
logging.warning("Control file is generated, unable to edit.")
|
|
||||||
return False
|
|
||||||
if made_changes:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def build_incrementally(
|
def build_incrementally(
|
||||||
local_tree,
|
local_tree,
|
||||||
apt,
|
apt,
|
||||||
|
@ -714,17 +673,17 @@ def build_incrementally(
|
||||||
if e.error is None:
|
if e.error is None:
|
||||||
logging.warning("Build failed with unidentified error. Giving up.")
|
logging.warning("Build failed with unidentified error. Giving up.")
|
||||||
raise
|
raise
|
||||||
if e.context is None:
|
if e.phase is None:
|
||||||
logging.info("No relevant context, not making any changes.")
|
logging.info("No relevant context, not making any changes.")
|
||||||
raise
|
raise
|
||||||
if (e.error, e.context) in fixed_errors:
|
if (e.error, e.phase) in fixed_errors:
|
||||||
logging.warning("Error was still not fixed on second try. Giving up.")
|
logging.warning("Error was still not fixed on second try. Giving up.")
|
||||||
raise
|
raise
|
||||||
if max_iterations is not None and len(fixed_errors) > max_iterations:
|
if max_iterations is not None and len(fixed_errors) > max_iterations:
|
||||||
logging.warning("Last fix did not address the issue. Giving up.")
|
logging.warning("Last fix did not address the issue. Giving up.")
|
||||||
raise
|
raise
|
||||||
reset_tree(local_tree, local_tree.basis_tree(), subpath=subpath)
|
reset_tree(local_tree, local_tree.basis_tree(), subpath=subpath)
|
||||||
if e.context[0] == "build":
|
if e.phase[0] == "build":
|
||||||
context = BuildDependencyContext(
|
context = BuildDependencyContext(
|
||||||
local_tree,
|
local_tree,
|
||||||
apt,
|
apt,
|
||||||
|
@ -732,9 +691,9 @@ def build_incrementally(
|
||||||
committer=committer,
|
committer=committer,
|
||||||
update_changelog=update_changelog,
|
update_changelog=update_changelog,
|
||||||
)
|
)
|
||||||
elif e.context[0] == "autopkgtest":
|
elif e.phase[0] == "autopkgtest":
|
||||||
context = AutopkgtestDependencyContext(
|
context = AutopkgtestDependencyContext(
|
||||||
e.context[1],
|
e.phase[1],
|
||||||
local_tree,
|
local_tree,
|
||||||
apt,
|
apt,
|
||||||
subpath=subpath,
|
subpath=subpath,
|
||||||
|
@ -742,7 +701,7 @@ def build_incrementally(
|
||||||
update_changelog=update_changelog,
|
update_changelog=update_changelog,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logging.warning("unable to install for context %r", e.context)
|
logging.warning("unable to install for context %r", e.phase)
|
||||||
raise
|
raise
|
||||||
try:
|
try:
|
||||||
if not resolve_error(
|
if not resolve_error(
|
||||||
|
@ -750,13 +709,18 @@ def build_incrementally(
|
||||||
):
|
):
|
||||||
logging.warning("Failed to resolve error %r. Giving up.", e.error)
|
logging.warning("Failed to resolve error %r. Giving up.", e.error)
|
||||||
raise
|
raise
|
||||||
|
except GeneratedFile:
|
||||||
|
logging.warning(
|
||||||
|
"Control file is generated, unable to edit to "
|
||||||
|
"resolver error %r.", e.error)
|
||||||
|
raise e
|
||||||
except CircularDependency:
|
except CircularDependency:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Unable to fix %r; it would introduce a circular " "dependency.",
|
"Unable to fix %r; it would introduce a circular " "dependency.",
|
||||||
e.error,
|
e.error,
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
fixed_errors.append((e.error, e.context))
|
fixed_errors.append((e.error, e.phase))
|
||||||
if os.path.exists(os.path.join(output_directory, "build.log")):
|
if os.path.exists(os.path.join(output_directory, "build.log")):
|
||||||
i = 1
|
i = 1
|
||||||
while os.path.exists(
|
while os.path.exists(
|
||||||
|
@ -772,7 +736,7 @@ def build_incrementally(
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser("janitor.fix_build")
|
parser = argparse.ArgumentParser("ognibuild.debian.fix_build")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--suffix", type=str, help="Suffix to use for test builds.", default="fixbuild1"
|
"--suffix", type=str, help="Suffix to use for test builds.", default="fixbuild1"
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,14 +25,10 @@ from buildlog_consultant.common import (
|
||||||
MissingPythonDistribution,
|
MissingPythonDistribution,
|
||||||
MissingCommand,
|
MissingCommand,
|
||||||
)
|
)
|
||||||
|
from breezy.mutabletree import MutableTree
|
||||||
|
|
||||||
from . import DetailedFailure
|
from . import DetailedFailure, UnidentifiedError
|
||||||
from .apt import UnidentifiedError, AptManager
|
from .debian.apt import AptManager
|
||||||
from .debian.fix_build import (
|
|
||||||
DependencyContext,
|
|
||||||
resolve_error,
|
|
||||||
APT_FIXERS,
|
|
||||||
)
|
|
||||||
from .session import Session, run_with_tee
|
from .session import Session, run_with_tee
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +60,28 @@ class SimpleBuildFixer(BuildFixer):
|
||||||
return self._fn(problem, context)
|
return self._fn(problem, context)
|
||||||
|
|
||||||
|
|
||||||
|
class DependencyContext(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
tree: MutableTree,
|
||||||
|
apt: AptManager,
|
||||||
|
subpath: str = "",
|
||||||
|
committer: Optional[str] = None,
|
||||||
|
update_changelog: bool = True,
|
||||||
|
):
|
||||||
|
self.tree = tree
|
||||||
|
self.apt = apt
|
||||||
|
self.resolver = AptResolver(apt)
|
||||||
|
self.subpath = subpath
|
||||||
|
self.committer = committer
|
||||||
|
self.update_changelog = update_changelog
|
||||||
|
|
||||||
|
def add_dependency(
|
||||||
|
self, package: str, minimum_version: Optional['Version'] = None
|
||||||
|
) -> bool:
|
||||||
|
raise NotImplementedError(self.add_dependency)
|
||||||
|
|
||||||
|
|
||||||
class SchrootDependencyContext(DependencyContext):
|
class SchrootDependencyContext(DependencyContext):
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
@ -144,3 +162,19 @@ def run_with_build_fixer(
|
||||||
logging.warning("Failed to find resolution for error %r. Giving up.", error)
|
logging.warning("Failed to find resolution for error %r. Giving up.", error)
|
||||||
raise DetailedFailure(retcode, args, error)
|
raise DetailedFailure(retcode, args, error)
|
||||||
fixed_errors.append(error)
|
fixed_errors.append(error)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_error(error, context, fixers):
|
||||||
|
relevant_fixers = []
|
||||||
|
for error_cls, fixer in fixers:
|
||||||
|
if isinstance(error, error_cls):
|
||||||
|
relevant_fixers.append(fixer)
|
||||||
|
if not relevant_fixers:
|
||||||
|
logging.warning("No fixer found for %r", error)
|
||||||
|
return False
|
||||||
|
for fixer in relevant_fixers:
|
||||||
|
logging.info("Attempting to use fixer %r to address %r", fixer, error)
|
||||||
|
made_changes = fixer(error, context)
|
||||||
|
if made_changes:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
|
@ -381,6 +381,16 @@ APT_REQUIREMENT_RESOLVERS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_requirement_apt(apt_mgr, req: UpstreamRequirement):
|
||||||
|
for rr_class, rr_fn in APT_REQUIREMENT_RESOLVERS:
|
||||||
|
if isinstance(req, rr_class):
|
||||||
|
deb_req = rr_fn(apt_mgr, req)
|
||||||
|
if deb_req is None:
|
||||||
|
raise NoAptPackage()
|
||||||
|
return deb_req
|
||||||
|
raise NotImplementedError(type(req))
|
||||||
|
|
||||||
|
|
||||||
class AptResolver(Resolver):
|
class AptResolver(Resolver):
|
||||||
|
|
||||||
def __init__(self, apt):
|
def __init__(self, apt):
|
||||||
|
@ -401,17 +411,10 @@ class AptResolver(Resolver):
|
||||||
if not pps or not any(self.apt.session.exists(p) for p in pps):
|
if not pps or not any(self.apt.session.exists(p) for p in pps):
|
||||||
missing.append(req)
|
missing.append(req)
|
||||||
if missing:
|
if missing:
|
||||||
self.apt.install(list(self.resolve(missing)))
|
self.apt.install([self.resolve(m) for m in missing])
|
||||||
|
|
||||||
def explain(self, requirements):
|
def explain(self, requirements):
|
||||||
raise NotImplementedError(self.explain)
|
raise NotImplementedError(self.explain)
|
||||||
|
|
||||||
def resolve(self, req: UpstreamRequirement):
|
def resolve(self, req: UpstreamRequirement):
|
||||||
for rr_class, rr_fn in APT_REQUIREMENT_RESOLVERS:
|
return resolve_requirement_apt(self.apt, req)
|
||||||
if isinstance(req, rr_class):
|
|
||||||
deb_req = rr_fn(self.apt, req)
|
|
||||||
if deb_req is None:
|
|
||||||
raise NoAptPackage()
|
|
||||||
return deb_req
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue