Clean the package layout

This commit is contained in:
Valentin Niess
2020-03-29 16:07:53 +02:00
parent f18d9d5537
commit f8a4c10644
15 changed files with 25 additions and 22 deletions

View File

View File

@@ -0,0 +1,99 @@
import os
import platform
import stat
from .fs import copy_file, copy_tree, make_tree
from .log import log
from .system import system
from .tmp import TemporaryDirectory
from .url import urlretrieve
__all__ = ['APPIMAGETOOL', 'EXCLUDELIST', 'PATCHELF', 'PREFIX',
'ensure_appimagetool', 'ensure_excludelist', 'ensure_patchelf',
'fetch_all']
_ARCH = platform.machine()
PREFIX = os.path.abspath(os.path.dirname(__file__) + '/..')
'''Package installation prefix'''
APPIMAGETOOL = PREFIX + '/bin/appimagetool.' + _ARCH
'''Location of the appimagetool binary'''
EXCLUDELIST = PREFIX + '/data/excludelist'
'''AppImage exclusion list'''
PATCHELF = PREFIX + '/bin/patchelf.' + _ARCH
'''Location of the PatchELF binary'''
def ensure_appimagetool():
'''Fetch appimagetool from the web if not available locally
'''
if os.path.exists(APPIMAGETOOL):
return
appimage = 'appimagetool-{0:}.AppImage'.format(_ARCH)
baseurl = 'https://github.com/AppImage/AppImageKit/releases/' \
'download/continuous'
log('INSTALL', 'appimagetool from %s', baseurl)
appdir_name = 'appimagetool-{:}.appdir'.format(_ARCH)
appdir = os.path.join(os.path.dirname(APPIMAGETOOL), appdir_name)
if not os.path.exists(appdir):
make_tree(os.path.dirname(appdir))
with TemporaryDirectory() as tmpdir:
urlretrieve(os.path.join(baseurl, appimage), appimage)
os.chmod(appimage, stat.S_IRWXU)
system('./' + appimage, '--appimage-extract')
copy_tree('squashfs-root', appdir)
if not os.path.exists(APPIMAGETOOL):
os.symlink(appdir_name + '/AppRun', APPIMAGETOOL)
# Installers for dependencies
def ensure_excludelist():
'''Fetch the AppImage excludelist from the web if not available locally
'''
if os.path.exists(EXCLUDELIST):
return
baseurl = 'https://raw.githubusercontent.com/probonopd/AppImages/master'
log('INSTALL', 'excludelist from %s', baseurl)
urlretrieve(baseurl + '/excludelist', EXCLUDELIST)
mode = os.stat(EXCLUDELIST)[stat.ST_MODE]
os.chmod(EXCLUDELIST, mode | stat.S_IWGRP | stat.S_IWOTH)
def ensure_patchelf():
'''Fetch PatchELF from the web if not available locally
'''
if os.path.exists(PATCHELF):
return
iarch = 'i386' if _ARCH == 'i686' else _ARCH
appimage = 'patchelf-{0:}.AppImage'.format(iarch)
baseurl = 'https://github.com/niess/patchelf.appimage/releases/download'
log('INSTALL', 'patchelf from %s', baseurl)
dirname = os.path.dirname(PATCHELF)
patchelf = dirname + '/patchelf.' + _ARCH
make_tree(dirname)
with TemporaryDirectory() as tmpdir:
urlretrieve(os.path.join(baseurl, 'rolling', appimage), appimage)
os.chmod(appimage, stat.S_IRWXU)
system('./' + appimage, '--appimage-extract')
copy_file('squashfs-root/usr/bin/patchelf', patchelf)
os.chmod(patchelf, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
def fetch_all():
'''Fetch all dependencies from the web
'''
ensure_appimagetool()
ensure_excludelist()
ensure_patchelf()

View File

@@ -0,0 +1,49 @@
import os
import platform
import stat
import subprocess
import sys
from .log import log
from .system import system
def docker_run(image, extra_cmds):
'''Execute commands within a docker container
'''
ARCH = platform.machine()
if image.endswith(ARCH):
bash_arg = '/pwd/run.sh'
elif image.endswith('i686') and ARCH == 'x86_64':
bash_arg = '-c "linux32 /pwd/run.sh"'
elif image.endswith('x86_64') and ARCH == 'i686':
bash_arg = '-c "linux64 /pwd/run.sh"'
else:
raise ValueError('Unsupported Docker image: ' + image)
log('PULL', image)
system('docker', 'pull', image)
script = [
'set -e',
'trap "chown -R {:}:{:} *" EXIT'.format(os.getuid(),
os.getgid()),
'cd /pwd'
]
script += extra_cmds
with open('run.sh', 'w') as f:
f.write(os.linesep.join(script))
os.chmod('run.sh', stat.S_IRWXU)
cmd = ' '.join(('docker', 'run', '--mount',
'type=bind,source={:},target=/pwd'.format(os.getcwd()),
image, '/bin/bash', bash_arg))
log('RUN', image)
p = subprocess.Popen(cmd, shell=True)
p.communicate()
if p.returncode != 0:
sys.exit(p.returncode)

View File

@@ -0,0 +1,75 @@
from distutils.dir_util import mkpath as _mkpath, remove_tree as _remove_tree
from distutils.file_util import copy_file as _copy_file
import errno
import os
from .log import debug
__all__ = ['copy_file', 'copy_tree', 'make_tree', 'remove_file', 'remove_tree']
# Wrap some file system related functions
def make_tree(path):
'''Create directories recursively if they don't exist
'''
debug('MKDIR', path)
return _mkpath(path)
def copy_file(source, destination, update=False, verbose=True):
'''
'''
name = os.path.basename(source)
if verbose:
debug('COPY', '%s from %s', name, os.path.dirname(source))
_copy_file(source, destination, update=update)
def copy_tree(source, destination):
'''Copy (or update) a directory preserving symlinks
'''
if not os.path.exists(source):
raise OSError(errno.ENOENT, 'No such file or directory: ' + source)
name = os.path.basename(source)
debug('COPY', '%s from %s', name, os.path.dirname(source))
for root, _, files in os.walk(source):
relpath = os.path.relpath(root, source)
dirname = os.path.join(destination, relpath)
_mkpath(dirname)
for file_ in files:
src = os.path.join(root, file_)
dst = os.path.join(dirname, file_)
if os.path.islink(src):
try:
os.remove(dst)
except OSError:
pass
linkto = os.readlink(src)
os.symlink(linkto, dst)
else:
copy_file(src, dst, update=True, verbose=False)
def remove_file(path):
'''remove a file if it exists
'''
name = os.path.basename(path)
debug('REMOVE', '%s from %s', name, os.path.dirname(path))
try:
os.remove(path)
except OSError:
pass
def remove_tree(path):
'''remove a directory if it exists
'''
name = os.path.basename(path)
debug('REMOVE', '%s from %s', name, os.path.dirname(path))
try:
_remove_tree(path)
except OSError:
pass

View File

@@ -0,0 +1,23 @@
import logging
__all__ = ['debug', 'log']
# Configure the logger
logging.basicConfig(
format='[%(asctime)s] %(message)s',
level=logging.INFO
)
def log(task, fmt, *args):
'''Log a standard message
'''
logging.info('%-8s ' + fmt, task, *args)
def debug(task, fmt, *args):
'''Report some debug information
'''
logging.debug('%-8s ' + fmt, task, *args)

View File

@@ -0,0 +1,46 @@
import os
import re
import subprocess
from .log import debug
__all__ = ['ldd', 'system']
def _decode(s):
'''Decode Python 3 bytes as str
'''
try:
return s.decode()
except AttributeError:
return s
def system(*args):
'''System call with capturing output
'''
cmd = ' '.join(args)
debug('SYSTEM', cmd)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
if err:
err = _decode(err)
stripped = [line for line in err.split(os.linesep)
if line and not line.startswith('fuse: warning:')]
if stripped:
raise RuntimeError(err)
return str(_decode(out).strip())
_ldd_pattern = re.compile('=> (.+) [(]0x')
def ldd(path):
'''Get dependencies list of dynamic libraries
'''
out = system('ldd', path)
return _ldd_pattern.findall(out)

View File

@@ -0,0 +1,24 @@
from contextlib import contextmanager as contextmanager
import os
import tempfile
from .fs import remove_tree
from .log import debug
__all__ = ['TemporaryDirectory']
@contextmanager
def TemporaryDirectory():
'''Create a temporary directory (Python 2 wrapper)
'''
tmpdir = tempfile.mkdtemp(prefix='python-appimage-')
debug('MKDIR', tmpdir)
pwd = os.getcwd()
os.chdir(tmpdir)
try:
yield tmpdir
finally:
os.chdir(pwd)
remove_tree(tmpdir)

View File

@@ -0,0 +1,28 @@
import os
try:
from urllib.request import urlretrieve as _urlretrieve
except ImportError:
import urllib2
_urlretrieve = None
from .log import debug
__all__ = ['urlretrieve']
def urlretrieve(url, filename=None):
'''Download a file to disk
'''
if filename is None:
filename = os.path.basename(url)
debug('DOWNLOAD', '%s from %s', name, os.path.dirname(url))
else:
debug('DOWNLOAD', '%s as %s', url, filename)
if _urlretrieve is None:
data = urllib2.urlopen(url).read()
with open(filename, 'w') as f:
f.write(data)
else:
_urlretrieve(url, filename)