From cd98e64f462e650c9ec4cb673c2b77831396113b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 2 Oct 2018 18:47:24 +0200 Subject: Add bundle command to sibs package Bundle command copies all dynamic library dependencies to one location and creates an archive that can be distributed. Currently only for linux. In testing phase... --- scripts/package.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 scripts/package.py (limited to 'scripts') diff --git a/scripts/package.py b/scripts/package.py new file mode 100755 index 0000000..3de3448 --- /dev/null +++ b/scripts/package.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +import os +import shutil +import re +import stat +import tarfile + +def get_executable_dynamic_libraries(filepath): + libs = [] + process = subprocess.Popen(["ldd", filepath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = process.communicate() + if process.returncode != 0: + raise RuntimeError("Failed to execute ldd on executable %s, error:\n%s" % (filepath, stderr)) + lines = stdout.splitlines() + for line in lines: + s = line.split() + if b"=>" in s: + if len(s) >= 3: + libs.append(os.path.realpath(s[2].decode("UTF-8"))) + return libs + +def make_executable(filepath): + mode = os.stat(filepath).st_mode + os.chmod(filepath, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + +def main(): + if len(sys.argv) <= 2: + print("usage: %s executable_path destination_path" % sys.argv[0]) + exit(1) + + os.makedirs(sys.argv[2], exist_ok=True) + libs = get_executable_dynamic_libraries(sys.argv[1]) + + so_loader_pattern = re.compile("ld-[0-9.]+\\.so.*") + so_loader = None + for lib in libs: + lib_filename = os.path.basename(lib) + if so_loader_pattern.match(lib_filename): + if so_loader != None: + print("Unexpected error: found multiple so loaders, unable to recover") + exit(2) + so_loader = lib_filename + + executable_filename = os.path.basename(sys.argv[1]) + new_executable_path = os.path.join(sys.argv[2], executable_filename) + shutil.copyfile(sys.argv[1], new_executable_path) + make_executable(new_executable_path) + + print("Patching executable") + process = subprocess.Popen(["patchelf", "--set-interpreter", so_loader, new_executable_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = process.communicate() + if process.returncode != 0: + print("Failed to execute patchelf --set-interpreter on executable %s, error: %s" % (new_executable_path, stderr)) + exit(3) + + process = subprocess.Popen(["patchelf", "--set-rpath", ".", new_executable_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = process.communicate() + if process.returncode != 0: + print("Failed to execute patchelf --set-rpath on executable %s, error: %s" % (new_executable_path, stderr)) + exit(4) + + package_name = new_executable_path + ".tar.gz" + print("Creating package %s" % os.path.basename(package_name)) + with tarfile.open(package_name, "w:gz") as tar: + for lib in libs: + print("Adding shared library %s to tar" % lib) + tar.add(lib, arcname=os.path.basename(lib)) + print("Adding executable %s to tar" % sys.argv[1]) + tar.add(new_executable_path, arcname=os.path.basename(new_executable_path)) + + print("Removing temporary file %s" % new_executable_path) + os.remove(new_executable_path) + print("Package has been created at %s" % package_name) + +if __name__ == "__main__": + main() \ No newline at end of file -- cgit v1.2.3