|
|
@ -1,6 +1,7 @@ |
|
|
|
import argparse |
|
|
|
import subprocess |
|
|
|
import os |
|
|
|
from shutil import copyfile |
|
|
|
|
|
|
|
def get_dependencies(file): |
|
|
|
# Call otool -L file to obtain the dependencies. |
|
|
@ -16,32 +17,87 @@ def get_dependencies(file): |
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
def create_package(args): |
|
|
|
create_package_dirs(args.dir) |
|
|
|
copy_binary_to_package_dir(args.bin, args.binary_basename, args.dir) |
|
|
|
run_dylibbundler(args.bundler_binary, args.dir, args.binary_basename) |
|
|
|
pass |
|
|
|
|
|
|
|
def parse_arguments(): |
|
|
|
parser = argparse.ArgumentParser(description='Package the storm binary on Mac OS.') |
|
|
|
parser.add_argument('binary', help='the binary to package') |
|
|
|
parser.add_argument('--bin', dest='bin', help='the binary to package', default='storm') |
|
|
|
parser.add_argument('--dir', dest='dir', help='the root directory of the package (will be created if it does not exist)', default='.') |
|
|
|
parser.add_argument('--dylibbundler', dest='bundler_binary', help='the binary of the dylibbundler', default='dylibbundler') |
|
|
|
args = parser.parse_args() |
|
|
|
args.binary_dir = os.path.split(args.bin)[0] |
|
|
|
args.binary_basename = os.path.split(args.bin)[1] |
|
|
|
return args |
|
|
|
|
|
|
|
dep_exeptions = set() |
|
|
|
dep_exeptions.add("libSystem.B.dylib") |
|
|
|
dep_exeptions.add("libc++.1.dylib") |
|
|
|
def create_package_dirs(root_dir): |
|
|
|
if not os.path.exists(root_dir): |
|
|
|
os.makedirs(root_dir) |
|
|
|
if not os.path.exists(root_dir + "/bin"): |
|
|
|
os.makedirs(root_dir + "/bin") |
|
|
|
if not os.path.exists(root_dir + "/lib"): |
|
|
|
os.makedirs(root_dir + "/bin") |
|
|
|
pass |
|
|
|
|
|
|
|
def copy_binary_to_package_dir(binary, binary_basename, root_dir): |
|
|
|
copyfile(binary, root_dir + "/bin/storm") |
|
|
|
pass |
|
|
|
|
|
|
|
def run_dylibbundler(bundler_binary, root_dir, binary_basename): |
|
|
|
command = [bundler_binary, "-cd", "-od", "-b", "-p", "@executable_path/../lib", "-x", root_dir + "/bin/" + binary_basename, "-d", root_dir + "/lib"] |
|
|
|
print("executing " + str(command)) |
|
|
|
#proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
|
|
|
pass |
|
|
|
|
|
|
|
def rec_gather_all_dependencies(file, deps = set()): |
|
|
|
current_deps = get_dependencies(file) |
|
|
|
new_deps = current_deps.keys() - dep_exeptions - deps |
|
|
|
deps = deps | current_deps.keys() |
|
|
|
def fix_paths(root_dir, binary_basename): |
|
|
|
fix_paths_file(root_dir + "/bin/" + binary_basename) |
|
|
|
for file in os.listdir(root_dir + "/lib"): |
|
|
|
fix_paths_file(root_dir + "/lib/" + file) |
|
|
|
pass |
|
|
|
pass |
|
|
|
|
|
|
|
for d in new_deps: |
|
|
|
rec_gather_all_dependencies(file, deps) |
|
|
|
def fix_paths_file(file): |
|
|
|
print("fixing paths for " + file) |
|
|
|
fixable_deps = get_fixable_deps(file) |
|
|
|
for (path, lib) in fixable_deps: |
|
|
|
change_fixable_dep(file, path, lib) |
|
|
|
|
|
|
|
return deps |
|
|
|
native_libs = ["libc++.1.dylib", "libSystem.B.dylib"] |
|
|
|
|
|
|
|
def get_fixable_deps(file): |
|
|
|
# Call otool -L file to obtain the dependencies. |
|
|
|
proc = subprocess.Popen(["otool", "-L", file], stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
|
|
|
result = [] |
|
|
|
for line_bytes in proc.stdout: |
|
|
|
line = line_bytes.decode("utf-8").strip() |
|
|
|
lib = line.split()[0] |
|
|
|
if lib.startswith("@rpath/"): |
|
|
|
result.append(("@rpath", lib.split("/", 1)[1])) |
|
|
|
elif lib.startswith("/"): |
|
|
|
path_file = os.path.split(lib) |
|
|
|
if path_file[1] not in native_libs: |
|
|
|
result.append((path_file[0], path_file[1])) |
|
|
|
return result |
|
|
|
|
|
|
|
def print_deps(deps): |
|
|
|
print("Found the following dependencies:") |
|
|
|
for d in deps: |
|
|
|
print(d) |
|
|
|
def change_fixable_dep(file, path, lib): |
|
|
|
# Call install_name_tool to change the fixable dependencies |
|
|
|
command = ["install_name_tool", "-change", path + "/" + lib, "@executable_path/../lib/" + lib, file] |
|
|
|
print("executing " + str(command)) |
|
|
|
proc = subprocess.Popen(command, stdout=subprocess.PIPE) |
|
|
|
#print ("after call to install_name_tool") |
|
|
|
#proc = subprocess.Popen(["otool", "-L", file], stdout=subprocess.PIPE) |
|
|
|
#for line_bytes in proc.stdout: |
|
|
|
# line = line_bytes.decode("utf-8").strip() |
|
|
|
# print(line) |
|
|
|
pass |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
args = parse_arguments() |
|
|
|
deps = rec_gather_all_dependencies(args.binary) |
|
|
|
print_deps(deps) |
|
|
|
|
|
|
|
#create_package(args) |
|
|
|
fix_paths(args.dir, args.binary_basename) |
|
|
|
|
|
|
|
|