Don't invoke apt unless we have to.

This commit is contained in:
Jelmer Vernooij 2021-02-06 18:35:23 +00:00
parent 8133a5fa68
commit db616ee3b0
No known key found for this signature in database
GPG key ID: 579C160D4C9E23E8
4 changed files with 60 additions and 33 deletions

View file

@ -19,6 +19,8 @@
from typing import List from typing import List
import apt_pkg
import os
from buildlog_consultant.sbuild import ( from buildlog_consultant.sbuild import (
find_apt_get_failure, find_apt_get_failure,
) )
@ -53,9 +55,29 @@ def run_apt(session: Session, args: List[str]) -> None:
raise UnidentifiedError(retcode, args, lines) raise UnidentifiedError(retcode, args, lines)
def install(session: Session, packages: List[str]) -> None: class AptResolver(object):
run_apt(session, ['install'] + packages)
session: Session
def satisfy(session: Session, deps: List[str]) -> None: def __init__(self, session):
run_apt(session, ['satisfy'] + deps) self.session = session
def missing(self, packages):
root = getattr(self.session, 'location', '/')
status_path = os.path.join(root, 'var/lib/dpkg/status')
missing = set(packages)
with apt_pkg.TagFile(status_path) as tagf:
while tagf and missing:
tagf.step()
if tagf.section['Package'] in missing:
if tagf.section['Status'] == 'install ok installed':
missing.remove(tagf.section['Package'])
return list(missing)
def install(self, packages: List[str]) -> None:
packages = self.missing(packages)
if packages:
run_apt(self.session, ['install'] + packages)
def satisfy(self, deps: List[str]) -> None:
run_apt(self.session, ['satisfy'] + deps)

View file

