aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pyside_postinstall.py183
1 files changed, 166 insertions, 17 deletions
diff --git a/pyside_postinstall.py b/pyside_postinstall.py
index 7cc0ab22e..1d162ac91 100644
--- a/pyside_postinstall.py
+++ b/pyside_postinstall.py
@@ -5,6 +5,10 @@
# This file is based on pywin32_postinstall.py file from pywin32 project
import os, sys, traceback, shutil, fnmatch, stat
+from os.path import dirname, abspath
+from subprocess import Popen, PIPE
+import re
+
try:
# When this script is run from inside the bdist_wininst installer,
@@ -23,7 +27,7 @@ def install():
if sys.platform == "win32":
install_win32()
else:
- install_linux()
+ install_posix()
def filter_match(name, patterns):
for pattern in patterns:
@@ -46,7 +50,138 @@ def set_exec(name):
print("Setting exec for '%s' (mode %o => %o)" % (name, mode, new_mode))
os.chmod(name, new_mode)
-def install_linux():
+
+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()
+ 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 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 install_posix():
# Try to find PySide package
try:
import PySide
@@ -56,27 +191,40 @@ def install_linux():
pyside_path = os.path.abspath(os.path.dirname(PySide.__file__))
print("PySide package found in %s..." % pyside_path)
+ executables = ['shiboken']
+ if sys.platform == 'linux2':
+ executables.append('patchelf')
+ patchelf_path = os.path.join(pyside_path, "patchelf")
+ from distutils.spawn import spawn
+
+ def rpath_cmd(pyside_path, srcpath):
+ cmd = [patchelf_path, '--set-rpath', pyside_path, srcpath]
+ spawn(cmd, search_path=False, verbose=1)
+
+ pyside_libs = [lib for lib in os.listdir(pyside_path) if filter_match(
+ lib, ["Qt*.so", "phonon.so", "shiboken"])]
+ elif sys.platform == 'darwin':
+ pyside_libs = [lib for lib in os.listdir(pyside_path) if filter_match(
+ lib, ["*.so", "*.dylib", "shiboken"])]
+
+ def rpath_cmd(pyside_path, srcpath):
+ localize_libpaths(srcpath, pyside_libs, pyside_path)
+
+ else:
+ raise RuntimeError('Not configured for platform ' +
+ sys.platform)
+
# Set exec mode on executables
- for elf in ["patchelf", "shiboken"]:
- elfpath = os.path.join(pyside_path, elf)
- set_exec(elfpath)
+ for executable in executables:
+ execpath = os.path.join(pyside_path, executable)
+ set_exec(execpath)
# Update rpath in PySide libs
- from distutils.spawn import spawn
- patchelf_path = os.path.join(pyside_path, "patchelf")
- for srcname in os.listdir(pyside_path):
+ for srcname in pyside_libs:
if os.path.isdir(srcname):
continue
- if not filter_match(srcname, ["Qt*.so", "phonon.so", "shiboken", "shiboken.so"]):
- continue
srcpath = os.path.join(pyside_path, srcname)
- cmd = [
- patchelf_path,
- "--set-rpath",
- pyside_path,
- srcpath,
- ]
- spawn(cmd, search_path=0, verbose=1)
+ rpath_cmd(pyside_path, srcpath)
print("Patched rpath in %s to %s." % (srcpath, pyside_path))
# Check PySide installation status
@@ -87,6 +235,7 @@ def install_linux():
except ImportError:
print("The PySide package not installed: %s" % traceback.print_exception(*sys.exc_info()))
+
def install_win32():
# Try to find PySide package
try: