1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
#!/usr/bin/env python
bundle = ['sqlite3', 'ssl', 'crypto', 'ffi', 'expat', 'tcl8', 'tk8', 'gdbm',
'lzma', 'tinfo', 'tinfow', 'ncursesw', 'panelw', 'ncurses', 'panel',
'panelw']
import os
from os.path import dirname, relpath, join, exists, basename, realpath
from shutil import copy
import sys
from glob import glob
from subprocess import check_output, check_call
def get_deps_darwin(binary):
deps = {}
output = check_output(['otool', '-L', binary], universal_newlines=True)
output = output.splitlines()
output = output[1:] # first line is binary name
libs = []
for line in output:
path = line.strip().split()[0]
if (not path or
path.startswith('/usr/') or
path.startswith('/System/') or
basename(path) == basename(binary)):
continue
needed = basename(path)
deps[needed] = path
deps.update(get_deps(path))
return deps
def get_deps(binary):
if sys.platform == 'darwin':
return get_deps_darwin(binary)
deps = {}
output = check_output(['ldd', binary], universal_newlines=True)
lib = []
for line in output.splitlines():
if '=>' not in line:
continue
line = line.strip()
needed, path = line.split(' => ')
if path == 'not found':
raise ValueError('Broken dependency in %s:\n %s' % (binary, output))
path = path.split(' ')[0]
if not path:
continue
if needed[3:].split('.', 1)[0] not in bundle:
continue
deps[needed] = path
deps.update(get_deps(path))
return deps
def gather_deps(binaries):
deps = {}
for binary in binaries:
deps.update(get_deps(binary))
return deps
def copy_deps(deps):
copied = {}
for needed, path in deps.items():
bname = basename(path)
copy(realpath(path), 'lib/' + bname)
copied[path] = 'lib/' + bname
if not exists('lib/' + needed):
os.symlink(bname, 'lib/' + needed)
return copied
def rpath_binaries(binaries):
rpaths = {}
for binary in binaries:
check_call(['chmod', 'a+w', binary])
if sys.platform == 'darwin':
rpath = join('@executable_path', relpath('lib', dirname(binary)))
check_call(['install_name_tool', '-add_rpath', rpath, binary])
# change path for deps, this deps call is sorta redundant, but we
# don't have this dependency info in the passed in data...
deps = get_deps(binary)
for dep, path in deps.items():
rpath = join('@rpath', dep)
if rpath != path:
print('Set RPATH of {0} for {1} to {2}'.format(binary, path, rpath))
check_call(['install_name_tool', '-change', path, rpath, binary])
else:
rpath = join('$ORIGIN', relpath('lib', dirname(binary)))
check_call(['patchelf', '--set-rpath', rpath, binary])
rpaths[binary] = rpath
return rpaths
def make_portable(copytree, python_ver, tk_patch, tcl_patch):
exts = ['so']
if sys.platform == 'darwin':
exts = ['dylib', 'so']
pyver_no_dot = python_ver.replace('.', '')
binaries = glob('bin/libpypy{}-c.'.format(python_ver) + exts[0])
tcltk = []
if not binaries:
raise ValueError('Could not find bin/libpypy%s-c.%s in "%s"' % (python_ver, exts[0], os.getcwd()))
for ext in exts:
binaries.extend(glob('lib/pypy{}/*_cffi.pypy{}*.{}'.format(python_ver, pyver_no_dot, ext)))
binaries.extend(glob('lib/pypy{}/*_pypy_openssl{}*.{}'.format(python_ver, pyver_no_dot, ext)))
binaries.extend(glob('lib/pypy{}/_tkinter/*_cffi.pypy{}*.{}'.format(python_ver, pyver_no_dot, ext)))
deps = gather_deps(binaries)
copied = copy_deps(deps)
for path, item in copied.items():
print('Copied {0} to {1}'.format(path, item))
binaries.extend(copied.values())
rpaths = rpath_binaries(binaries)
for binary, rpath in rpaths.items():
print('Set RPATH of {0} to {1}'.format(binary, rpath))
# copy tcl/tk shared files, but only if they are the correct version
base = os.environ.get('HOMEBREW_CELLAR', '/usr')
found_tk = found_tcl = False
for path, dirs, files in os.walk(base):
if not found_tk and 'tk.tcl' in files:
with open(os.path.join(path, 'tk.tcl')) as fid:
for line in fid:
if line.startswith('package require -exact Tk'):
if tk_patch in line:
print('Found tk shared files at: %s' % (path))
found_tk = True
target = 'lib/{}'.format(os.path.split(path)[-1])
copytree(path, target)
else:
print("Found tk.tcl at %s but wrong version" % path)
break
if found_tcl and found_tk:
break
if not found_tcl and 'init.tcl' in files:
with open(os.path.join(path, 'init.tcl')) as fid:
for line in fid:
if line.startswith('package require -exact Tcl'):
if tcl_patch in line:
print('Found tcl shared files at: %s' % path)
found_tcl = True
target = 'lib/{}'.format(os.path.split(path)[-1])
copytree(path, target)
else:
print("Found init.tcl at %s but wrong version" % path)
break
if found_tcl and found_tk:
break
return deps
|