99 lines
4.6 KiB
Python
99 lines
4.6 KiB
Python
# Copyright (C) 2018, 2020, 2021 Igalia S.L.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
# list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
|
|
class BinaryBundler:
|
|
VAR_MYDIR = 'MYDIR'
|
|
VAR_INTERPRETER = 'INTERPRETER'
|
|
|
|
def __init__(self, destination_dir, should_strip_objects):
|
|
if shutil.which('patchelf') is None:
|
|
_log.error("Could not find `patchelf` in $PATH")
|
|
raise Exception("Missing required binary `patchelf`")
|
|
self._destination_dir = destination_dir
|
|
self._should_strip_objects = should_strip_objects
|
|
|
|
def destination_dir(self):
|
|
return self._destination_dir
|
|
|
|
def copy_and_remove_rpath(self, orig_file, type='bin', destination_dir=None):
|
|
dir_suffix = 'lib' if type == 'interpreter' else type
|
|
destination_dir = os.path.join(self._destination_dir, dir_suffix)
|
|
if not os.path.isdir(destination_dir):
|
|
os.makedirs(destination_dir)
|
|
|
|
if not os.path.isfile(orig_file):
|
|
raise ValueError('Can not find file %s' % orig_file)
|
|
|
|
_log.info('Add to bundle [%s]: %s' % (type, orig_file))
|
|
try:
|
|
shutil.copy(orig_file, destination_dir)
|
|
except shutil.SameFileError:
|
|
# May reasonably happen if the caller tries to bundle the files 'in place'.
|
|
pass
|
|
|
|
patch_elf_command = ['patchelf', '--remove-rpath', os.path.join(destination_dir, os.path.basename(orig_file))]
|
|
if subprocess.call(patch_elf_command) != 0:
|
|
_log.error('The patchelf command returned non-zero status')
|
|
|
|
if self._should_strip_objects:
|
|
if shutil.which('strip'):
|
|
strip_command = ['strip', '--strip-unneeded', os.path.join(destination_dir, os.path.basename(orig_file))]
|
|
if subprocess.call(strip_command) != 0:
|
|
_log.error('The strip command returned non-zero status')
|
|
else:
|
|
_log.warning('strip not found. Not stripping object')
|
|
|
|
def generate_wrapper_script(self, interpreter, binary_to_wrap, extra_environment_variables={}):
|
|
if not os.path.isfile(os.path.join(self._destination_dir, 'bin', binary_to_wrap)):
|
|
raise RuntimeError('Cannot find binary to wrap for %s' % binary_to_wrap)
|
|
_log.info('Generate wrapper script %s' % binary_to_wrap)
|
|
script_file = os.path.join(self._destination_dir, binary_to_wrap)
|
|
|
|
with open(script_file, 'w') as script_handle:
|
|
script_handle.write('#!/bin/sh\n')
|
|
script_handle.write('%s="$(dirname $(readlink -f $0))"\n' % self.VAR_MYDIR)
|
|
script_handle.write('export LD_LIBRARY_PATH="${%s}/lib"\n' % self.VAR_MYDIR)
|
|
|
|
if interpreter is not None:
|
|
script_handle.write('INTERPRETER="${%s}/lib/%s"\n' % (self.VAR_MYDIR, os.path.basename(interpreter)))
|
|
|
|
# May use the value of INTERPRETER, so has to come after
|
|
# the definition.
|
|
for var, value in extra_environment_variables.items():
|
|
script_handle.write('export %s=\"%s\"\n' % (var, value))
|
|
|
|
if interpreter is not None:
|
|
script_handle.write('exec "${INTERPRETER}" "${%s}/bin/%s" "$@"\n' % (self.VAR_MYDIR, binary_to_wrap))
|
|
else:
|
|
script_handle.write('exec "${%s}/bin/%s" "$@"\n' % (self.VAR_MYDIR, binary_to_wrap))
|
|
|
|
os.chmod(script_file, 0o755)
|