aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugo Parente Lima <hugo.pl@gmail.com>2013-01-24 09:51:01 -0800
committerHugo Parente Lima <hugo.pl@gmail.com>2013-01-24 09:51:01 -0800
commit6d18ae7a282ef9e810c615f5101a681c084dd184 (patch)
tree1620970cd897bf7ff64ec9b72716bd4310f8e349
parent64444264ced0c15d848c5ad2db230755d145903e (diff)
parent389384129dde6cbb51a7e77a96d28daf62c2245a (diff)
Merge pull request #3 from matthew-brett/osx-fixes
Osx fixes
-rw-r--r--pyside_postinstall.py183
-rw-r--r--setup.py87
2 files changed, 217 insertions, 53 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:
diff --git a/setup.py b/setup.py
index 1768b5802..a2901acf9 100644
--- a/setup.py
+++ b/setup.py
@@ -112,6 +112,9 @@ else:
print("Invalid option --make-spec. Available values are %s" % (["make"]))
sys.exit(1)
+if sys.platform == 'darwin' and OPTION_STANDALONE:
+ print("--standalone option does not yet work on OSX")
+
# Show available versions
if OPTION_LISTVERSIONS:
for v in submodules:
@@ -286,28 +289,31 @@ class pyside_build(_build):
py_library = os.path.join(py_libdir, "python%s%s.lib" % \
(py_version.replace(".", ""), dbgPostfix))
else:
+ lib_exts = ['.so']
+ if sys.platform == 'darwin':
+ lib_exts.append('.dylib')
if sys.version_info[0] > 2:
- py_abiflags = getattr(sys, 'abiflags', None)
- py_library = os.path.join(py_libdir, "libpython%s%s.so" % \
- (py_version, py_abiflags))
- # Python was compiled as a static library ?
- if not os.path.exists(py_library):
- log.error("Failed to locate the Python library %s" % py_library)
- py_library = py_library[:-3] + ".a"
+ lib_suff = getattr(sys, 'abiflags', None)
+ else: # Python 2
+ lib_suff = dbgPostfix
+ lib_exts.append('.so.1')
+ lib_exts.append('.a') # static library as last gasp
+ libs_tried = []
+ for lib_ext in lib_exts:
+ lib_name = "libpython%s%s%s" % (py_version, lib_suff, lib_ext)
+ py_library = os.path.join(py_libdir, lib_name)
+ if os.path.exists(py_library):
+ break
+ libs_tried.append(py_library)
else:
- py_library = os.path.join(py_libdir, "libpython%s%s.so" % \
- (py_version, dbgPostfix))
- if not os.path.exists(py_library):
- log.error("Failed to locate the Python library %s" % py_library)
- py_library = py_library + ".1"
- # Python was compiled as a static library ?
- if not os.path.exists(py_library):
- log.error("Failed to locate the Python library %s" % py_library)
- py_library = py_library[:-5] + ".a"
- if not os.path.exists(py_library):
- raise DistutilsSetupError(
- "Failed to locate the Python library %s" % py_library)
-
+ raise DistutilsSetupError(
+ "Failed to locate the Python library with %s" %
+ ', '.join(libs_tried))
+ if py_library.endswith('.a'):
+ # Python was compiled as a static library
+ log.error("Failed to locate a dynamic Python library, using %s"
+ % py_library)
+
qtinfo = QtInfo(OPTION_QMAKE)
# Update os.path
@@ -388,7 +394,7 @@ class pyside_build(_build):
for ext in ['shiboken', 'pyside', 'pyside-tools']:
self.build_extension(ext)
- # Build patchelf
+ # Build patchelf if needed
self.build_patchelf()
# Prepare packages
@@ -400,18 +406,14 @@ class pyside_build(_build):
def build_patchelf(self):
if sys.platform != "linux2":
return
-
log.info("Building patchelf...")
-
module_src_dir = os.path.join(self.sources_dir, "patchelf")
-
build_cmd = [
"g++",
"%s/patchelf.cc" % (module_src_dir),
"-o",
"patchelf",
]
-
if run_process(build_cmd, log) != 0:
raise DistutilsSetupError("Error building patchelf")
@@ -458,7 +460,12 @@ class pyside_build(_build):
cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes")
if sys.version_info[0] > 2:
cmake_cmd.append("-DUSE_PYTHON3=ON")
-
+ elif sys.platform == 'darwin':
+ # Work round cmake include problem
+ # http://neilweisenfeld.com/wp/120/building-pyside-on-the-mac
+ # https://groups.google.com/forum/#!msg/pyside/xciZZ4Hm2j8/CUmqfJptOwoJ
+ cmake_cmd.append('-DALTERNATIVE_QT_INCLUDE_DIR=/Library/Frameworks')
+
log.info("Configuring module %s (%s)..." % (extension, module_src_dir))
if run_process(cmake_cmd, log) != 0:
raise DistutilsSetupError("Error configuring " + extension)
@@ -495,14 +502,20 @@ class pyside_build(_build):
os.chdir(self.script_dir)
if sys.platform == "win32":
return self.prepare_packages_win32(vars)
- return self.prepare_packages_linux(vars)
-
- def prepare_packages_linux(self, vars):
- # patchelf -> PySide/patchelf
- copyfile(
- "{setup_dir}/patchelf",
- "{setup_dir}/PySide/patchelf",
- logger=log, vars=vars)
+ return self.prepare_packages_posix(vars)
+
+ def prepare_packages_posix(self, vars):
+ if sys.platform == 'linux2':
+ # patchelf -> PySide/patchelf
+ copyfile(
+ "{setup_dir}/patchelf",
+ "{setup_dir}/PySide/patchelf",
+ logger=log, vars=vars)
+ so_ext = '.so'
+ so_star = so_ext + '.*'
+ elif sys.platform == 'darwin':
+ so_ext = '.dylib'
+ so_star = so_ext
# <install>/lib/site-packages/PySide/* -> <setup>/PySide
copydir(
"{install_dir}/lib/python{py_version}/site-packages/PySide",
@@ -541,8 +554,8 @@ class pyside_build(_build):
"{install_dir}/lib/",
"{setup_dir}/PySide",
filter=[
- "libpyside*.so.*",
- "libshiboken*.so.*",
+ "libpyside*" + so_star,
+ "libshiboken*" + so_star,
],
recursive=False, logger=log, vars=vars)
# <install>/share/PySide/typesystems/* -> <setup>/PySide/typesystems
@@ -562,6 +575,8 @@ class pyside_build(_build):
force=False, logger=log, vars=vars)
# Copy Qt libs to package
if OPTION_STANDALONE:
+ if sys.platform == 'darwin':
+ raise RuntimeError('--standalone not yet supported for OSX')
# <qt>/bin/* -> <setup>/PySide
copydir("{qt_bin_dir}", "{setup_dir}/PySide",
filter=[