blob: c6e55b8893157d8bdaca6fd265507cc0413abe8d [file] [log] [blame]
# 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)