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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#!/usr/bin/env python
# Postinstall script for PySide
#
# TODO:
# This file can be removed after OSX support
# is implemented in pyside_build.update_rpath()
import os, sys, traceback, shutil, fnmatch, stat
from os.path import dirname, abspath
from subprocess import Popen, PIPE
import re
def filter_match(name, patterns):
for pattern in patterns:
if pattern is None:
continue
if fnmatch.fnmatch(name, pattern):
return True
return False
def back_tick(cmd, ret_err=False):
""" Run command `cmd`, return stdout, or stdout, stderr if `ret_err`
Roughly equivalent to ``check_output`` in Python 2.7
Parameters
----------
cmd : str
command to execute
ret_err : bool, optional
If True, return stderr in addition to stdout. If False, just return
stdout
Returns
-------
out : str or tuple
If `ret_err` is False, return stripped string containing stdout from
`cmd`. If `ret_err` is True, return tuple of (stdout, stderr) where
``stdout`` is the stripped stdout, and ``stderr`` is the stripped
stderr.
Raises
------
Raises RuntimeError if command returns non-zero exit code
"""
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
out, err = proc.communicate()
if not isinstance(out, str):
# python 3
out = out.decode()
retcode = proc.returncode
if retcode is None:
proc.terminate()
raise RuntimeError(cmd + ' process did not terminate')
if retcode != 0:
raise RuntimeError(cmd + ' process returned code %d' % retcode)
out = out.strip()
if not ret_err:
return out
return out, err.strip()
OSX_OUTNAME_RE = re.compile(r'\(compatibility version [\d.]+, current version '
'[\d.]+\)')
def osx_get_install_names(libpath):
""" Get OSX library install names from library `libpath` using ``otool``
Parameters
----------
libpath : str
path to library
Returns
-------
install_names : list of str
install names in library `libpath`
"""
out = back_tick('otool -L ' + libpath)
libs = [line for line in out.split('\n')][1:]
return [OSX_OUTNAME_RE.sub('', lib).strip() for lib in libs]
OSX_RPATH_RE = re.compile(r"path (.+) \(offset \d+\)")
def osx_get_rpaths(libpath):
""" Get rpaths from library `libpath` using ``otool``
Parameters
----------
libpath : str
path to library
Returns
-------
rpaths : list of str
rpath values stored in ``libpath``
Notes
-----
See ``man dyld`` for more information on rpaths in libraries
"""
lines = back_tick('otool -l ' + libpath).split('\n')
ctr = 0
rpaths = []
while ctr < len(lines):
line = lines[ctr].strip()
if line != 'cmd LC_RPATH':
ctr += 1
continue
assert lines[ctr + 1].strip().startswith('cmdsize')
rpath_line = lines[ctr + 2].strip()
match = OSX_RPATH_RE.match(rpath_line)
if match is None:
raise RuntimeError('Unexpected path line: ' + rpath_line)
rpaths.append(match.groups()[0])
ctr += 3
return rpaths
def osx_localize_libpaths(libpath, local_libs, enc_path=None):
""" Set rpaths and install names to load local dynamic libs at run time
Use ``install_name_tool`` to set relative install names in `libpath` (as
named in `local_libs` to be relative to `enc_path`. The default for
`enc_path` is the directory containing `libpath`.
Parameters
----------
libpath : str
path to library for which to set install names and rpaths
local_libs : sequence of str
library (install) names that should be considered relative paths
enc_path : str, optional
path that does or will contain the `libpath` library, and to which the
`local_libs` are relative. Defaults to current directory containing
`libpath`.
"""
if enc_path is None:
enc_path = abspath(dirname(libpath))
install_names = osx_get_install_names(libpath)
need_rpath = False
for install_name in install_names:
if install_name[0] in '/@':
continue
back_tick('install_name_tool -change %s @rpath/%s %s' %
(install_name, install_name, libpath))
need_rpath = True
if need_rpath and enc_path not in osx_get_rpaths(libpath):
back_tick('install_name_tool -add_rpath %s %s' %
(enc_path, libpath))
def post_install_osx():
# Try to find PySide package
try:
import PySide
except ImportError:
print("The PySide package not found: %s" % traceback.print_exception(*sys.exc_info()))
return
pyside_path = os.path.abspath(os.path.dirname(PySide.__file__))
print("PySide package found in %s..." % pyside_path)
pyside_libs = [lib for lib in os.listdir(pyside_path) if filter_match(
lib, ["*.so", "*.dylib", "shiboken"])]
# Update rpath in PySide libs
for srcname in pyside_libs:
srcpath = os.path.join(pyside_path, srcname)
if os.path.isdir(srcpath):
continue
if not os.path.exists(srcpath):
continue
osx_localize_libpaths(srcpath, pyside_libs, pyside_path)
print("Patched rpath in %s to %s." % (srcpath, pyside_path))
# Check PySide installation status
try:
from PySide import QtCore
print("PySide package successfully installed in %s..." % \
os.path.abspath(os.path.dirname(QtCore.__file__)))
except ImportError:
print("The PySide package not installed: %s" % traceback.print_exception(*sys.exc_info()))
if __name__ == '__main__':
if sys.platform == "darwin":
post_install_osx()
|