Before installing python files to their final location in install.sh, replace them with a thunk so that they can work with our python3 interpreter. The way the thunk works, they will also work without our python3 interpreter so unconditionally fixing them up is always safe. I opt in this patch for fixing up just at install time to simplify developer's life, who won't have to worry about this at all. Note about the rpm .spec file: since we are relying on specific format for the shebangs, we shouldn't let rpmbuild mess with them. Therefore, we need to disable a global variable that controls that behavior (by definition, Fedora rpmbuild will rewrite all shebangs to /usr/bin/python3) Signed-off-by: Glauber Costa <glauber@scylladb.com>
145 lines
4.8 KiB
Python
Executable File
145 lines
4.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2018 ScyllaDB
|
|
#
|
|
|
|
#
|
|
# This file is part of Scylla.
|
|
#
|
|
# Scylla is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Scylla 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 Scylla. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import argparse
|
|
import io
|
|
import os
|
|
import subprocess
|
|
import tarfile
|
|
import pathlib
|
|
|
|
|
|
def ldd(executable):
|
|
'''Given an executable file, return a dictionary with the keys
|
|
containing its shared library dependencies and the values pointing
|
|
at the files they resolve to. A fake key ld.so points at the
|
|
dynamic loader.'''
|
|
libraries = {}
|
|
for ldd_line in subprocess.check_output(
|
|
['ldd', executable],
|
|
universal_newlines=True).splitlines():
|
|
elements = ldd_line.split()
|
|
if ldd_line.endswith('not found'):
|
|
raise Exception('ldd could not resolve {}'.format(elements[0]))
|
|
if elements[1] != '=>':
|
|
if elements[0].startswith('linux-vdso.so'):
|
|
# provided by kernel
|
|
continue
|
|
libraries['ld.so'] = os.path.realpath(elements[0])
|
|
else:
|
|
libraries[elements[0]] = os.path.realpath(elements[2])
|
|
return libraries
|
|
|
|
|
|
ap = argparse.ArgumentParser(description='Create a relocatable scylla package.')
|
|
ap.add_argument('dest',
|
|
help='Destination file (tar format)')
|
|
ap.add_argument('--mode', dest='mode', default='release',
|
|
help='Build mode (debug/release) to use')
|
|
|
|
args = ap.parse_args()
|
|
|
|
executables = ['build/{}/scylla'.format(args.mode),
|
|
'build/{}/iotune'.format(args.mode)]
|
|
|
|
output = args.dest
|
|
|
|
libs = {}
|
|
for exe in executables:
|
|
libs.update(ldd(exe))
|
|
|
|
ld_so = libs['ld.so']
|
|
|
|
ar = tarfile.open(output, mode='w|gz')
|
|
pathlib.Path('build/SCYLLA-RELOCATABLE-FILE').touch()
|
|
ar.add('build/SCYLLA-RELOCATABLE-FILE', arcname='SCYLLA-RELOCATABLE-FILE')
|
|
|
|
# This thunk is a shell script that arranges for the executable to be invoked,
|
|
# under the following conditions:
|
|
#
|
|
# - the same argument vector is passed to the executable, including argv[0]
|
|
# - the executable name (/proc/pid/comm, shown in top(1)) is the same
|
|
# - the dynamic linker is taken from this package rather than the executable's
|
|
# default (which is hardcoded to point to /lib64/ld-linux-x86_64.so or similar)
|
|
# - LD_LIBRARY_PATH points to the lib/ directory so shared library dependencies
|
|
# are satisified from there rather than the system default (e.g. /lib64)
|
|
|
|
# To do that, the dynamic linker is invoked using a symbolic link named after the
|
|
# executable, not its standard name. We use "bash -a" to set argv[0].
|
|
|
|
# The full tangled web looks like:
|
|
#
|
|
# foobar/bin/scylla a shell script invoking everything
|
|
# foobar/libexec/scylla.bin the real binary
|
|
# foobar/libexec/scylla a symlink to ../lib/ld.so
|
|
# foobar/libreloc/ld.so the dynamic linker
|
|
# foobar/libreloc/lib... all the other libraries
|
|
|
|
# the transformations (done by the thunk and symlinks) are:
|
|
#
|
|
# bin/scylla args -> libexec/scylla libexec/scylla.bin args -> lib/ld.so libexec/scylla.bin args
|
|
|
|
thunk = b'''\
|
|
#!/bin/bash
|
|
|
|
x="$(readlink -f "$0")"
|
|
b="$(basename "$x")"
|
|
d="$(dirname "$x")/.."
|
|
ldso="$d/libexec/$b"
|
|
realexe="$d/libexec/$b.bin"
|
|
LD_LIBRARY_PATH="$d/libreloc" exec -a "$0" "$ldso" "$realexe" "$@"
|
|
'''
|
|
|
|
for exe in executables:
|
|
basename = os.path.basename(exe)
|
|
ar.add(exe, arcname='libexec/' + basename + '.bin')
|
|
ti = tarfile.TarInfo(name='bin/' + basename)
|
|
ti.size = len(thunk)
|
|
ti.mode = 0o755
|
|
ti.mtime = os.stat(exe).st_mtime
|
|
ar.addfile(ti, fileobj=io.BytesIO(thunk))
|
|
ti = tarfile.TarInfo(name='libexec/' + basename)
|
|
ti.type = tarfile.SYMTYPE
|
|
ti.linkname = '../libreloc/ld.so'
|
|
ti.mtime = os.stat(exe).st_mtime
|
|
ar.addfile(ti)
|
|
for lib, libfile in libs.items():
|
|
ar.add(libfile, arcname='libreloc/' + lib)
|
|
ar.add('conf')
|
|
ar.add('dist')
|
|
ar.add('build/SCYLLA-RELEASE-FILE', arcname='SCYLLA-RELEASE-FILE')
|
|
ar.add('build/SCYLLA-VERSION-FILE', arcname='SCYLLA-VERSION-FILE')
|
|
ar.add('seastar/scripts')
|
|
ar.add('seastar/dpdk/usertools')
|
|
ar.add('install.sh')
|
|
ar.add('scripts/relocate_python_scripts.py', arcname='relocate_python_scripts.py')
|
|
ar.add('README.md')
|
|
ar.add('README-DPDK.md')
|
|
ar.add('NOTICE.txt')
|
|
ar.add('ORIGIN')
|
|
ar.add('licenses')
|
|
ar.add('swagger-ui')
|
|
ar.add('api')
|
|
ar.add('tools')
|
|
ar.add('scylla-gdb.py')
|