New upstream release.
This commit is contained in:
commit
17b984bcee
24 changed files with 598 additions and 145 deletions
1
.github/workflows/pythonpackage.yml
vendored
1
.github/workflows/pythonpackage.yml
vendored
|
@ -28,6 +28,7 @@ jobs:
|
||||||
python -m pip install wheel
|
python -m pip install wheel
|
||||||
python -m pip install git+https://salsa.debian.org/apt-team/python-apt
|
python -m pip install git+https://salsa.debian.org/apt-team/python-apt
|
||||||
python -m pip install -e ".[debian]"
|
python -m pip install -e ".[debian]"
|
||||||
|
python -m pip install testtools
|
||||||
mkdir -p ~/.config/breezy/plugins
|
mkdir -p ~/.config/breezy/plugins
|
||||||
brz branch lp:brz-debian ~/.config/breezy/plugins/debian
|
brz branch lp:brz-debian ~/.config/breezy/plugins/debian
|
||||||
if: "matrix.python-version != 'pypy3' && matrix.os == 'ubuntu-latest'"
|
if: "matrix.python-version != 'pypy3' && matrix.os == 'ubuntu-latest'"
|
||||||
|
|
2
PKG-INFO
2
PKG-INFO
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 2.1
|
Metadata-Version: 2.1
|
||||||
Name: ognibuild
|
Name: ognibuild
|
||||||
Version: 0.0.3
|
Version: 0.0.4
|
||||||
Summary: Detect and run any build system
|
Summary: Detect and run any build system
|
||||||
Home-page: https://jelmer.uk/code/ognibuild
|
Home-page: https://jelmer.uk/code/ognibuild
|
||||||
Maintainer: Jelmer Vernooij
|
Maintainer: Jelmer Vernooij
|
||||||
|
|
|
@ -54,6 +54,8 @@ issues (or lack of support for a particular ecosystem), please file a bug.
|
||||||
- Octave
|
- Octave
|
||||||
- Perl
|
- Perl
|
||||||
- Module::Build::Tiny
|
- Module::Build::Tiny
|
||||||
|
- Dist::Zilla
|
||||||
|
- Minilla
|
||||||
- PHP Pear
|
- PHP Pear
|
||||||
- Python - setup.py/setup.cfg/pyproject.toml
|
- Python - setup.py/setup.cfg/pyproject.toml
|
||||||
- R
|
- R
|
||||||
|
|
5
debian/changelog
vendored
5
debian/changelog
vendored
|
@ -1,3 +1,8 @@
|
||||||
|
ognibuild (0.0.4-1) UNRELEASED; urgency=low
|
||||||
|
* New upstream release.
|
||||||
|
|
||||||
|
-- Jelmer Vernooij <jelmer@debian.org> Wed, 07 Apr 2021 00:10:53 -0000
|
||||||
|
|
||||||
ognibuild (0.0.3-1) unstable; urgency=medium
|
ognibuild (0.0.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Add missing dependency on python3-lz4.
|
* Add missing dependency on python3-lz4.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 2.1
|
Metadata-Version: 2.1
|
||||||
Name: ognibuild
|
Name: ognibuild
|
||||||
Version: 0.0.3
|
Version: 0.0.4
|
||||||
Summary: Detect and run any build system
|
Summary: Detect and run any build system
|
||||||
Home-page: https://jelmer.uk/code/ognibuild
|
Home-page: https://jelmer.uk/code/ognibuild
|
||||||
Maintainer: Jelmer Vernooij
|
Maintainer: Jelmer Vernooij
|
||||||
|
|
|
@ -20,7 +20,7 @@ import os
|
||||||
import stat
|
import stat
|
||||||
|
|
||||||
|
|
||||||
__version__ = (0, 0, 3)
|
__version__ = (0, 0, 4)
|
||||||
|
|
||||||
|
|
||||||
USER_AGENT = "Ognibuild"
|
USER_AGENT = "Ognibuild"
|
||||||
|
|
|
@ -150,6 +150,10 @@ def main(): # noqa: C901
|
||||||
external_dir, internal_dir = session.setup_from_directory(args.directory)
|
external_dir, internal_dir = session.setup_from_directory(args.directory)
|
||||||
session.chdir(internal_dir)
|
session.chdir(internal_dir)
|
||||||
os.chdir(external_dir)
|
os.chdir(external_dir)
|
||||||
|
|
||||||
|
if not session.is_temporary and args.subcommand == 'info':
|
||||||
|
args.explain = True
|
||||||
|
|
||||||
if args.resolve == "apt":
|
if args.resolve == "apt":
|
||||||
resolver = AptResolver.from_session(session)
|
resolver = AptResolver.from_session(session)
|
||||||
elif args.resolve == "native":
|
elif args.resolve == "native":
|
||||||
|
@ -211,7 +215,7 @@ def main(): # noqa: C901
|
||||||
if args.subcommand == "info":
|
if args.subcommand == "info":
|
||||||
from .info import run_info
|
from .info import run_info
|
||||||
|
|
||||||
run_info(session, buildsystems=bss)
|
run_info(session, buildsystems=bss, fixers=fixers)
|
||||||
except ExplainInstall as e:
|
except ExplainInstall as e:
|
||||||
display_explain_commands(e.commands)
|
display_explain_commands(e.commands)
|
||||||
except (UnidentifiedError, DetailedFailure):
|
except (UnidentifiedError, DetailedFailure):
|
||||||
|
|
|
@ -53,6 +53,7 @@ from buildlog_consultant.common import (
|
||||||
MissingVagueDependency,
|
MissingVagueDependency,
|
||||||
DhAddonLoadFailure,
|
DhAddonLoadFailure,
|
||||||
MissingMavenArtifacts,
|
MissingMavenArtifacts,
|
||||||
|
MissingIntrospectionTypelib,
|
||||||
GnomeCommonMissing,
|
GnomeCommonMissing,
|
||||||
MissingGnomeCommonDependency,
|
MissingGnomeCommonDependency,
|
||||||
UnknownCertificateAuthority,
|
UnknownCertificateAuthority,
|
||||||
|
@ -60,7 +61,11 @@ from buildlog_consultant.common import (
|
||||||
MissingLibtool,
|
MissingLibtool,
|
||||||
MissingQt,
|
MissingQt,
|
||||||
MissingX11,
|
MissingX11,
|
||||||
|
MissingPerlPredeclared,
|
||||||
|
MissingLatexFile,
|
||||||
|
MissingCargoCrate,
|
||||||
)
|
)
|
||||||
|
from buildlog_consultant.apt import UnsatisfiedAptDependencies
|
||||||
|
|
||||||
from .fix_build import BuildFixer
|
from .fix_build import BuildFixer
|
||||||
from .requirements import (
|
from .requirements import (
|
||||||
|
@ -99,6 +104,10 @@ from .requirements import (
|
||||||
X11Requirement,
|
X11Requirement,
|
||||||
LibtoolRequirement,
|
LibtoolRequirement,
|
||||||
VagueDependencyRequirement,
|
VagueDependencyRequirement,
|
||||||
|
IntrospectionTypelibRequirement,
|
||||||
|
PerlPreDeclaredRequirement,
|
||||||
|
LatexPackageRequirement,
|
||||||
|
CargoCrateRequirement,
|
||||||
)
|
)
|
||||||
from .resolver import UnsatisfiedRequirements
|
from .resolver import UnsatisfiedRequirements
|
||||||
|
|
||||||
|
@ -112,6 +121,8 @@ def problem_to_upstream_requirement(problem): # noqa: C901
|
||||||
return PkgConfigRequirement(problem.module, problem.minimum_version)
|
return PkgConfigRequirement(problem.module, problem.minimum_version)
|
||||||
elif isinstance(problem, MissingCHeader):
|
elif isinstance(problem, MissingCHeader):
|
||||||
return CHeaderRequirement(problem.header)
|
return CHeaderRequirement(problem.header)
|
||||||
|
elif isinstance(problem, MissingIntrospectionTypelib):
|
||||||
|
return IntrospectionTypelibRequirement(problem.library)
|
||||||
elif isinstance(problem, MissingJavaScriptRuntime):
|
elif isinstance(problem, MissingJavaScriptRuntime):
|
||||||
return JavaScriptRuntimeRequirement()
|
return JavaScriptRuntimeRequirement()
|
||||||
elif isinstance(problem, MissingRubyGem):
|
elif isinstance(problem, MissingRubyGem):
|
||||||
|
@ -130,6 +141,10 @@ def problem_to_upstream_requirement(problem): # noqa: C901
|
||||||
return NodeModuleRequirement(problem.module)
|
return NodeModuleRequirement(problem.module)
|
||||||
elif isinstance(problem, MissingNodePackage):
|
elif isinstance(problem, MissingNodePackage):
|
||||||
return NodePackageRequirement(problem.package)
|
return NodePackageRequirement(problem.package)
|
||||||
|
elif isinstance(problem, MissingLatexFile):
|
||||||
|
if problem.filename.endswith('.sty'):
|
||||||
|
return LatexPackageRequirement(problem.filename[:-4])
|
||||||
|
return None
|
||||||
elif isinstance(problem, MissingVagueDependency):
|
elif isinstance(problem, MissingVagueDependency):
|
||||||
return VagueDependencyRequirement(problem.name, minimum_version=problem.minimum_version)
|
return VagueDependencyRequirement(problem.name, minimum_version=problem.minimum_version)
|
||||||
elif isinstance(problem, MissingLibrary):
|
elif isinstance(problem, MissingLibrary):
|
||||||
|
@ -169,6 +184,11 @@ def problem_to_upstream_requirement(problem): # noqa: C901
|
||||||
return LibtoolRequirement()
|
return LibtoolRequirement()
|
||||||
elif isinstance(problem, UnknownCertificateAuthority):
|
elif isinstance(problem, UnknownCertificateAuthority):
|
||||||
return CertificateAuthorityRequirement(problem.url)
|
return CertificateAuthorityRequirement(problem.url)
|
||||||
|
elif isinstance(problem, MissingPerlPredeclared):
|
||||||
|
return PerlPreDeclaredRequirement(problem.name)
|
||||||
|
elif isinstance(problem, MissingCargoCrate):
|
||||||
|
# TODO(jelmer): handle problem.requirements
|
||||||
|
return CargoCrateRequirement(problem.crate)
|
||||||
elif isinstance(problem, MissingSetupPyCommand):
|
elif isinstance(problem, MissingSetupPyCommand):
|
||||||
if problem.command == "test":
|
if problem.command == "test":
|
||||||
return PythonPackageRequirement("setuptools")
|
return PythonPackageRequirement("setuptools")
|
||||||
|
@ -207,6 +227,9 @@ def problem_to_upstream_requirement(problem): # noqa: C901
|
||||||
python_version=problem.python_version,
|
python_version=problem.python_version,
|
||||||
minimum_version=problem.minimum_version,
|
minimum_version=problem.minimum_version,
|
||||||
)
|
)
|
||||||
|
elif isinstance(problem, UnsatisfiedAptDependencies):
|
||||||
|
from .resolver.apt import AptRequirement
|
||||||
|
return AptRequirement(problem.relations)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,11 @@ class Pear(BuildSystem):
|
||||||
|
|
||||||
name = "pear"
|
name = "pear"
|
||||||
|
|
||||||
|
PEAR_NAMESPACES = [
|
||||||
|
"http://pear.php.net/dtd/package-2.0",
|
||||||
|
"http://pear.php.net/dtd/package-2.1",
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
@ -146,10 +151,7 @@ class Pear(BuildSystem):
|
||||||
try:
|
try:
|
||||||
root = xmlparse_simplify_namespaces(
|
root = xmlparse_simplify_namespaces(
|
||||||
path,
|
path,
|
||||||
[
|
self.PEAR_NAMESPACES
|
||||||
"http://pear.php.net/dtd/package-2.0",
|
|
||||||
"http://pear.php.net/dtd/package-2.1",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
except ET.ParseError as e:
|
except ET.ParseError as e:
|
||||||
logging.warning("Unable to parse package.xml: %s", e)
|
logging.warning("Unable to parse package.xml: %s", e)
|
||||||
|
@ -173,9 +175,25 @@ class Pear(BuildSystem):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def probe(cls, path):
|
def probe(cls, path):
|
||||||
if os.path.exists(os.path.join(path, "package.xml")):
|
package_xml_path = os.path.join(path, "package.xml")
|
||||||
logging.debug("Found package.xml, assuming pear package.")
|
if not os.path.exists(package_xml_path):
|
||||||
return cls(os.path.join(path, "package.xml"))
|
return
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
try:
|
||||||
|
tree = ET.iterparse(package_xml_path)
|
||||||
|
except ET.ParseError as e:
|
||||||
|
logging.warning("Unable to parse package.xml: %s", e)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not tree.root:
|
||||||
|
# No root?
|
||||||
|
return
|
||||||
|
|
||||||
|
for ns in cls.PEAR_NAMESPACES:
|
||||||
|
if tree.root.tag == '{%s}package' % ns:
|
||||||
|
logging.debug("Found package.xml with namespace %s, assuming pear package.")
|
||||||
|
return cls(path)
|
||||||
|
|
||||||
|
|
||||||
# run_setup, but setting __name__
|
# run_setup, but setting __name__
|
||||||
|
@ -221,7 +239,6 @@ import sys
|
||||||
|
|
||||||
script_name = %(script_name)s
|
script_name = %(script_name)s
|
||||||
|
|
||||||
save_argv = sys.argv.copy()
|
|
||||||
g = {"__file__": script_name, "__name__": "__main__"}
|
g = {"__file__": script_name, "__name__": "__main__"}
|
||||||
try:
|
try:
|
||||||
core._setup_stop_after = "init"
|
core._setup_stop_after = "init"
|
||||||
|
@ -522,9 +539,6 @@ class SetupPy(BuildSystem):
|
||||||
if os.path.exists(os.path.join(path, "setup.py")):
|
if os.path.exists(os.path.join(path, "setup.py")):
|
||||||
logging.debug("Found setup.py, assuming python project.")
|
logging.debug("Found setup.py, assuming python project.")
|
||||||
return cls(path)
|
return cls(path)
|
||||||
if os.path.exists(os.path.join(path, "setup.cfg")):
|
|
||||||
logging.debug("Found setup.py, assuming python project.")
|
|
||||||
return cls(path)
|
|
||||||
if os.path.exists(os.path.join(path, "pyproject.toml")):
|
if os.path.exists(os.path.join(path, "pyproject.toml")):
|
||||||
logging.debug("Found pyproject.toml, assuming python project.")
|
logging.debug("Found pyproject.toml, assuming python project.")
|
||||||
return cls(path)
|
return cls(path)
|
||||||
|
@ -713,6 +727,10 @@ class R(BuildSystem):
|
||||||
if "Imports" in description:
|
if "Imports" in description:
|
||||||
for s in parse_list(description["Imports"]):
|
for s in parse_list(description["Imports"]):
|
||||||
yield "build", RPackageRequirement.from_str(s)
|
yield "build", RPackageRequirement.from_str(s)
|
||||||
|
if "LinkingTo" in description:
|
||||||
|
for s in parse_list(description["LinkingTo"]):
|
||||||
|
yield "build", RPackageRequirement.from_str(s)
|
||||||
|
# TODO(jelmer): Suggests
|
||||||
|
|
||||||
def get_declared_outputs(self, session, fixers=None):
|
def get_declared_outputs(self, session, fixers=None):
|
||||||
description = self._read_description()
|
description = self._read_description()
|
||||||
|
@ -978,6 +996,8 @@ class RunTests(BuildSystem):
|
||||||
|
|
||||||
def _read_cpanfile(session, args, kind, fixers):
|
def _read_cpanfile(session, args, kind, fixers):
|
||||||
for line in run_with_build_fixers(session, ["cpanfile-dump"] + args, fixers):
|
for line in run_with_build_fixers(session, ["cpanfile-dump"] + args, fixers):
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
yield kind, PerlModuleRequirement(line)
|
yield kind, PerlModuleRequirement(line)
|
||||||
|
|
||||||
|
|
||||||
|
@ -986,6 +1006,26 @@ def _declared_deps_from_cpanfile(session, fixers):
|
||||||
yield from _read_cpanfile(session, ["--test"], "test", fixers)
|
yield from _read_cpanfile(session, ["--test"], "test", fixers)
|
||||||
|
|
||||||
|
|
||||||
|
def _declared_deps_from_meta_yml(f):
|
||||||
|
# See http://module-build.sourceforge.net/META-spec-v1.4.html for
|
||||||
|
# the specification of the format.
|
||||||
|
import ruamel.yaml
|
||||||
|
import ruamel.yaml.reader
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = ruamel.yaml.load(f, ruamel.yaml.SafeLoader)
|
||||||
|
except ruamel.yaml.reader.ReaderError as e:
|
||||||
|
warnings.warn("Unable to parse META.yml: %s" % e)
|
||||||
|
return
|
||||||
|
for require in data.get("requires", []):
|
||||||
|
yield "core", PerlModuleRequirement(require)
|
||||||
|
for require in data.get("build_requires", []):
|
||||||
|
yield "build", PerlModuleRequirement(require)
|
||||||
|
for require in data.get("configure_requires", []):
|
||||||
|
yield "build", PerlModuleRequirement(require)
|
||||||
|
# TODO(jelmer): recommends
|
||||||
|
|
||||||
|
|
||||||
class Make(BuildSystem):
|
class Make(BuildSystem):
|
||||||
|
|
||||||
name = "make"
|
name = "make"
|
||||||
|
@ -1106,19 +1146,8 @@ class Make(BuildSystem):
|
||||||
something = False
|
something = False
|
||||||
# TODO(jelmer): Split out the perl-specific stuff?
|
# TODO(jelmer): Split out the perl-specific stuff?
|
||||||
if os.path.exists(os.path.join(self.path, "META.yml")):
|
if os.path.exists(os.path.join(self.path, "META.yml")):
|
||||||
# See http://module-build.sourceforge.net/META-spec-v1.4.html for
|
|
||||||
# the specification of the format.
|
|
||||||
import ruamel.yaml
|
|
||||||
import ruamel.yaml.reader
|
|
||||||
|
|
||||||
with open(os.path.join(self.path, "META.yml"), "rb") as f:
|
with open(os.path.join(self.path, "META.yml"), "rb") as f:
|
||||||
try:
|
yield from _declared_deps_from_meta_yml(f)
|
||||||
data = ruamel.yaml.load(f, ruamel.yaml.SafeLoader)
|
|
||||||
except ruamel.yaml.reader.ReaderError as e:
|
|
||||||
warnings.warn("Unable to parse META.yml: %s" % e)
|
|
||||||
return
|
|
||||||
for require in data.get("requires", []):
|
|
||||||
yield "build", PerlModuleRequirement(require)
|
|
||||||
something = True
|
something = True
|
||||||
if os.path.exists(os.path.join(self.path, "cpanfile")):
|
if os.path.exists(os.path.join(self.path, "cpanfile")):
|
||||||
yield from _declared_deps_from_cpanfile(session, fixers)
|
yield from _declared_deps_from_cpanfile(session, fixers)
|
||||||
|
@ -1204,6 +1233,7 @@ def _parse_go_mod(f):
|
||||||
while line:
|
while line:
|
||||||
parts = line.strip().split(" ")
|
parts = line.strip().split(" ")
|
||||||
if not parts or parts == [""]:
|
if not parts or parts == [""]:
|
||||||
|
line = readline()
|
||||||
continue
|
continue
|
||||||
if len(parts) == 2 and parts[1] == "(":
|
if len(parts) == 2 and parts[1] == "(":
|
||||||
line = readline()
|
line = readline()
|
||||||
|
@ -1393,6 +1423,7 @@ class PerlBuildTiny(BuildSystem):
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.minilla = os.path.exists(os.path.join(self.path, "minil.toml"))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.path)
|
return "%s(%r)" % (type(self).__name__, self.path)
|
||||||
|
@ -1402,6 +1433,9 @@ class PerlBuildTiny(BuildSystem):
|
||||||
|
|
||||||
def test(self, session, resolver, fixers):
|
def test(self, session, resolver, fixers):
|
||||||
self.setup(session, fixers)
|
self.setup(session, fixers)
|
||||||
|
if self.minilla:
|
||||||
|
run_with_build_fixers(session, ["minil", "test"], fixers)
|
||||||
|
else:
|
||||||
run_with_build_fixers(session, ["./Build", "test"], fixers)
|
run_with_build_fixers(session, ["./Build", "test"], fixers)
|
||||||
|
|
||||||
def build(self, session, resolver, fixers):
|
def build(self, session, resolver, fixers):
|
||||||
|
@ -1412,10 +1446,46 @@ class PerlBuildTiny(BuildSystem):
|
||||||
self.setup(session, fixers)
|
self.setup(session, fixers)
|
||||||
run_with_build_fixers(session, ["./Build", "clean"], fixers)
|
run_with_build_fixers(session, ["./Build", "clean"], fixers)
|
||||||
|
|
||||||
|
def dist(self, session, resolver, fixers, target_directory, quiet=False):
|
||||||
|
self.setup(session, fixers)
|
||||||
|
with DistCatcher([session.external_path('.')]) as dc:
|
||||||
|
if self.minilla:
|
||||||
|
run_with_build_fixers(session, ["minil", "dist"], fixers)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
run_with_build_fixers(session, ["./Build", "dist"], fixers)
|
||||||
|
except UnidentifiedError as e:
|
||||||
|
if "Can't find dist packages without a MANIFEST file" in e.lines:
|
||||||
|
run_with_build_fixers(session, ["./Build", "manifest"], fixers)
|
||||||
|
run_with_build_fixers(session, ["./Build", "dist"], fixers)
|
||||||
|
elif "No such action 'dist'" in e.lines:
|
||||||
|
raise NotImplementedError
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return dc.copy_single(target_directory)
|
||||||
|
|
||||||
def install(self, session, resolver, fixers, install_target):
|
def install(self, session, resolver, fixers, install_target):
|
||||||
self.setup(session, fixers)
|
self.setup(session, fixers)
|
||||||
|
if self.minilla:
|
||||||
|
run_with_build_fixers(session, ["minil", "install"], fixers)
|
||||||
|
else:
|
||||||
run_with_build_fixers(session, ["./Build", "install"], fixers)
|
run_with_build_fixers(session, ["./Build", "install"], fixers)
|
||||||
|
|
||||||
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
|
self.setup(session, fixers)
|
||||||
|
try:
|
||||||
|
run_with_build_fixers(session, ["./Build", "distmeta"], fixers)
|
||||||
|
except UnidentifiedError as e:
|
||||||
|
if "No such action 'distmeta'" in e.lines:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
try:
|
||||||
|
with open(os.path.join(self.path, 'META.yml'), 'r') as f:
|
||||||
|
yield from _declared_deps_from_meta_yml(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def probe(cls, path):
|
def probe(cls, path):
|
||||||
if os.path.exists(os.path.join(path, "Build.PL")):
|
if os.path.exists(os.path.join(path, "Build.PL")):
|
||||||
|
|
|
@ -28,7 +28,7 @@ from .. import DetailedFailure, UnidentifiedError
|
||||||
from ..session import Session, run_with_tee, get_user
|
from ..session import Session, run_with_tee, get_user
|
||||||
from .file_search import (
|
from .file_search import (
|
||||||
FileSearcher,
|
FileSearcher,
|
||||||
AptCachedContentsFileSearcher,
|
get_apt_contents_file_searcher,
|
||||||
GENERATED_FILE_SEARCHER,
|
GENERATED_FILE_SEARCHER,
|
||||||
get_packages_for_paths,
|
get_packages_for_paths,
|
||||||
)
|
)
|
||||||
|
@ -41,6 +41,7 @@ def run_apt(
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
prefix = []
|
prefix = []
|
||||||
args = prefix = ["apt", "-y"] + args
|
args = prefix = ["apt", "-y"] + args
|
||||||
|
logging.info('apt: running %r', args)
|
||||||
retcode, lines = run_with_tee(session, args, cwd="/", user="root")
|
retcode, lines = run_with_tee(session, args, cwd="/", user="root")
|
||||||
if retcode == 0:
|
if retcode == 0:
|
||||||
return
|
return
|
||||||
|
@ -76,21 +77,27 @@ class AptManager(object):
|
||||||
def searchers(self):
|
def searchers(self):
|
||||||
if self._searchers is None:
|
if self._searchers is None:
|
||||||
self._searchers = [
|
self._searchers = [
|
||||||
AptCachedContentsFileSearcher.from_session(self.session),
|
get_apt_contents_file_searcher(self.session),
|
||||||
GENERATED_FILE_SEARCHER,
|
GENERATED_FILE_SEARCHER,
|
||||||
]
|
]
|
||||||
return self._searchers
|
return self._searchers
|
||||||
|
|
||||||
def package_exists(self, package):
|
@property
|
||||||
|
def apt_cache(self):
|
||||||
if self._apt_cache is None:
|
if self._apt_cache is None:
|
||||||
import apt
|
import apt
|
||||||
|
|
||||||
self._apt_cache = apt.Cache(rootdir=self.session.location)
|
self._apt_cache = apt.Cache(rootdir=self.session.location)
|
||||||
return package in self._apt_cache
|
return self._apt_cache
|
||||||
|
|
||||||
|
def package_exists(self, package):
|
||||||
|
return package in self.apt_cache
|
||||||
|
|
||||||
|
def package_versions(self, package):
|
||||||
|
return list(self.apt_cache[package].versions)
|
||||||
|
|
||||||
def get_packages_for_paths(self, paths, regex=False, case_insensitive=False):
|
def get_packages_for_paths(self, paths, regex=False, case_insensitive=False):
|
||||||
logging.debug("Searching for packages containing %r", paths)
|
logging.debug("Searching for packages containing %r", paths)
|
||||||
# TODO(jelmer): Make sure we use whatever is configured in self.session
|
|
||||||
return get_packages_for_paths(
|
return get_packages_for_paths(
|
||||||
paths, self.searchers(), regex=regex, case_insensitive=case_insensitive
|
paths, self.searchers(), regex=regex, case_insensitive=case_insensitive
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,22 +16,24 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"changes_filename",
|
|
||||||
"get_build_architecture",
|
"get_build_architecture",
|
||||||
"add_dummy_changelog_entry",
|
"add_dummy_changelog_entry",
|
||||||
"build",
|
"build",
|
||||||
"SbuildFailure",
|
"DetailedDebianBuildFailure",
|
||||||
|
"UnidentifiedDebianBuildError",
|
||||||
]
|
]
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from debmutate.changelog import ChangelogEditor
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from debian.changelog import Changelog
|
from debian.changelog import Changelog
|
||||||
from debmutate.changelog import get_maintainer, format_datetime
|
from debmutate.changelog import get_maintainer
|
||||||
|
|
||||||
from breezy.mutabletree import MutableTree
|
from breezy.mutabletree import MutableTree
|
||||||
from breezy.plugins.debian.builder import BuildFailedError
|
from breezy.plugins.debian.builder import BuildFailedError
|
||||||
|
@ -39,13 +41,33 @@ from breezy.tree import Tree
|
||||||
|
|
||||||
from buildlog_consultant.sbuild import (
|
from buildlog_consultant.sbuild import (
|
||||||
worker_failure_from_sbuild_log,
|
worker_failure_from_sbuild_log,
|
||||||
SbuildFailure,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .. import DetailedFailure as DetailedFailure, UnidentifiedError
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BUILDER = "sbuild --no-clean-source"
|
DEFAULT_BUILDER = "sbuild --no-clean-source"
|
||||||
|
|
||||||
|
|
||||||
|
class DetailedDebianBuildFailure(DetailedFailure):
|
||||||
|
|
||||||
|
def __init__(self, stage, phase, retcode, argv, error, description):
|
||||||
|
super(DetailedDebianBuildFailure, self).__init__(retcode, argv, error)
|
||||||
|
self.stage = stage
|
||||||
|
self.phase = phase
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
|
class UnidentifiedDebianBuildError(UnidentifiedError):
|
||||||
|
|
||||||
|
def __init__(self, stage, phase, retcode, argv, lines, description, secondary=None):
|
||||||
|
super(UnidentifiedDebianBuildError, self).__init__(
|
||||||
|
retcode, argv, lines, secondary)
|
||||||
|
self.stage = stage
|
||||||
|
self.phase = phase
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
class MissingChangesFile(Exception):
|
class MissingChangesFile(Exception):
|
||||||
"""Expected changes file was not written."""
|
"""Expected changes file was not written."""
|
||||||
|
|
||||||
|
@ -53,11 +75,15 @@ class MissingChangesFile(Exception):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
|
||||||
def changes_filename(package, version, arch):
|
def find_changes_files(path, package, version):
|
||||||
non_epoch_version = version.upstream_version
|
non_epoch_version = version.upstream_version
|
||||||
if version.debian_version is not None:
|
if version.debian_version is not None:
|
||||||
non_epoch_version += "-%s" % version.debian_version
|
non_epoch_version += "-%s" % version.debian_version
|
||||||
return "%s_%s_%s.changes" % (package, non_epoch_version, arch)
|
c = re.compile('%s_%s_(.*).changes' % (re.escape(package), re.escape(non_epoch_version)))
|
||||||
|
for entry in os.scandir(path):
|
||||||
|
m = c.match(entry.name)
|
||||||
|
if m:
|
||||||
|
yield m.group(1), entry
|
||||||
|
|
||||||
|
|
||||||
def get_build_architecture():
|
def get_build_architecture():
|
||||||
|
@ -119,25 +145,16 @@ def add_dummy_changelog_entry(
|
||||||
maintainer = get_maintainer()
|
maintainer = get_maintainer()
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now()
|
timestamp = datetime.now()
|
||||||
with tree.get_file(path) as f:
|
with ChangelogEditor(tree.abspath(os.path.join(path))) as editor:
|
||||||
cl = Changelog()
|
version = editor[0].version
|
||||||
cl.parse_changelog(f, max_blocks=None, allow_empty_author=True, strict=False)
|
|
||||||
version = cl[0].version
|
|
||||||
if version.debian_revision:
|
if version.debian_revision:
|
||||||
version.debian_revision = add_suffix(version.debian_revision, suffix)
|
version.debian_revision = add_suffix(version.debian_revision, suffix)
|
||||||
else:
|
else:
|
||||||
version.upstream_version = add_suffix(version.upstream_version, suffix)
|
version.upstream_version = add_suffix(version.upstream_version, suffix)
|
||||||
cl.new_block(
|
editor.auto_version(version, timestamp=timestamp)
|
||||||
package=cl[0].package,
|
editor.add_entry(
|
||||||
version=version,
|
summary=[message], maintainer=maintainer, timestamp=timestamp, urgency='low')
|
||||||
urgency="low",
|
editor[0].distributions = suite
|
||||||
distributions=suite,
|
|
||||||
author="%s <%s>" % maintainer,
|
|
||||||
date=format_datetime(timestamp),
|
|
||||||
changes=["", " * " + message, ""],
|
|
||||||
)
|
|
||||||
cl_str = cl._format(allow_missing_author=True)
|
|
||||||
tree.put_file_bytes_non_atomic(path, cl_str.encode(cl._encoding))
|
|
||||||
|
|
||||||
|
|
||||||
def get_latest_changelog_version(local_tree, subpath=""):
|
def get_latest_changelog_version(local_tree, subpath=""):
|
||||||
|
@ -158,7 +175,10 @@ def build(
|
||||||
distribution=None,
|
distribution=None,
|
||||||
subpath="",
|
subpath="",
|
||||||
source_date_epoch=None,
|
source_date_epoch=None,
|
||||||
|
extra_repositories=None,
|
||||||
):
|
):
|
||||||
|
for repo in extra_repositories or []:
|
||||||
|
build_command += " --extra-repository=" + shlex.quote(repo)
|
||||||
args = [
|
args = [
|
||||||
sys.executable,
|
sys.executable,
|
||||||
"-m",
|
"-m",
|
||||||
|
@ -192,8 +212,10 @@ def build_once(
|
||||||
build_command,
|
build_command,
|
||||||
subpath="",
|
subpath="",
|
||||||
source_date_epoch=None,
|
source_date_epoch=None,
|
||||||
|
extra_repositories=None
|
||||||
):
|
):
|
||||||
build_log_path = os.path.join(output_directory, "build.log")
|
build_log_path = os.path.join(output_directory, "build.log")
|
||||||
|
logging.debug("Writing build log to %s", build_log_path)
|
||||||
try:
|
try:
|
||||||
with open(build_log_path, "w") as f:
|
with open(build_log_path, "w") as f:
|
||||||
build(
|
build(
|
||||||
|
@ -204,21 +226,35 @@ def build_once(
|
||||||
distribution=build_suite,
|
distribution=build_suite,
|
||||||
subpath=subpath,
|
subpath=subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
except BuildFailedError:
|
except BuildFailedError as e:
|
||||||
with open(build_log_path, "rb") as f:
|
with open(build_log_path, "rb") as f:
|
||||||
raise worker_failure_from_sbuild_log(f)
|
sbuild_failure = worker_failure_from_sbuild_log(f)
|
||||||
|
retcode = getattr(e, 'returncode', None)
|
||||||
|
if sbuild_failure.error:
|
||||||
|
raise DetailedDebianBuildFailure(
|
||||||
|
sbuild_failure.stage,
|
||||||
|
sbuild_failure.phase, retcode,
|
||||||
|
shlex.split(build_command),
|
||||||
|
sbuild_failure.error,
|
||||||
|
sbuild_failure.description)
|
||||||
|
else:
|
||||||
|
raise UnidentifiedDebianBuildError(
|
||||||
|
sbuild_failure.stage,
|
||||||
|
sbuild_failure.phase,
|
||||||
|
retcode, shlex.split(build_command),
|
||||||
|
[], sbuild_failure.description)
|
||||||
|
|
||||||
(cl_package, cl_version) = get_latest_changelog_version(local_tree, subpath)
|
(cl_package, cl_version) = get_latest_changelog_version(local_tree, subpath)
|
||||||
changes_name = changes_filename(cl_package, cl_version, get_build_architecture())
|
changes_names = []
|
||||||
changes_path = os.path.join(output_directory, changes_name)
|
for kind, entry in find_changes_files(output_directory, cl_package, cl_version):
|
||||||
if not os.path.exists(changes_path):
|
changes_names.append((entry.name))
|
||||||
raise MissingChangesFile(changes_name)
|
return (changes_names, cl_version)
|
||||||
return (changes_name, cl_version)
|
|
||||||
|
|
||||||
|
|
||||||
def gbp_dch(path):
|
def gbp_dch(path):
|
||||||
subprocess.check_call(["gbp", "dch"], cwd=path)
|
subprocess.check_call(["gbp", "dch", "--ignore-branch"], cwd=path)
|
||||||
|
|
||||||
|
|
||||||
def attempt_build(
|
def attempt_build(
|
||||||
|
@ -230,6 +266,8 @@ def attempt_build(
|
||||||
build_changelog_entry=None,
|
build_changelog_entry=None,
|
||||||
subpath="",
|
subpath="",
|
||||||
source_date_epoch=None,
|
source_date_epoch=None,
|
||||||
|
run_gbp_dch=False,
|
||||||
|
extra_repositories=None
|
||||||
):
|
):
|
||||||
"""Attempt a build, with a custom distribution set.
|
"""Attempt a build, with a custom distribution set.
|
||||||
|
|
||||||
|
@ -244,6 +282,8 @@ def attempt_build(
|
||||||
source_date_epoch: Source date epoch to set
|
source_date_epoch: Source date epoch to set
|
||||||
Returns: Tuple with (changes_name, cl_version)
|
Returns: Tuple with (changes_name, cl_version)
|
||||||
"""
|
"""
|
||||||
|
if run_gbp_dch and not subpath:
|
||||||
|
gbp_dch(local_tree.abspath(subpath))
|
||||||
if build_changelog_entry is not None:
|
if build_changelog_entry is not None:
|
||||||
add_dummy_changelog_entry(
|
add_dummy_changelog_entry(
|
||||||
local_tree, subpath, suffix, build_suite, build_changelog_entry
|
local_tree, subpath, suffix, build_suite, build_changelog_entry
|
||||||
|
@ -255,4 +295,5 @@ def attempt_build(
|
||||||
build_command,
|
build_command,
|
||||||
subpath,
|
subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
|
|
|
@ -65,9 +65,9 @@ class BuildDependencyTieBreaker(object):
|
||||||
return None
|
return None
|
||||||
top = max(by_count.items(), key=lambda k: k[1])
|
top = max(by_count.items(), key=lambda k: k[1])
|
||||||
logging.info(
|
logging.info(
|
||||||
"Breaking tie between %r to %r based on build-depends count",
|
"Breaking tie between [%s] to %s based on build-depends count",
|
||||||
[repr(r) for r in reqs],
|
', '.join([repr(r.pkg_relation_str()) for r in reqs]),
|
||||||
top[0],
|
repr(top[0].pkg_relation_str()),
|
||||||
)
|
)
|
||||||
return top[0]
|
return top[0]
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,13 @@ from datetime import datetime
|
||||||
from debian.deb822 import Release
|
from debian.deb822 import Release
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
from typing import Iterator, List
|
from typing import Iterator, List
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
from .. import USER_AGENT
|
from .. import USER_AGENT
|
||||||
|
from ..session import Session
|
||||||
|
|
||||||
|
|
||||||
class FileSearcher(object):
|
class FileSearcher(object):
|
||||||
|
@ -158,7 +160,68 @@ def load_apt_cache_file(url, cache_dir):
|
||||||
raise FileNotFoundError(url)
|
raise FileNotFoundError(url)
|
||||||
|
|
||||||
|
|
||||||
class AptCachedContentsFileSearcher(FileSearcher):
|
class AptFileFileSearcher(FileSearcher):
|
||||||
|
|
||||||
|
CACHE_IS_EMPTY_PATH = '/usr/share/apt-file/is-cache-empty'
|
||||||
|
|
||||||
|
def __init__(self, session: Session):
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_cache(cls, session: Session) -> bool:
|
||||||
|
if not os.path.exists(session.external_path(cls.CACHE_IS_EMPTY_PATH)):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
session.check_call([cls.CACHE_IS_EMPTY_PATH])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
if e.returncode == 1:
|
||||||
|
return True
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_session(cls, session):
|
||||||
|
logging.info('Using apt-file to search apt contents')
|
||||||
|
if not os.path.exists(session.external_path(cls.CACHE_IS_EMPTY_PATH)):
|
||||||
|
from .apt import AptManager
|
||||||
|
AptManager.from_session(session).install(['apt-file'])
|
||||||
|
if not cls.has_cache(session):
|
||||||
|
session.check_call(['apt-file', 'update'], user='root')
|
||||||
|
return cls(session)
|
||||||
|
|
||||||
|
def search_files(self, path, regex=False, case_insensitive=False):
|
||||||
|
args = []
|
||||||
|
if regex:
|
||||||
|
args.append('-x')
|
||||||
|
else:
|
||||||
|
args.append('-F')
|
||||||
|
if case_insensitive:
|
||||||
|
args.append('-i')
|
||||||
|
args.append(path)
|
||||||
|
try:
|
||||||
|
output = self.session.check_output(['/usr/bin/apt-file', 'search'] + args)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
if e.returncode == 1:
|
||||||
|
# No results
|
||||||
|
return
|
||||||
|
if e.returncode == 3:
|
||||||
|
raise Exception('apt-file cache is empty')
|
||||||
|
raise
|
||||||
|
|
||||||
|
for line in output.splitlines(False):
|
||||||
|
pkg, path = line.split(b': ')
|
||||||
|
yield pkg.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def get_apt_contents_file_searcher(session):
|
||||||
|
if AptFileFileSearcher.has_cache(session):
|
||||||
|
return AptFileFileSearcher.from_session(session)
|
||||||
|
|
||||||
|
return RemoteContentsFileSearcher.from_session(session)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteContentsFileSearcher(FileSearcher):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._db = {}
|
self._db = {}
|
||||||
|
|
||||||
|
@ -268,12 +331,12 @@ class GeneratedFileSearcher(FileSearcher):
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
(path, pkg) = line.strip().split(None, 1)
|
(path, pkg) = line.strip().split(None, 1)
|
||||||
self._db[path] = pkg
|
self._db.append(path, pkg)
|
||||||
|
|
||||||
def search_files(
|
def search_files(
|
||||||
self, path: str, regex: bool = False, case_insensitive: bool = False
|
self, path: str, regex: bool = False, case_insensitive: bool = False
|
||||||
) -> Iterator[str]:
|
) -> Iterator[str]:
|
||||||
for p, pkg in sorted(self._db.items()):
|
for p, pkg in self._db:
|
||||||
if regex:
|
if regex:
|
||||||
flags = 0
|
flags = 0
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
|
@ -290,16 +353,16 @@ class GeneratedFileSearcher(FileSearcher):
|
||||||
|
|
||||||
# TODO(jelmer): read from a file
|
# TODO(jelmer): read from a file
|
||||||
GENERATED_FILE_SEARCHER = GeneratedFileSearcher(
|
GENERATED_FILE_SEARCHER = GeneratedFileSearcher(
|
||||||
{
|
[
|
||||||
"/etc/locale.gen": "locales",
|
("/etc/locale.gen", "locales"),
|
||||||
# Alternative
|
# Alternative
|
||||||
"/usr/bin/rst2html": "python3-docutils",
|
("/usr/bin/rst2html", "python3-docutils"),
|
||||||
# aclocal is a symlink to aclocal-1.XY
|
# aclocal is a symlink to aclocal-1.XY
|
||||||
"/usr/bin/aclocal": "automake",
|
("/usr/bin/aclocal", "automake"),
|
||||||
"/usr/bin/automake": "automake",
|
("/usr/bin/automake", "automake"),
|
||||||
# maven lives in /usr/share
|
# maven lives in /usr/share
|
||||||
"/usr/bin/mvn": "maven",
|
("/usr/bin/mvn", "maven"),
|
||||||
}
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -322,6 +385,7 @@ def get_packages_for_paths(
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
import argparse
|
import argparse
|
||||||
|
from ..session.plain import PlainSession
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("path", help="Path to search for.", type=str, nargs="*")
|
parser.add_argument("path", help="Path to search for.", type=str, nargs="*")
|
||||||
|
@ -334,7 +398,7 @@ def main(argv):
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
main_searcher = AptCachedContentsFileSearcher()
|
main_searcher = get_apt_contents_file_searcher(PlainSession())
|
||||||
main_searcher.load_local()
|
main_searcher.load_local()
|
||||||
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ __all__ = [
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Set, Optional, Type
|
from typing import List, Set, Optional, Type
|
||||||
|
@ -31,7 +32,7 @@ from debian.deb822 import (
|
||||||
PkgRelation,
|
PkgRelation,
|
||||||
)
|
)
|
||||||
|
|
||||||
from breezy.commit import PointlessCommit
|
from breezy.commit import PointlessCommit, NullCommitReporter
|
||||||
from breezy.tree import Tree
|
from breezy.tree import Tree
|
||||||
from debmutate.changelog import ChangelogEditor
|
from debmutate.changelog import ChangelogEditor
|
||||||
from debmutate.control import (
|
from debmutate.control import (
|
||||||
|
@ -111,9 +112,13 @@ from buildlog_consultant.common import (
|
||||||
MissingPerlFile,
|
MissingPerlFile,
|
||||||
)
|
)
|
||||||
from buildlog_consultant.sbuild import (
|
from buildlog_consultant.sbuild import (
|
||||||
SbuildFailure,
|
DebcargoUnacceptablePredicate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .build import (
|
||||||
|
DetailedDebianBuildFailure,
|
||||||
|
UnidentifiedDebianBuildError,
|
||||||
|
)
|
||||||
from ..buildlog import problem_to_upstream_requirement
|
from ..buildlog import problem_to_upstream_requirement
|
||||||
from ..fix_build import BuildFixer, resolve_error
|
from ..fix_build import BuildFixer, resolve_error
|
||||||
from ..resolver.apt import (
|
from ..resolver.apt import (
|
||||||
|
@ -154,7 +159,10 @@ class DebianPackagingContext(object):
|
||||||
cl_path = self.abspath("debian/changelog")
|
cl_path = self.abspath("debian/changelog")
|
||||||
with ChangelogEditor(cl_path) as editor:
|
with ChangelogEditor(cl_path) as editor:
|
||||||
editor.add_entry([summary])
|
editor.add_entry([summary])
|
||||||
debcommit(self.tree, committer=self.committer, subpath=self.subpath)
|
debcommit(
|
||||||
|
self.tree, committer=self.committer,
|
||||||
|
subpath=self.subpath,
|
||||||
|
reporter=self.commit_reporter)
|
||||||
else:
|
else:
|
||||||
self.tree.commit(
|
self.tree.commit(
|
||||||
message=summary,
|
message=summary,
|
||||||
|
@ -304,6 +312,8 @@ def python_tie_breaker(tree, subpath, reqs):
|
||||||
return True
|
return True
|
||||||
if pkg.startswith("lib%s-" % python_version):
|
if pkg.startswith("lib%s-" % python_version):
|
||||||
return True
|
return True
|
||||||
|
if re.match(r'lib%s\.[0-9]-dev' % python_version, pkg):
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for python_version in targeted:
|
for python_version in targeted:
|
||||||
|
@ -423,6 +433,13 @@ def fix_missing_makefile_pl(error, phase, context):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def coerce_unaccpetable_predicate(error, phase, context):
|
||||||
|
from debmutate.debcargo import DebcargoEditor
|
||||||
|
with DebcargoEditor(context.abspath('debian/debcargo.toml')) as editor:
|
||||||
|
editor['allow_prerelease_deps'] = True
|
||||||
|
return context.commit('Enable allow_prerelease_deps.')
|
||||||
|
|
||||||
|
|
||||||
class SimpleBuildFixer(BuildFixer):
|
class SimpleBuildFixer(BuildFixer):
|
||||||
def __init__(self, packaging_context, problem_cls: Type[Problem], fn):
|
def __init__(self, packaging_context, problem_cls: Type[Problem], fn):
|
||||||
self.context = packaging_context
|
self.context = packaging_context
|
||||||
|
@ -475,6 +492,7 @@ def versioned_package_fixers(session, packaging_context, apt):
|
||||||
packaging_context, MissingConfigStatusInput, fix_missing_config_status_input
|
packaging_context, MissingConfigStatusInput, fix_missing_config_status_input
|
||||||
),
|
),
|
||||||
SimpleBuildFixer(packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
SimpleBuildFixer(packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
||||||
|
SimpleBuildFixer(packaging_context, DebcargoUnacceptablePredicate, coerce_unaccpetable_predicate),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -497,6 +515,16 @@ def apt_fixers(apt, packaging_context) -> List[BuildFixer]:
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def default_fixers(local_tree, subpath, apt, committer=None, update_changelog=None):
|
||||||
|
packaging_context = DebianPackagingContext(
|
||||||
|
local_tree, subpath, committer, update_changelog,
|
||||||
|
commit_reporter=NullCommitReporter()
|
||||||
|
)
|
||||||
|
return versioned_package_fixers(apt.session, packaging_context, apt) + apt_fixers(
|
||||||
|
apt, packaging_context
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_incrementally(
|
def build_incrementally(
|
||||||
local_tree,
|
local_tree,
|
||||||
apt,
|
apt,
|
||||||
|
@ -510,14 +538,14 @@ def build_incrementally(
|
||||||
subpath="",
|
subpath="",
|
||||||
source_date_epoch=None,
|
source_date_epoch=None,
|
||||||
update_changelog=True,
|
update_changelog=True,
|
||||||
|
extra_repositories=None,
|
||||||
|
fixers=None
|
||||||
):
|
):
|
||||||
fixed_errors = []
|
fixed_errors = []
|
||||||
packaging_context = DebianPackagingContext(
|
if fixers is None:
|
||||||
local_tree, subpath, committer, update_changelog
|
fixers = default_fixers(
|
||||||
)
|
local_tree, subpath, apt, committer=committer,
|
||||||
fixers = versioned_package_fixers(apt.session, packaging_context, apt) + apt_fixers(
|
update_changelog=update_changelog)
|
||||||
apt, packaging_context
|
|
||||||
)
|
|
||||||
logging.info("Using fixers: %r", fixers)
|
logging.info("Using fixers: %r", fixers)
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -530,11 +558,13 @@ def build_incrementally(
|
||||||
build_changelog_entry,
|
build_changelog_entry,
|
||||||
subpath=subpath,
|
subpath=subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
|
run_gbp_dch=(update_changelog is False),
|
||||||
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
except SbuildFailure as e:
|
except UnidentifiedDebianBuildError:
|
||||||
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
|
||||||
|
except DetailedDebianBuildFailure as e:
|
||||||
if e.phase 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
|
||||||
|
@ -603,6 +633,11 @@ def main(argv=None):
|
||||||
dest="update_changelog",
|
dest="update_changelog",
|
||||||
help="do not update the changelog",
|
help="do not update the changelog",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--max-iterations',
|
||||||
|
type=int,
|
||||||
|
default=DEFAULT_MAX_ITERATIONS,
|
||||||
|
help='Maximum number of issues to attempt to fix before giving up.')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--update-changelog",
|
"--update-changelog",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
@ -646,7 +681,7 @@ def main(argv=None):
|
||||||
apt = AptManager(session)
|
apt = AptManager(session)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(changes_filename, cl_version) = build_incrementally(
|
(changes_filenames, cl_version) = build_incrementally(
|
||||||
tree,
|
tree,
|
||||||
apt,
|
apt,
|
||||||
args.suffix,
|
args.suffix,
|
||||||
|
@ -656,23 +691,30 @@ def main(argv=None):
|
||||||
None,
|
None,
|
||||||
committer=args.committer,
|
committer=args.committer,
|
||||||
update_changelog=args.update_changelog,
|
update_changelog=args.update_changelog,
|
||||||
|
max_iterations=args.max_iterations,
|
||||||
)
|
)
|
||||||
except SbuildFailure as e:
|
except DetailedDebianBuildFailure as e:
|
||||||
if e.phase is None:
|
if e.phase is None:
|
||||||
phase = "unknown phase"
|
phase = "unknown phase"
|
||||||
elif len(e.phase) == 1:
|
elif len(e.phase) == 1:
|
||||||
phase = e.phase[0]
|
phase = e.phase[0]
|
||||||
else:
|
else:
|
||||||
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
||||||
if e.error:
|
|
||||||
logging.fatal("Error during %s: %s", phase, e.error)
|
logging.fatal("Error during %s: %s", phase, e.error)
|
||||||
|
return 1
|
||||||
|
except UnidentifiedDebianBuildError as e:
|
||||||
|
if e.phase is None:
|
||||||
|
phase = "unknown phase"
|
||||||
|
elif len(e.phase) == 1:
|
||||||
|
phase = e.phase[0]
|
||||||
else:
|
else:
|
||||||
|
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
||||||
logging.fatal("Error during %s: %s", phase, e.description)
|
logging.fatal("Error during %s: %s", phase, e.description)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
'Built %s - changes file at %s.',
|
'Built %s - changes file at %r.',
|
||||||
os.path.join(output_directory, changes_filename))
|
cl_version, changes_filenames)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -51,6 +51,8 @@ def run_dist(session, buildsystems, resolver, fixers, target_directory, quiet=Fa
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
|
||||||
|
logging.info('Using dependency resolver: %s', resolver)
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
filename = buildsystem.dist(
|
filename = buildsystem.dist(
|
||||||
session, resolver, fixers, target_directory, quiet=quiet
|
session, resolver, fixers, target_directory, quiet=quiet
|
||||||
|
|
|
@ -94,7 +94,8 @@ class DistCatcher(object):
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Found multiple tarballs %r in %s.", possible_new, directory
|
"Found multiple tarballs %r in %s.", possible_new, directory
|
||||||
)
|
)
|
||||||
return
|
self.files.extend([entry.path for entry in possible_new])
|
||||||
|
return possible_new[0].name
|
||||||
|
|
||||||
if len(possible_updated) == 1:
|
if len(possible_updated) == 1:
|
||||||
entry = possible_updated[0]
|
entry = possible_updated[0]
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Tuple, Callable, Any
|
from typing import List, Tuple, Callable, Any, Optional
|
||||||
|
|
||||||
from buildlog_consultant import Problem
|
from buildlog_consultant import Problem
|
||||||
from buildlog_consultant.common import (
|
from buildlog_consultant.common import (
|
||||||
|
@ -104,8 +104,10 @@ def iterate_with_build_fixers(fixers: List[BuildFixer], cb: Callable[[], Any]):
|
||||||
|
|
||||||
|
|
||||||
def run_with_build_fixers(
|
def run_with_build_fixers(
|
||||||
session: Session, args: List[str], fixers: List[BuildFixer], **kwargs
|
session: Session, args: List[str], fixers: Optional[List[BuildFixer]], **kwargs
|
||||||
):
|
):
|
||||||
|
if fixers is None:
|
||||||
|
fixers = []
|
||||||
return iterate_with_build_fixers(
|
return iterate_with_build_fixers(
|
||||||
fixers, partial(run_detecting_problems, session, args, **kwargs)
|
fixers, partial(run_detecting_problems, session, args, **kwargs)
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
def run_info(session, buildsystems):
|
def run_info(session, buildsystems, fixers=None):
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
print("%r:" % buildsystem)
|
print("%r:" % buildsystem)
|
||||||
deps = {}
|
deps = {}
|
||||||
try:
|
try:
|
||||||
for kind, dep in buildsystem.get_declared_dependencies(session):
|
for kind, dep in buildsystem.get_declared_dependencies(session, fixers=fixers):
|
||||||
deps.setdefault(kind, []).append(dep)
|
deps.setdefault(kind, []).append(dep)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print(
|
print(
|
||||||
|
@ -35,7 +35,7 @@ def run_info(session, buildsystems):
|
||||||
print("\t\t\t%s" % dep)
|
print("\t\t\t%s" % dep)
|
||||||
print("")
|
print("")
|
||||||
try:
|
try:
|
||||||
outputs = list(buildsystem.get_declared_outputs(session))
|
outputs = list(buildsystem.get_declared_outputs(session, fixers=fixers))
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print("\tUnable to detect declared outputs for this type of build system")
|
print("\tUnable to detect declared outputs for this type of build system")
|
||||||
outputs = []
|
outputs = []
|
||||||
|
|
|
@ -82,6 +82,15 @@ class PythonPackageRequirement(Requirement):
|
||||||
return p.returncode == 0
|
return p.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
class LatexPackageRequirement(Requirement):
|
||||||
|
|
||||||
|
def __init__(self, package: str):
|
||||||
|
self.package = package
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (type(self).__name__, self.package)
|
||||||
|
|
||||||
|
|
||||||
class PhpPackageRequirement(Requirement):
|
class PhpPackageRequirement(Requirement):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -192,6 +201,32 @@ class NodePackageRequirement(Requirement):
|
||||||
return "%s(%r)" % (type(self).__name__, self.package)
|
return "%s(%r)" % (type(self).__name__, self.package)
|
||||||
|
|
||||||
|
|
||||||
|
class PerlPreDeclaredRequirement(Requirement):
|
||||||
|
|
||||||
|
name: str
|
||||||
|
|
||||||
|
# TODO(jelmer): Can we obtain this information elsewhere?
|
||||||
|
KNOWN_MODULES = {
|
||||||
|
'auto_set_repository': 'Module::Install::Repository',
|
||||||
|
'author_tests': 'Module::Install::AuthorTests',
|
||||||
|
'readme_from': 'Module::Install::ReadmeFromPod',
|
||||||
|
'catalyst': 'Module::Install::Catalyst',
|
||||||
|
'githubmeta': 'Module::Install::GithubMeta',
|
||||||
|
'use_ppport': 'Module::Install::XSUtil',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super(PerlPreDeclaredRequirement, self).__init__("perl-predeclared")
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def lookup_module(self):
|
||||||
|
module = self.KNOWN_MODULES[self.name]
|
||||||
|
return PerlModuleRequirement(module=module)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (type(self).__name__, self.name)
|
||||||
|
|
||||||
|
|
||||||
class NodeModuleRequirement(Requirement):
|
class NodeModuleRequirement(Requirement):
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
|
@ -246,6 +281,10 @@ class PkgConfigRequirement(Requirement):
|
||||||
self.module = module
|
self.module = module
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r, minimum_version=%r)" % (
|
||||||
|
type(self).__name__, self.module, self.minimum_version)
|
||||||
|
|
||||||
|
|
||||||
class PathRequirement(Requirement):
|
class PathRequirement(Requirement):
|
||||||
|
|
||||||
|
@ -255,6 +294,9 @@ class PathRequirement(Requirement):
|
||||||
super(PathRequirement, self).__init__("path")
|
super(PathRequirement, self).__init__("path")
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (type(self).__name__, self.path)
|
||||||
|
|
||||||
|
|
||||||
class CHeaderRequirement(Requirement):
|
class CHeaderRequirement(Requirement):
|
||||||
|
|
||||||
|
@ -264,6 +306,9 @@ class CHeaderRequirement(Requirement):
|
||||||
super(CHeaderRequirement, self).__init__("c-header")
|
super(CHeaderRequirement, self).__init__("c-header")
|
||||||
self.header = header
|
self.header = header
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (type(self).__name__, self.header)
|
||||||
|
|
||||||
|
|
||||||
class JavaScriptRuntimeRequirement(Requirement):
|
class JavaScriptRuntimeRequirement(Requirement):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -587,6 +632,11 @@ class LibtoolRequirement(Requirement):
|
||||||
super(LibtoolRequirement, self).__init__("libtool")
|
super(LibtoolRequirement, self).__init__("libtool")
|
||||||
|
|
||||||
|
|
||||||
|
class IntrospectionTypelibRequirement(Requirement):
|
||||||
|
def __init__(self, library):
|
||||||
|
self.library = library
|
||||||
|
|
||||||
|
|
||||||
class PythonModuleRequirement(Requirement):
|
class PythonModuleRequirement(Requirement):
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
|
@ -619,3 +669,8 @@ class PythonModuleRequirement(Requirement):
|
||||||
)
|
)
|
||||||
p.communicate()
|
p.communicate()
|
||||||
return p.returncode == 0
|
return p.returncode == 0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r, python_version=%r, minimum_version=%r)" % (
|
||||||
|
type(self).__name__, self.module, self.python_version,
|
||||||
|
self.minimum_version)
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from .. import UnidentifiedError
|
||||||
from ..fix_build import run_detecting_problems
|
from ..fix_build import run_detecting_problems
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ class CPANResolver(Resolver):
|
||||||
continue
|
continue
|
||||||
perlreqs.append(requirement)
|
perlreqs.append(requirement)
|
||||||
if perlreqs:
|
if perlreqs:
|
||||||
yield (self._cmd(perlreqs), [perlreqs])
|
yield (self._cmd(perlreqs), perlreqs)
|
||||||
|
|
||||||
def install(self, requirements):
|
def install(self, requirements):
|
||||||
from ..requirements import PerlModuleRequirement
|
from ..requirements import PerlModuleRequirement
|
||||||
|
@ -88,9 +90,11 @@ class CPANResolver(Resolver):
|
||||||
if not isinstance(requirement, PerlModuleRequirement):
|
if not isinstance(requirement, PerlModuleRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
|
cmd = self._cmd([requirement])
|
||||||
|
logging.info("CPAN: running %r", cmd)
|
||||||
run_detecting_problems(
|
run_detecting_problems(
|
||||||
self.session,
|
self.session,
|
||||||
self._cmd([requirement]),
|
cmd,
|
||||||
env=env,
|
env=env,
|
||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
|
@ -98,6 +102,73 @@ class CPANResolver(Resolver):
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
|
||||||
|
class TlmgrResolver(Resolver):
|
||||||
|
def __init__(self, session, repository: str, user_local=False):
|
||||||
|
self.session = session
|
||||||
|
self.user_local = user_local
|
||||||
|
self.repository = repository
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.repository.startswith('http://') or self.repository.startswith('https://'):
|
||||||
|
return 'tlmgr(%r)' % self.repository
|
||||||
|
else:
|
||||||
|
return self.repository
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r, %r)" % (
|
||||||
|
type(self).__name__, self.session, self.repository)
|
||||||
|
|
||||||
|
def _cmd(self, reqs):
|
||||||
|
ret = ["tlmgr", "--repository=%s" % self.repository, "install"]
|
||||||
|
if self.user_local:
|
||||||
|
ret.append("--usermode")
|
||||||
|
ret.extend([req.package for req in reqs])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def explain(self, requirements):
|
||||||
|
from ..requirements import LatexPackageRequirement
|
||||||
|
|
||||||
|
latexreqs = []
|
||||||
|
for requirement in requirements:
|
||||||
|
if not isinstance(requirement, LatexPackageRequirement):
|
||||||
|
continue
|
||||||
|
latexreqs.append(requirement)
|
||||||
|
if latexreqs:
|
||||||
|
yield (self._cmd(latexreqs), latexreqs)
|
||||||
|
|
||||||
|
def install(self, requirements):
|
||||||
|
from ..requirements import LatexPackageRequirement
|
||||||
|
|
||||||
|
if not self.user_local:
|
||||||
|
user = "root"
|
||||||
|
else:
|
||||||
|
user = None
|
||||||
|
|
||||||
|
missing = []
|
||||||
|
for requirement in requirements:
|
||||||
|
if not isinstance(requirement, LatexPackageRequirement):
|
||||||
|
missing.append(requirement)
|
||||||
|
continue
|
||||||
|
cmd = self._cmd([requirement])
|
||||||
|
logging.info("tlmgr: running %r", cmd)
|
||||||
|
try:
|
||||||
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
|
except UnidentifiedError as e:
|
||||||
|
if "tlmgr: user mode not initialized, please read the documentation!" in e.lines:
|
||||||
|
self.session.check_call(['tlmgr', 'init-usertree'])
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
if missing:
|
||||||
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
|
||||||
|
class CTANResolver(TlmgrResolver):
|
||||||
|
|
||||||
|
def __init__(self, session, user_local=False):
|
||||||
|
super(CTANResolver, self).__init__(
|
||||||
|
session, "ctan", user_local=user_local)
|
||||||
|
|
||||||
|
|
||||||
class RResolver(Resolver):
|
class RResolver(Resolver):
|
||||||
def __init__(self, session, repos, user_local=False):
|
def __init__(self, session, repos, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
@ -142,7 +213,9 @@ class RResolver(Resolver):
|
||||||
if not isinstance(requirement, RPackageRequirement):
|
if not isinstance(requirement, RPackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
self.session.check_call(self._cmd(requirement), user=user)
|
cmd = self._cmd(requirement)
|
||||||
|
logging.info("RResolver(%r): running %r", self.repos, cmd)
|
||||||
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
if missing:
|
if missing:
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
@ -186,7 +259,9 @@ class OctaveForgeResolver(Resolver):
|
||||||
if not isinstance(requirement, OctavePackageRequirement):
|
if not isinstance(requirement, OctavePackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
self.session.check_call(self._cmd(requirement), user=user)
|
cmd = self._cmd(requirement)
|
||||||
|
logging.info("Octave: running %r", cmd)
|
||||||
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
if missing:
|
if missing:
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
@ -235,7 +310,9 @@ class HackageResolver(Resolver):
|
||||||
if not isinstance(requirement, HaskellPackageRequirement):
|
if not isinstance(requirement, HaskellPackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
self.session.check_call(self._cmd([requirement]), user=user)
|
cmd = self._cmd([requirement])
|
||||||
|
logging.info("Hackage: running %r", cmd)
|
||||||
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
if missing:
|
if missing:
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
@ -281,8 +358,10 @@ class PypiResolver(Resolver):
|
||||||
if not isinstance(requirement, PythonPackageRequirement):
|
if not isinstance(requirement, PythonPackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
|
cmd = self._cmd([requirement])
|
||||||
|
logging.info("pip: running %r", cmd)
|
||||||
try:
|
try:
|
||||||
self.session.check_call(self._cmd([requirement]), user=user)
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
if missing:
|
if missing:
|
||||||
|
@ -325,7 +404,9 @@ class GoResolver(Resolver):
|
||||||
if not isinstance(requirement, GoPackageRequirement):
|
if not isinstance(requirement, GoPackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
self.session.check_call(["go", "get", requirement.package], env=env)
|
cmd = ["go", "get", requirement.package]
|
||||||
|
logging.info("go: running %r", cmd)
|
||||||
|
run_detecting_problems(self.session, cmd, env=env)
|
||||||
if missing:
|
if missing:
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
@ -344,6 +425,7 @@ class GoResolver(Resolver):
|
||||||
NPM_COMMAND_PACKAGES = {
|
NPM_COMMAND_PACKAGES = {
|
||||||
"del-cli": "del-cli",
|
"del-cli": "del-cli",
|
||||||
"husky": "husky",
|
"husky": "husky",
|
||||||
|
"cross-env": "cross-env",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -382,13 +464,17 @@ class NpmResolver(Resolver):
|
||||||
requirement = NodePackageRequirement(package)
|
requirement = NodePackageRequirement(package)
|
||||||
if isinstance(requirement, NodeModuleRequirement):
|
if isinstance(requirement, NodeModuleRequirement):
|
||||||
# TODO: Is this legit?
|
# TODO: Is this legit?
|
||||||
requirement = NodePackageRequirement(requirement.module.split("/")[0])
|
parts = requirement.module.split("/")
|
||||||
|
if parts[0].startswith('@'):
|
||||||
|
requirement = NodePackageRequirement('/'.join(parts[:2]))
|
||||||
|
else:
|
||||||
|
requirement = NodePackageRequirement(parts[0])
|
||||||
if not isinstance(requirement, NodePackageRequirement):
|
if not isinstance(requirement, NodePackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
self.session.check_call(
|
cmd = ["npm", "-g", "install", requirement.package]
|
||||||
["npm", "-g", "install", requirement.package], user=user
|
logging.info("npm: running %r", cmd)
|
||||||
)
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
if missing:
|
if missing:
|
||||||
raise UnsatisfiedRequirements(missing)
|
raise UnsatisfiedRequirements(missing)
|
||||||
|
|
||||||
|
@ -445,6 +531,7 @@ class StackedResolver(Resolver):
|
||||||
|
|
||||||
NATIVE_RESOLVER_CLS = [
|
NATIVE_RESOLVER_CLS = [
|
||||||
CPANResolver,
|
CPANResolver,
|
||||||
|
CTANResolver,
|
||||||
PypiResolver,
|
PypiResolver,
|
||||||
NpmResolver,
|
NpmResolver,
|
||||||
GoResolver,
|
GoResolver,
|
||||||
|
|
|
@ -68,6 +68,8 @@ from ..requirements import (
|
||||||
CertificateAuthorityRequirement,
|
CertificateAuthorityRequirement,
|
||||||
LibtoolRequirement,
|
LibtoolRequirement,
|
||||||
VagueDependencyRequirement,
|
VagueDependencyRequirement,
|
||||||
|
PerlPreDeclaredRequirement,
|
||||||
|
IntrospectionTypelibRequirement,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,6 +117,36 @@ class AptRequirement(Requirement):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def satisfied_by(self, binaries, version):
|
||||||
|
def binary_pkg_matches(entry, binary):
|
||||||
|
# TODO(jelmer): check versions
|
||||||
|
if entry['name'] == binary['Package']:
|
||||||
|
return True
|
||||||
|
for provides_top in PkgRelation.parse_relations(
|
||||||
|
binary.get('Provides', '')):
|
||||||
|
for provides in provides_top:
|
||||||
|
if entry['name'] == provides['name']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
for rel in self.relations:
|
||||||
|
for entry in rel:
|
||||||
|
if any(binary_pkg_matches(entry, binary) for binary in binaries):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_perl_predeclared_req(apt_mgr, req):
|
||||||
|
try:
|
||||||
|
req = req.lookup_module()
|
||||||
|
except KeyError:
|
||||||
|
logging.warning(
|
||||||
|
'Unable to map predeclared function %s to a perl module', req.name)
|
||||||
|
return None
|
||||||
|
return resolve_perl_module_req(apt_mgr, req)
|
||||||
|
|
||||||
|
|
||||||
def find_package_names(
|
def find_package_names(
|
||||||
apt_mgr: AptManager, paths: List[str], regex: bool = False, case_insensitive=False
|
apt_mgr: AptManager, paths: List[str], regex: bool = False, case_insensitive=False
|
||||||
|
@ -282,6 +314,17 @@ def get_package_for_python_module(apt_mgr, module, python_version, specs):
|
||||||
vague_map = {
|
vague_map = {
|
||||||
"the Gnu Scientific Library": "libgsl-dev",
|
"the Gnu Scientific Library": "libgsl-dev",
|
||||||
"the required FreeType library": "libfreetype-dev",
|
"the required FreeType library": "libfreetype-dev",
|
||||||
|
"the Boost C++ libraries": "libboost-dev",
|
||||||
|
|
||||||
|
# TODO(jelmer): Support resolving virtual packages
|
||||||
|
"PythonLibs": "libpython3-dev",
|
||||||
|
"ZLIB": "libz3-dev",
|
||||||
|
"Osmium": "libosmium2-dev",
|
||||||
|
"glib": "libglib2.0-dev",
|
||||||
|
|
||||||
|
# TODO(jelmer): For Python, check minimum_version and map to python 2 or python 3
|
||||||
|
"Python": "libpython3-dev",
|
||||||
|
"Lua": "liblua5.4-dev",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,7 +332,7 @@ def resolve_vague_dep_req(apt_mgr, req):
|
||||||
name = req.name
|
name = req.name
|
||||||
options = []
|
options = []
|
||||||
if name in vague_map:
|
if name in vague_map:
|
||||||
options.append(AptRequirement.simple(vague_map[name]))
|
options.append(AptRequirement.simple(vague_map[name], minimum_version=req.minimum_version))
|
||||||
for x in req.expand():
|
for x in req.expand():
|
||||||
options.extend(resolve_requirement_apt(apt_mgr, x))
|
options.extend(resolve_requirement_apt(apt_mgr, x))
|
||||||
return options
|
return options
|
||||||
|
@ -393,9 +436,9 @@ def resolve_php_package_req(apt_mgr, req):
|
||||||
|
|
||||||
def resolve_r_package_req(apt_mgr, req):
|
def resolve_r_package_req(apt_mgr, req):
|
||||||
paths = [
|
paths = [
|
||||||
posixpath.join("/usr/lib/R/site-library/.*/R/%s$" % re.escape(req.package))
|
posixpath.join("/usr/lib/R/site-library", req.package, "DESCRIPTION")
|
||||||
]
|
]
|
||||||
return find_reqs_simple(apt_mgr, paths, regex=True)
|
return find_reqs_simple(apt_mgr, paths, minimum_version=req.minimum_version)
|
||||||
|
|
||||||
|
|
||||||
def resolve_node_module_req(apt_mgr, req):
|
def resolve_node_module_req(apt_mgr, req):
|
||||||
|
@ -485,7 +528,7 @@ def resolve_cmake_file_req(apt_mgr, req):
|
||||||
|
|
||||||
|
|
||||||
def resolve_haskell_package_req(apt_mgr, req):
|
def resolve_haskell_package_req(apt_mgr, req):
|
||||||
path = "/var/lib/ghc/package\\.conf\\.d/%s-.*\\.conf" % re.escape(req.deps[0][0])
|
path = "/var/lib/ghc/package\\.conf\\.d/%s-.*\\.conf" % re.escape(req.package)
|
||||||
return find_reqs_simple(apt_mgr, [path], regex=True)
|
return find_reqs_simple(apt_mgr, [path], regex=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -620,6 +663,12 @@ def resolve_ca_req(apt_mgr, req):
|
||||||
return [AptRequirement.simple("ca-certificates")]
|
return [AptRequirement.simple("ca-certificates")]
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_introspection_typelib_req(apt_mgr, req):
|
||||||
|
return find_reqs_simple(
|
||||||
|
apt_mgr, [r'/usr/lib/.*/girepository-.*/%s-.*\.typelib' % re.escape(req.library)],
|
||||||
|
regex=True)
|
||||||
|
|
||||||
|
|
||||||
def resolve_apt_req(apt_mgr, req):
|
def resolve_apt_req(apt_mgr, req):
|
||||||
# TODO(jelmer): This should be checking whether versions match as well.
|
# TODO(jelmer): This should be checking whether versions match as well.
|
||||||
for package_name in req.package_names():
|
for package_name in req.package_names():
|
||||||
|
@ -632,6 +681,7 @@ APT_REQUIREMENT_RESOLVERS = [
|
||||||
(AptRequirement, resolve_apt_req),
|
(AptRequirement, resolve_apt_req),
|
||||||
(BinaryRequirement, resolve_binary_req),
|
(BinaryRequirement, resolve_binary_req),
|
||||||
(VagueDependencyRequirement, resolve_vague_dep_req),
|
(VagueDependencyRequirement, resolve_vague_dep_req),
|
||||||
|
(PerlPreDeclaredRequirement, resolve_perl_predeclared_req),
|
||||||
(PkgConfigRequirement, resolve_pkg_config_req),
|
(PkgConfigRequirement, resolve_pkg_config_req),
|
||||||
(PathRequirement, resolve_path_req),
|
(PathRequirement, resolve_path_req),
|
||||||
(CHeaderRequirement, resolve_c_header_req),
|
(CHeaderRequirement, resolve_c_header_req),
|
||||||
|
@ -668,6 +718,7 @@ APT_REQUIREMENT_RESOLVERS = [
|
||||||
(PythonPackageRequirement, resolve_python_package_req),
|
(PythonPackageRequirement, resolve_python_package_req),
|
||||||
(CertificateAuthorityRequirement, resolve_ca_req),
|
(CertificateAuthorityRequirement, resolve_ca_req),
|
||||||
(CargoCrateRequirement, resolve_cargo_crate_req),
|
(CargoCrateRequirement, resolve_cargo_crate_req),
|
||||||
|
(IntrospectionTypelibRequirement, resolve_introspection_typelib_req),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -683,11 +734,20 @@ def resolve_requirement_apt(apt_mgr, req: Requirement) -> List[AptRequirement]:
|
||||||
raise NotImplementedError(type(req))
|
raise NotImplementedError(type(req))
|
||||||
|
|
||||||
|
|
||||||
|
def default_tie_breakers(session):
|
||||||
|
from ..debian.udd import popcon_tie_breaker
|
||||||
|
from ..debian.build_deps import BuildDependencyTieBreaker
|
||||||
|
return [
|
||||||
|
BuildDependencyTieBreaker.from_session(session),
|
||||||
|
popcon_tie_breaker,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class AptResolver(Resolver):
|
class AptResolver(Resolver):
|
||||||
def __init__(self, apt, tie_breakers=None):
|
def __init__(self, apt, tie_breakers=None):
|
||||||
self.apt = apt
|
self.apt = apt
|
||||||
if tie_breakers is None:
|
if tie_breakers is None:
|
||||||
tie_breakers = []
|
tie_breakers = default_tie_breakers(apt.session)
|
||||||
self.tie_breakers = tie_breakers
|
self.tie_breakers = tie_breakers
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -185,6 +185,8 @@ class SchrootSession(Session):
|
||||||
self.check_call(["chown", user, home], cwd="/", user="root")
|
self.check_call(["chown", user, home], cwd="/", user="root")
|
||||||
|
|
||||||
def external_path(self, path: str) -> str:
|
def external_path(self, path: str) -> str:
|
||||||
|
if os.path.isabs(path):
|
||||||
|
return os.path.join(self.location, path.lstrip("/"))
|
||||||
if self._cwd is None:
|
if self._cwd is None:
|
||||||
raise ValueError("no cwd set")
|
raise ValueError("no cwd set")
|
||||||
return os.path.join(self.location, os.path.join(self._cwd, path).lstrip("/"))
|
return os.path.join(self.location, os.path.join(self._cwd, path).lstrip("/"))
|
||||||
|
|
|
@ -53,17 +53,12 @@ janitor (0.1-1) UNRELEASED; urgency=medium
|
||||||
)
|
)
|
||||||
self.assertFileEqual(
|
self.assertFileEqual(
|
||||||
"""\
|
"""\
|
||||||
janitor (0.1-1jan+some1) some-fixes; urgency=low
|
janitor (0.1-1jan+some1) some-fixes; urgency=medium
|
||||||
|
|
||||||
|
* Initial release. (Closes: #XXXXXX)
|
||||||
* Dummy build.
|
* Dummy build.
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
||||||
|
|
||||||
janitor (0.1-1) UNRELEASED; urgency=medium
|
|
||||||
|
|
||||||
* Initial release. (Closes: #XXXXXX)
|
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 04 Apr 2020 14:12:13 +0000
|
|
||||||
""",
|
""",
|
||||||
"debian/changelog",
|
"debian/changelog",
|
||||||
)
|
)
|
||||||
|
@ -97,17 +92,12 @@ janitor (0.1) UNRELEASED; urgency=medium
|
||||||
)
|
)
|
||||||
self.assertFileEqual(
|
self.assertFileEqual(
|
||||||
"""\
|
"""\
|
||||||
janitor (0.1jan+some1) some-fixes; urgency=low
|
janitor (0.1jan+some1) some-fixes; urgency=medium
|
||||||
|
|
||||||
|
* Initial release. (Closes: #XXXXXX)
|
||||||
* Dummy build.
|
* Dummy build.
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
||||||
|
|
||||||
janitor (0.1) UNRELEASED; urgency=medium
|
|
||||||
|
|
||||||
* Initial release. (Closes: #XXXXXX)
|
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 04 Apr 2020 14:12:13 +0000
|
|
||||||
""",
|
""",
|
||||||
"debian/changelog",
|
"debian/changelog",
|
||||||
)
|
)
|
||||||
|
@ -141,17 +131,12 @@ janitor (0.1-1jan+some1) UNRELEASED; urgency=medium
|
||||||
)
|
)
|
||||||
self.assertFileEqual(
|
self.assertFileEqual(
|
||||||
"""\
|
"""\
|
||||||
janitor (0.1-1jan+some2) some-fixes; urgency=low
|
janitor (0.1-1jan+some2) some-fixes; urgency=medium
|
||||||
|
|
||||||
|
* Initial release. (Closes: #XXXXXX)
|
||||||
* Dummy build.
|
* Dummy build.
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
-- Jelmer Vernooij <jelmer@debian.org> Sat, 05 Sep 2020 12:35:04 -0000
|
||||||
|
|
||||||
janitor (0.1-1jan+some1) UNRELEASED; urgency=medium
|
|
||||||
|
|
||||||
* Initial release. (Closes: #XXXXXX)
|
|
||||||
|
|
||||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 04 Apr 2020 14:12:13 +0000
|
|
||||||
""",
|
""",
|
||||||
"debian/changelog",
|
"debian/changelog",
|
||||||
)
|
)
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -6,12 +6,12 @@ from setuptools import setup
|
||||||
|
|
||||||
setup(name="ognibuild",
|
setup(name="ognibuild",
|
||||||
description="Detect and run any build system",
|
description="Detect and run any build system",
|
||||||
version="0.0.3",
|
version="0.0.4",
|
||||||
maintainer="Jelmer Vernooij",
|
maintainer="Jelmer Vernooij",
|
||||||
maintainer_email="jelmer@jelmer.uk",
|
maintainer_email="jelmer@jelmer.uk",
|
||||||
license="GNU GPLv2 or later",
|
license="GNU GPLv2 or later",
|
||||||
url="https://jelmer.uk/code/ognibuild",
|
url="https://jelmer.uk/code/ognibuild",
|
||||||
packages=['ognibuild'],
|
packages=['ognibuild', 'ognibuild.tests', 'ognibuild.debian', 'ognibuild.resolver', 'ognibuild.session'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
'License :: OSI Approved :: '
|
'License :: OSI Approved :: '
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue