Add ognibuild script.
This commit is contained in:
commit
38f6f3feec
1 changed files with 264 additions and 0 deletions
264
ognibuild.py
Normal file
264
ognibuild.py
Normal file
|
@ -0,0 +1,264 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2019-2020 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
# encoding: utf-8
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
|
||||
DEFAULT_PYTHON = 'python3'
|
||||
|
||||
|
||||
class UnidentifiedError(Exception):
|
||||
|
||||
def __init__(self, retcode, argv, lines):
|
||||
self.retcode = retcode
|
||||
self.argv = argv
|
||||
self.lines = lines
|
||||
|
||||
|
||||
class NoBuildToolsFound(Exception):
|
||||
"""No supported build tools were found."""
|
||||
|
||||
|
||||
def shebang_binary(p):
|
||||
if not (os.stat(p).st_mode & stat.S_IEXEC):
|
||||
return None
|
||||
with open(p, 'rb') as f:
|
||||
firstline = f.readline()
|
||||
if not firstline.startswith(b'#!'):
|
||||
return None
|
||||
args = firstline[2:].split(b' ')
|
||||
if args[0] in (b'/usr/bin/env', b'env'):
|
||||
return os.path.basename(args[1].decode())
|
||||
return os.path.basename(args[0].decode())
|
||||
|
||||
|
||||
def note(m):
|
||||
sys.stdout.write('%s\n' % m)
|
||||
|
||||
|
||||
def warning(m):
|
||||
sys.stderr.write('WARNING: %s\n' % m)
|
||||
|
||||
|
||||
def run_with_tee(session, args: List[str], **kwargs):
|
||||
p = session.Popen(
|
||||
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||
contents = []
|
||||
while p.poll() is None:
|
||||
line = p.stdout.readline()
|
||||
sys.stdout.buffer.write(line)
|
||||
sys.stdout.buffer.flush()
|
||||
contents.append(line.decode('utf-8', 'surrogateescape'))
|
||||
return p.returncode, contents
|
||||
|
||||
|
||||
def run_apt(session, args: List[str]) -> None:
|
||||
args = ['apt', '-y'] + args
|
||||
retcode, lines = run_with_tee(session, args, cwd='/', user='root')
|
||||
if retcode == 0:
|
||||
return
|
||||
raise UnidentifiedError(retcode, args, lines)
|
||||
|
||||
|
||||
def apt_install(session, packages: List[str]) -> None:
|
||||
run_apt(session, ['install'] + packages)
|
||||
|
||||
|
||||
def run_with_build_fixer(session, args):
|
||||
session.check_call(args)
|
||||
|
||||
|
||||
def run_dist(session):
|
||||
# TODO(jelmer): Check $PATH rather than hardcoding?
|
||||
if not os.path.exists('/usr/bin/git'):
|
||||
apt_install(session, ['git'])
|
||||
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
if os.path.exists('package.xml'):
|
||||
apt_install(session, ['php-pear', 'php-horde-core'])
|
||||
note('Found package.xml, assuming pear package.')
|
||||
session.check_call(['pear', 'package'])
|
||||
return
|
||||
|
||||
if os.path.exists('pyproject.toml'):
|
||||
import toml
|
||||
with open('pyproject.toml', 'r') as pf:
|
||||
pyproject = toml.load(pf)
|
||||
if 'poetry' in pyproject.get('tool', []):
|
||||
note('Found pyproject.toml with poetry section, '
|
||||
'assuming poetry project.')
|
||||
apt_install(session, ['python3-venv', 'python3-pip'])
|
||||
session.check_call(['pip3', 'install', 'poetry'], user='root')
|
||||
session.check_call(['poetry', 'build', '-f', 'sdist'])
|
||||
return
|
||||
|
||||
if os.path.exists('setup.py'):
|
||||
note('Found setup.py, assuming python project.')
|
||||
apt_install(session, ['python3', 'python3-pip'])
|
||||
with open('setup.py', 'r') as f:
|
||||
setup_py_contents = f.read()
|
||||
try:
|
||||
with open('setup.cfg', 'r') as f:
|
||||
setup_cfg_contents = f.read()
|
||||
except FileNotFoundError:
|
||||
setup_cfg_contents = ''
|
||||
if 'setuptools' in setup_py_contents:
|
||||
note('Reference to setuptools found, installing.')
|
||||
apt_install(session, ['python3-setuptools'])
|
||||
if ('setuptools_scm' in setup_py_contents or
|
||||
'setuptools_scm' in setup_cfg_contents):
|
||||
note('Reference to setuptools-scm found, installing.')
|
||||
apt_install(
|
||||
session, ['python3-setuptools-scm', 'git', 'mercurial'])
|
||||
|
||||
# TODO(jelmer): Install setup_requires
|
||||
|
||||
interpreter = shebang_binary('setup.py')
|
||||
if interpreter is not None:
|
||||
if interpreter == 'python2' or interpreter.startswith('python2.'):
|
||||
apt_install(session, [interpreter])
|
||||
elif (interpreter == 'python3' or
|
||||
interpreter.startswith('python3.')):
|
||||
apt_install(session, [interpreter])
|
||||
else:
|
||||
apt_install(session, [DEFAULT_PYTHON])
|
||||
run_with_build_fixer(session, ['./setup.py', 'sdist'])
|
||||
else:
|
||||
# Just assume it's Python 3
|
||||
apt_install(session, ['python3'])
|
||||
run_with_build_fixer(session, ['python3', './setup.py', 'sdist'])
|
||||
return
|
||||
|
||||
if os.path.exists('setup.cfg'):
|
||||
note('Found setup.cfg, assuming python project.')
|
||||
apt_install(session, ['python3-pep517', 'python3-pip'])
|
||||
session.check_call(['python3', '-m', 'pep517.build', '-s', '.'])
|
||||
return
|
||||
|
||||
if os.path.exists('dist.ini') and not os.path.exists('Makefile.PL'):
|
||||
apt_install(session, ['libdist-inkt-perl'])
|
||||
with open('dist.ini', 'rb') as f:
|
||||
for line in f:
|
||||
if not line.startswith(b';;'):
|
||||
continue
|
||||
try:
|
||||
(key, value) = line[2:].split(b'=', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if (key.strip() == b'class' and
|
||||
value.strip().startswith(b"'Dist::Inkt")):
|
||||
note('Found Dist::Inkt section in dist.ini, '
|
||||
'assuming distinkt.')
|
||||
# TODO(jelmer): install via apt if possible
|
||||
session.check_call(
|
||||
['cpan', 'install', value.decode().strip("'")],
|
||||
user='root')
|
||||
run_with_build_fixer(session, ['distinkt-dist'])
|
||||
return
|
||||
# Default to invoking Dist::Zilla
|
||||
note('Found dist.ini, assuming dist-zilla.')
|
||||
apt_install(session, ['libdist-zilla-perl'])
|
||||
run_with_build_fixer(session, ['dzil', 'build', '--in', '..'])
|
||||
return
|
||||
|
||||
if os.path.exists('package.json'):
|
||||
apt_install(session, ['npm'])
|
||||
run_with_build_fixer(session, ['npm', 'pack'])
|
||||
return
|
||||
|
||||
gemfiles = [name for name in os.listdir('.') if name.endswith('.gem')]
|
||||
if gemfiles:
|
||||
apt_install(session, ['gem2deb'])
|
||||
if len(gemfiles) > 1:
|
||||
warning('More than one gemfile. Trying the first?')
|
||||
run_with_build_fixer(session, ['gem2tgz', gemfiles[0]])
|
||||
return
|
||||
|
||||
if os.path.exists('waf'):
|
||||
apt_install(session, ['python3'])
|
||||
run_with_build_fixer(session, ['./waf', 'dist'])
|
||||
return
|
||||
|
||||
if os.path.exists('Makefile.PL') and not os.path.exists('Makefile'):
|
||||
apt_install(session, ['perl'])
|
||||
run_with_build_fixer(session, ['perl', 'Makefile.PL'])
|
||||
|
||||
if not os.path.exists('Makefile') and not os.path.exists('configure'):
|
||||
if os.path.exists('autogen.sh'):
|
||||
if shebang_binary('autogen.sh') is None:
|
||||
run_with_build_fixer(session, ['/bin/sh', './autogen.sh'])
|
||||
else:
|
||||
run_with_build_fixer(session, ['./autogen.sh'])
|
||||
|
||||
elif os.path.exists('configure.ac') or os.path.exists('configure.in'):
|
||||
apt_install(session, [
|
||||
'autoconf', 'automake', 'gettext', 'libtool', 'gnu-standards'])
|
||||
run_with_build_fixer(session, ['autoreconf', '-i'])
|
||||
|
||||
if not os.path.exists('Makefile') and os.path.exists('configure'):
|
||||
session.check_call(['./configure'])
|
||||
|
||||
if os.path.exists('Makefile'):
|
||||
apt_install(session, ['make'])
|
||||
run_with_build_fixer(session, ['make', 'dist'])
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
||||
|
||||
class PlainSession(object):
|
||||
"""Session ignoring user."""
|
||||
|
||||
def create_home(self):
|
||||
pass
|
||||
|
||||
def check_call(self, args):
|
||||
return subprocess.check_call(args)
|
||||
|
||||
def Popen(self, args, stdout=None, stderr=None, user=None, cwd=None):
|
||||
return subprocess.Popen(
|
||||
args, stdout=stdout, stderr=stderr, cwd=cwd)
|
||||
|
||||
|
||||
def main(argv):
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('subcommand', type=str, choices=['dist'])
|
||||
parser.add_argument(
|
||||
'--directory', '-d', type=str, help='Directory for project.',
|
||||
default='.')
|
||||
args = parser.parse_args()
|
||||
session = PlainSession()
|
||||
os.chdir(args.directory)
|
||||
try:
|
||||
if args.subcommand == 'dist':
|
||||
run_dist(session)
|
||||
except NoBuildToolsFound:
|
||||
note('No build tools found.')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
Loading…
Add table
Reference in a new issue