@ -33,7 +33,8 @@ from breezy.workingtree import WorkingTree
from breezy.plugins.debian.repack_tarball import get_filetype from breezy.plugins.debian.repack_tarball import get_filetype
from . import apt, DetailedFailure, shebang_binary from . import DetailedFailure, shebang_binary
from .apt import AptResolver, UnidentifiedError
from .buildsystem import detect_buildsystems, NoBuildToolsFound from .buildsystem import detect_buildsystems, NoBuildToolsFound
from .session import run_with_tee, Session from .session import run_with_tee, Session
from .session.schroot import SchrootSession from .session.schroot import SchrootSession
@ -72,17 +73,19 @@ def satisfy_build_deps(session: Session, tree):
deps = [ deps = [
dep.strip().strip(',') dep.strip().strip(',')
for dep in deps] for dep in deps]
apt.satisfy(session, deps) apt = AptResolver(session)
apt.satisfy(deps)
class SchrootDependencyContext(DependencyContext): class SchrootDependencyContext(DependencyContext):
def __init__(self, session): def __init__(self, session):
self.session = session self.session = session
self.apt = AptResolver(session)
def add_dependency(self, package, minimum_version=None): def add_dependency(self, package, minimum_version=None):
# TODO(jelmer): Handle minimum_version # TODO(jelmer): Handle minimum_version
apt.install(self.session, [package]) self.apt.install([package])
return True return True
@ -127,9 +130,9 @@ def run_with_build_fixer(session: Session, args: List[str]):
if error is None: if error is None:
logging.warning('Build failed with unidentified error. Giving up.') logging.warning('Build failed with unidentified error. Giving up.')
if line is not None: if line is not None:
raise apt.UnidentifiedError( raise UnidentifiedError(
retcode, args, lines, secondary=(offset, line)) retcode, args, lines, secondary=(offset, line))
raise apt.UnidentifiedError(retcode, args, lines) raise UnidentifiedError(retcode, args, lines)
logging.info('Identified error: %r', error) logging.info('Identified error: %r', error)
if error in fixed_errors: if error in fixed_errors:
@ -148,7 +151,8 @@ def run_with_build_fixer(session: Session, args: List[str]):
def run_dist(session): def run_dist(session):
apt.install(session, ['git']) apt = AptResolver(session)
apt.install(['git'])
# Some things want to write to the user's home directory, # Some things want to write to the user's home directory,
# e.g. pip caches in ~/.cache # e.g. pip caches in ~/.cache
@ -159,7 +163,7 @@ def run_dist(session):
return return
if os.path.exists('package.xml'): if os.path.exists('package.xml'):
apt.install(session, ['php-pear', 'php-horde-core']) apt.install(['php-pear', 'php-horde-core'])
logging.info('Found package.xml, assuming pear package.') logging.info('Found package.xml, assuming pear package.')
session.check_call(['pear', 'package']) session.check_call(['pear', 'package'])
return return
@ -172,14 +176,14 @@ def run_dist(session):
logging.info( logging.info(
'Found pyproject.toml with poetry section, ' 'Found pyproject.toml with poetry section, '
'assuming poetry project.') 'assuming poetry project.')
apt.install(session, ['python3-venv', 'python3-pip']) apt.install(['python3-venv', 'python3-pip'])
session.check_call(['pip3', 'install', 'poetry'], user='root') session.check_call(['pip3', 'install', 'poetry'], user='root')
session.check_call(['poetry', 'build', '-f', 'sdist']) session.check_call(['poetry', 'build', '-f', 'sdist'])
return return
if os.path.exists('setup.py'): if os.path.exists('setup.py'):
logging.info('Found setup.py, assuming python project.') logging.info('Found setup.py, assuming python project.')
apt.install(session, ['python3', 'python3-pip']) apt.install(['python3', 'python3-pip'])
with open('setup.py', 'r') as f: with open('setup.py', 'r') as f:
setup_py_contents = f.read() setup_py_contents = f.read()
try: try:
@ -189,41 +193,40 @@ def run_dist(session):
setup_cfg_contents = '' setup_cfg_contents = ''
if 'setuptools' in setup_py_contents: if 'setuptools' in setup_py_contents:
logging.info('Reference to setuptools found, installing.') logging.info('Reference to setuptools found, installing.')
apt.install(session, ['python3-setuptools']) apt.install(['python3-setuptools'])
if ('setuptools_scm' in setup_py_contents or if ('setuptools_scm' in setup_py_contents or
'setuptools_scm' in setup_cfg_contents): 'setuptools_scm' in setup_cfg_contents):
logging.info('Reference to setuptools-scm found, installing.') logging.info('Reference to setuptools-scm found, installing.')
apt.install( apt.install(['python3-setuptools-scm', 'git', 'mercurial'])
session, ['python3-setuptools-scm', 'git', 'mercurial'])
# TODO(jelmer): Install setup_requires # TODO(jelmer): Install setup_requires
interpreter = shebang_binary('setup.py') interpreter = shebang_binary('setup.py')
if interpreter is not None: if interpreter is not None:
if interpreter == 'python3': if interpreter == 'python3':
apt.install(session, ['python3']) apt.install(['python3'])
elif interpreter == 'python2': elif interpreter == 'python2':
apt.install(session, ['python2']) apt.install(['python2'])
elif interpreter == 'python': elif interpreter == 'python':
apt.install(session, ['python']) apt.install(['python'])
else: else:
raise ValueError('Unknown interpreter %r' % interpreter) raise ValueError('Unknown interpreter %r' % interpreter)
apt.install(session, ['python2', 'python3']) apt.install(['python2', 'python3'])
run_with_build_fixer(session, ['./setup.py', 'sdist']) run_with_build_fixer(session, ['./setup.py', 'sdist'])
else: else:
# Just assume it's Python 3 # Just assume it's Python 3
apt.install(session, ['python3']) apt.install(['python3'])
run_with_build_fixer(session, ['python3', './setup.py', 'sdist']) run_with_build_fixer(session, ['python3', './setup.py', 'sdist'])
return return
if os.path.exists('setup.cfg'): if os.path.exists('setup.cfg'):
logging.info('Found setup.cfg, assuming python project.') logging.info('Found setup.cfg, assuming python project.')
apt.install(session, ['python3-pep517', 'python3-pip']) apt.install(['python3-pep517', 'python3-pip'])
session.check_call(['python3', '-m', 'pep517.build', '-s', '.']) session.check_call(['python3', '-m', 'pep517.build', '-s', '.'])
return return
if os.path.exists('dist.ini') and not os.path.exists('Makefile.PL'): if os.path.exists('dist.ini') and not os.path.exists('Makefile.PL'):
apt.install(session, ['libdist-inkt-perl']) apt.install(['libdist-inkt-perl'])
with open('dist.ini', 'rb') as f: with open('dist.ini', 'rb') as f:
for line in f: for line in f:
if not line.startswith(b';;'): if not line.startswith(b';;'):
@ -245,30 +248,30 @@ def run_dist(session):
return return
# Default to invoking Dist::Zilla # Default to invoking Dist::Zilla
logging.info('Found dist.ini, assuming dist-zilla.') logging.info('Found dist.ini, assuming dist-zilla.')
apt.install(session, ['libdist-zilla-perl']) apt.install(['libdist-zilla-perl'])
run_with_build_fixer(session, ['dzil', 'build', '--in', '..']) run_with_build_fixer(session, ['dzil', 'build', '--in', '..'])
return return
if os.path.exists('package.json'): if os.path.exists('package.json'):
apt.install(session, ['npm']) apt.install(['npm'])
run_with_build_fixer(session, ['npm', 'pack']) run_with_build_fixer(session, ['npm', 'pack'])
return return
gemfiles = [name for name in os.listdir('.') if name.endswith('.gem')] gemfiles = [name for name in os.listdir('.') if name.endswith('.gem')]
if gemfiles: if gemfiles:
apt.install(session, ['gem2deb']) apt.install(['gem2deb'])
if len(gemfiles) > 1: if len(gemfiles) > 1:
logging.warning('More than one gemfile. Trying the first?') logging.warning('More than one gemfile. Trying the first?')
run_with_build_fixer(session, ['gem2tgz', gemfiles[0]]) run_with_build_fixer(session, ['gem2tgz', gemfiles[0]])
return return
if os.path.exists('waf'): if os.path.exists('waf'):
apt.install(session, ['python3']) apt.install(['python3'])
run_with_build_fixer(session, ['./waf', 'dist']) run_with_build_fixer(session, ['./waf', 'dist'])
return return
if os.path.exists('Makefile.PL') and not os.path.exists('Makefile'): if os.path.exists('Makefile.PL') and not os.path.exists('Makefile'):
apt.install(session, ['perl']) apt.install(['perl'])
run_with_build_fixer(session, ['perl', 'Makefile.PL']) run_with_build_fixer(session, ['perl', 'Makefile.PL'])
if not os.path.exists('Makefile') and not os.path.exists('configure'): if not os.path.exists('Makefile') and not os.path.exists('configure'):
@ -277,7 +280,7 @@ def run_dist(session):
run_with_build_fixer(session, ['/bin/sh', './autogen.sh']) run_with_build_fixer(session, ['/bin/sh', './autogen.sh'])
try: try:
run_with_build_fixer(session, ['./autogen.sh']) run_with_build_fixer(session, ['./autogen.sh'])
except apt.UnidentifiedError as e: except UnidentifiedError as e:
if ("Gnulib not yet bootstrapped; " if ("Gnulib not yet bootstrapped; "
"run ./bootstrap instead.\n" in e.lines): "run ./bootstrap instead.\n" in e.lines):
run_with_build_fixer(session, ["./bootstrap"]) run_with_build_fixer(session, ["./bootstrap"])
@ -286,7 +289,7 @@ def run_dist(session):
raise raise
elif os.path.exists('configure.ac') or os.path.exists('configure.in'): elif os.path.exists('configure.ac') or os.path.exists('configure.in'):
apt.install(session, [ apt.install([
'autoconf', 'automake', 'gettext', 'libtool', 'gnu-standards']) 'autoconf', 'automake', 'gettext', 'libtool', 'gnu-standards'])
run_with_build_fixer(session, ['autoreconf', '-i']) run_with_build_fixer(session, ['autoreconf', '-i'])
@ -294,10 +297,10 @@ def run_dist(session):
session.check_call(['./configure']) session.check_call(['./configure'])
if os.path.exists('Makefile'): if os.path.exists('Makefile'):
apt.install(session, ['make']) apt.install(['make'])
try: try:
run_with_build_fixer(session, ['make', 'dist']) run_with_build_fixer(session, ['make', 'dist'])
except apt.UnidentifiedError as e: except UnidentifiedError as e:
if "make: *** No rule to make target 'dist'. Stop.\n" in e.lines: if "make: *** No rule to make target 'dist'. Stop.\n" in e.lines:
pass pass
elif ("make[1]: *** No rule to make target 'dist'. Stop.\n" elif ("make[1]: *** No rule to make target 'dist'. Stop.\n"

View file

@ -34,7 +34,7 @@ class Session(object):
@property @property
def location(self) -> str: def location(self) -> str:
raise NotImplementedError(self.location) raise NotImplementedError
def check_call( def check_call(
self, self,

View file

@ -24,6 +24,8 @@ import subprocess
class PlainSession(Session): class PlainSession(Session):
"""Session ignoring user.""" """Session ignoring user."""
location = '/'
def create_home(self): def create_home(self):
pass pass