From f88c4dec173312fbda20a58cf57398917c918c19 Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Fri, 26 Apr 2019 13:38:48 +0100 Subject: [PATCH] pressure-vessel: Add bwrap and its dependencies (x86_64 only) Signed-off-by: Simon McVittie <smcv@collabora.com> --- THIRD-PARTY.md | 19 +++- build-relocatable-install.py | 199 ++++++++++++++++++++++++++++------- ci/Jenkinsfile | 4 +- 3 files changed, 176 insertions(+), 46 deletions(-) diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md index 2cdb3204d..a71ce3b14 100644 --- a/THIRD-PARTY.md +++ b/THIRD-PARTY.md @@ -8,18 +8,29 @@ same place as pressure-vessel, or from <https://gitlab.collabora.com/vivek/libcapsule/>. Binary releases of pressure-vessel include the following third-party -libraries: +programs and libraries: + +- bubblewrap: [LGPL-2.0-or-later][]. See bubblewrap.txt. + +- libcap.so.2 from libcap: dual-licensed under [GPL-2.0-only][] and + a [BSD-3-Clause][] variant. See libcap2.txt. - libelf.so.1 from Red Hat elfutils: [GPL-2.0-or-later][] with linking exception. See elfutils.txt. +- libselinux.so.1 from SELinux: public-domain with [GPL-2.0-only][] + parts. See libselinux.txt. + +- libpcre.so.3 from PCRE: [BSD-3-Clause][]. See pcre3.txt. + - libz.so.1 from zlib: [Zlib][] license. See zlib.txt. Complete source code can be obtained from the same place as -pressure-vessel, or from -<https://packages.debian.org/source/jessie/elfutils> -and <https://packages.debian.org/source/jessie/zlib>. +pressure-vessel, or from <http://repo.steampowered.com/steamrt/>. +[BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html +[GPL-2.0-only]: https://spdx.org/licenses/GPL-2.0-only.html [GPL-2.0-or-later]: https://spdx.org/licenses/GPL-2.0-or-later.html +[LGPL-2.0-or-later]: https://spdx.org/licenses/LGPL-2.0-or-later.html [LGPL-2.1-or-later]: https://spdx.org/licenses/LGPL-2.1-or-later.html [Zlib]: https://spdx.org/licenses/Zlib.html diff --git a/build-relocatable-install.py b/build-relocatable-install.py index c2ef136f9..e48f46ef1 100755 --- a/build-relocatable-install.py +++ b/build-relocatable-install.py @@ -27,6 +27,7 @@ import argparse import glob import os import re +import shlex import shutil import subprocess @@ -38,20 +39,57 @@ else: typing # silence pyflakes +class Architecture: + def __init__( + self, + name, # type: str + multiarch, # type: str + ld_so # type: str + ): + # type: (...) -> None + self.name = name + self.multiarch = multiarch + self.ld_so = ld_so + + # Debian architecture => Debian multiarch tuple -ARCHS = { - 'amd64': 'x86_64-linux-gnu', - 'i386': 'i386-linux-gnu', -} +ARCHS = [ + Architecture( + name='amd64', + multiarch='x86_64-linux-gnu', + ld_so='/lib64/ld-linux-x86-64.so.2', + ), + Architecture( + name='i386', + multiarch='i386-linux-gnu', + ld_so='/lib/ld-linux.so.2', + ), +] # package to install from => source package for copyright information DEPENDENCIES = { 'libcapsule-tools-relocatable': 'libcapsule', 'libelf1': 'elfutils', 'zlib1g': 'zlib', } +# program to install => binary package +WRAPPED_PROGRAMS = { + 'bwrap': 'bubblewrap', +} +PRIMARY_ARCH_DEPENDENCIES = { + 'bubblewrap': 'bubblewrap', + 'libcap2': 'libcap2', + 'libselinux1': 'libselinux', + 'libpcre3': 'pcre3', +} DESTDIR = 'relocatable-install' -SCRIPTS = ('pressure-vessel-unruntime', 'pressure-vessel-wrap') -TOOLS = ('capsule-capture-libs', 'capsule-symbols') +SCRIPTS = [ + 'pressure-vessel-unruntime', + 'pressure-vessel-wrap' +] +TOOLS = [ + 'capsule-capture-libs', + 'capsule-symbols', +] def install(src, dst, mode=0o644): @@ -92,9 +130,9 @@ def main(): os.makedirs(os.path.join(DESTDIR, 'bin'), exist_ok=True) os.makedirs(os.path.join(DESTDIR, 'sources'), exist_ok=True) - for ma in ARCHS.values(): + for arch in ARCHS: os.makedirs( - os.path.join(DESTDIR, 'lib', ma), + os.path.join(DESTDIR, 'lib', arch.multiarch), exist_ok=True, ) @@ -108,12 +146,12 @@ def main(): ) if args.relocatabledir is not None: - for arch, ma in ARCHS.items(): + for arch in ARCHS: for tool in TOOLS: install_exe( os.path.join( args.relocatabledir, - '{}-{}'.format(ma, tool), + '{}-{}'.format(arch.multiarch, tool), ), os.path.join(DESTDIR, 'bin'), ) @@ -125,19 +163,28 @@ def main(): else: v_check_call(['make', '-f', 'Makefile.libcapsule']) - for arch, ma in ARCHS.items(): + for arch in ARCHS: for tool in TOOLS: install_exe( - os.path.join('_build', arch, 'libcapsule', tool), - os.path.join('_build', '{}-{}'.format(ma, tool)), + os.path.join('_build', arch.name, 'libcapsule', tool), + os.path.join( + '_build', + '{}-{}'.format(arch.multiarch, tool), + ), ) v_check_call([ 'chrpath', '-r', - '${ORIGIN}/../lib/' + ma, - os.path.join('_build', '{}-{}'.format(ma, tool)), + '${ORIGIN}/../lib/' + arch.multiarch, + os.path.join( + '_build', + '{}-{}'.format(arch.multiarch, tool), + ), ]) install_exe( - os.path.join('_build', '{}-{}'.format(ma, tool)), + os.path.join( + '_build', + '{}-{}'.format(arch.multiarch, tool), + ), os.path.join(DESTDIR, 'bin'), ) @@ -152,45 +199,117 @@ def main(): os.path.join(DESTDIR, 'sources'), ]) - for arch, ma in ARCHS.items(): - os.makedirs(os.path.join('_build', arch, 'lib'), exist_ok=True) + primary_architecture = subprocess.check_output([ + 'dpkg', '--print-architecture', + ]).decode('utf-8').strip() + + for arch in ARCHS: + os.makedirs(os.path.join('_build', arch.name, 'lib'), exist_ok=True) + v_check_call([ - '{}/bin/{}-capsule-capture-libs'.format(DESTDIR, ma), - '--dest=_build/{}/lib'.format(arch), + '{}/bin/{}-capsule-capture-libs'.format(DESTDIR, arch.multiarch), + '--dest=_build/{}/lib'.format(arch.name), '--no-glibc', 'soname:libelf.so.1', 'soname:libz.so.1', ]) + if arch.name == primary_architecture: + v_check_call([ + '{}/bin/{}-capsule-capture-libs'.format( + DESTDIR, + arch.multiarch, + ), + '--dest=_build/{}/lib'.format(arch.name), + '--no-glibc', + 'soname:libcap.so.2', + 'soname:libpcre.so.3', + 'soname:libselinux.so.1', + ]) + for so in glob.glob( - os.path.join('_build', arch, 'lib', '*.so.*'), + os.path.join('_build', arch.name, 'lib', '*.so.*'), ): - install(so, os.path.join(DESTDIR, 'lib', ma)) + install(so, os.path.join(DESTDIR, 'lib', arch.multiarch)) + + # For bwrap (and possibly other programs in future) we don't have + # a relocatable version with a RPATH/RUNPATH, so we wrap a script + # around it instead. The script avoids setting LD_LIBRARY_PATH + # because that would leak through to the programs invoked by bwrap. + for exe, package in WRAPPED_PROGRAMS.items(): + path = '/usr/bin/{}'.format(exe) + + if not os.path.exists(path): + v_check_call([ + 'apt-get', + 'download', + package, + ]) + v_check_call( + 'dpkg-deb -x {}_*.deb _build'.format(shlex.quote(package)), + shell=True, + ) + path = '_build/usr/bin/{}'.format(exe) + + for arch in ARCHS: + if arch.name != primary_architecture: + continue + + install_exe( + path, + os.path.join(DESTDIR, 'bin', exe + '.bin'), + ) + + with open( + os.path.join('_build', arch.name, exe), + 'w', + ) as writer: + writer.write('#!/bin/sh\n') + writer.write('set -eu\n') + writer.write('here="$(dirname "$0")"\n') + writer.write( + 'exec {} --library-path "$here"/../lib/{} ' + '"$here"/{}.bin "$@"\n'.format( + shlex.quote(arch.ld_so), + shlex.quote(arch.multiarch), + shlex.quote(exe), + ) + ) + + install_exe( + os.path.join('_build', arch.name, exe), + os.path.join(DESTDIR, 'bin', exe), + ) source_to_download = set() # type: typing.Set[str] - for package, source in DEPENDENCIES.items(): + for package, source in ( + list(DEPENDENCIES.items()) + list(PRIMARY_ARCH_DEPENDENCIES.items()) + ): if args.relocatabledir is None and source == 'libcapsule': continue - install( - '/usr/share/doc/{}/copyright'.format(package), - os.path.join(DESTDIR, 'sources', '{}.txt'.format(source)), - ) - install( - '/usr/share/doc/{}/copyright'.format(package), - os.path.join(DESTDIR, 'sources', '{}.txt'.format(source)), - ) + if os.path.exists('/usr/share/doc/{}/copyright'.format(package)): + install( + '/usr/share/doc/{}/copyright'.format(package), + os.path.join(DESTDIR, 'sources', '{}.txt'.format(source)), + ) - for expr in set( - v_check_output([ - 'dpkg-query', - '-W', - '-f', '${source:Package}=${source:Version}\n', - package, - ], universal_newlines=True).splitlines() - ): - source_to_download.add(re.sub(r'[+]srt[0-9a-z.]+$', '', expr)) + for expr in set( + v_check_output([ + 'dpkg-query', + '-W', + '-f', '${source:Package}=${source:Version}\n', + package, + ], universal_newlines=True).splitlines() + ): + source_to_download.add(re.sub(r'[+]srt[0-9a-z.]+$', '', expr)) + else: + install( + '_build/usr/share/doc/{}/copyright'.format(package), + os.path.join(DESTDIR, 'sources', '{}.txt'.format(source)), + ) + source_to_download.add(source) v_check_call( [ diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile index f422766af..06d204b89 100644 --- a/ci/Jenkinsfile +++ b/ci/Jenkinsfile @@ -60,8 +60,8 @@ pipeline { docker.withRegistry("https://${env.CI_DOCKER_REGISTRY}", dockerRegistryCred) { docker.image("${env.CI_DOCKER_REGISTRY}/steamrt/sdk:scout").inside('--mount type=tmpfs,destination=/var/cache/apt/archives') { sh ''' - set -e - make -C src + set -eu + make -C src PYTHON=python3.5 ''' } } -- GitLab