aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2017-11-02 11:50:25 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2017-11-15 15:47:02 +0000
commit8815ef1cfddd79b99314a8e8e351b1a2235afc26 (patch)
tree99aad14c7f58c3be9141cbc4d7a8c9ff806e9ab5
parentcadc370720773b4372496fafc40b34f604da6f9b (diff)
Make standalone option work on macOS
Implements standalone option on macOS, both for .dylib Qt build and framework build. Multiple rules are applied to figure out which files need to be copied into the final package. We also take care to embed a proper LC_RPATH for the PySide libraries, so that they point to the copied over Qt libraries. Change-Id: I442749e7c2318a66a22e3a1dd0ae703fb8943acf Task-number: PYSIDE-558 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--qtinfo.py12
-rw-r--r--setup.py161
-rw-r--r--sources/pyside2/PySide2/CMakeLists.txt5
-rw-r--r--sources/pyside2/PySide2/_built_modules.py.in3
-rw-r--r--utils.py10
5 files changed, 157 insertions, 34 deletions
diff --git a/qtinfo.py b/qtinfo.py
index 1e3f278e0..e85b322e2 100644
--- a/qtinfo.py
+++ b/qtinfo.py
@@ -28,9 +28,15 @@ class QtInfo(object):
def getLibsPath(self):
return self.getProperty("QT_INSTALL_LIBS")
+ def getLibsExecsPath(self):
+ return self.getProperty("QT_INSTALL_LIBEXECS")
+
def getPluginsPath(self):
return self.getProperty("QT_INSTALL_PLUGINS")
+ def getPrefixPath(self):
+ return self.getProperty("QT_INSTALL_PREFIX")
+
def getImportsPath(self):
return self.getProperty("QT_INSTALL_IMPORTS")
@@ -43,6 +49,9 @@ class QtInfo(object):
def getDocsPath(self):
return self.getProperty("QT_INSTALL_DOCS")
+ def getQmlPath(self):
+ return self.getProperty("QT_INSTALL_QML")
+
def _getProperty(self, prop_name):
cmd = self._qmake_command + ["-query", prop_name]
proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, shell=False)
@@ -62,9 +71,12 @@ class QtInfo(object):
version = property(getVersion)
bins_dir = property(getBinsPath)
libs_dir = property(getLibsPath)
+ lib_execs_dir = property(getLibsExecsPath)
plugins_dir = property(getPluginsPath)
+ prefix_dir = property(getPrefixPath)
qmake_command = property(getQMakeCommand)
imports_dir = property(getImportsPath)
translations_dir = property(getTranslationsPath)
headers_dir = property(getHeadersPath)
docs_dir = property(getDocsPath)
+ qml_dir = property(getQmlPath)
diff --git a/setup.py b/setup.py
index 1ee13ffcd..ecc9dbb5f 100644
--- a/setup.py
+++ b/setup.py
@@ -151,6 +151,7 @@ import sys
import platform
import time
import re
+import fnmatch
import difflib # for a close match of dirname and module
@@ -301,10 +302,6 @@ if OPTION_JOBS:
else:
OPTION_JOBS = ''
-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:
@@ -938,11 +935,15 @@ class pyside_build(_build):
"qt_bin_dir": self.qtinfo.bins_dir,
"qt_doc_dir": self.qtinfo.docs_dir,
"qt_lib_dir": self.qtinfo.libs_dir,
+ "qt_lib_execs_dir": self.qtinfo.lib_execs_dir,
"qt_plugins_dir": self.qtinfo.plugins_dir,
+ "qt_prefix_dir": self.qtinfo.prefix_dir,
"qt_translations_dir": self.qtinfo.translations_dir,
+ "qt_qml_dir": self.qtinfo.qml_dir,
"version": version_str,
}
os.chdir(self.script_dir)
+
if sys.platform == "win32":
vars['dbgPostfix'] = OPTION_DEBUG and "_d" or ""
return self.prepare_packages_win32(vars)
@@ -1050,36 +1051,131 @@ class pyside_build(_build):
# Copy Qt libs to package
if OPTION_STANDALONE:
if sys.platform == 'darwin':
- raise RuntimeError('--standalone not yet supported for OSX')
- # <qt>/bin/* -> <setup>/PySide2
- executables.extend(copydir("{qt_bin_dir}", "{dist_dir}/PySide2",
- filter=[
- "designer",
- "linguist",
- "lrelease",
- "lupdate",
- "lconvert",
- ],
- recursive=False, vars=vars))
- # <qt>/lib/* -> <setup>/PySide2
- copydir("{qt_lib_dir}", "{dist_dir}/PySide2",
- filter=[
- "libQt*.so.?",
- "libphonon.so.?",
- ],
- recursive=False, vars=vars)
- # <qt>/plugins/* -> <setup>/PySide2/plugins
- copydir("{qt_plugins_dir}", "{dist_dir}/PySide2/plugins",
- filter=["*.so"],
- vars=vars)
- # <qt>/translations/* -> <setup>/PySide2/translations
- copydir("{qt_translations_dir}", "{dist_dir}/PySide2/translations",
- filter=["*.qm"],
- vars=vars)
+ self.prepare_standalone_package_osx(executables, vars)
+ else:
+ # <qt>/bin/* -> <setup>/PySide2
+ executables.extend(copydir("{qt_bin_dir}", "{dist_dir}/PySide2",
+ filter=[
+ "designer",
+ "linguist",
+ "lrelease",
+ "lupdate",
+ "lconvert",
+ ],
+ recursive=False, vars=vars))
+ # <qt>/lib/* -> <setup>/PySide2
+ copydir("{qt_lib_dir}", "{dist_dir}/PySide2",
+ filter=[
+ "libQt*.so.?",
+ "libphonon.so.?",
+ ],
+ recursive=False, vars=vars)
+ # <qt>/plugins/* -> <setup>/PySide2/plugins
+ copydir("{qt_plugins_dir}", "{dist_dir}/PySide2/plugins",
+ filter=["*.so"],
+ vars=vars)
+ # <qt>/translations/* -> <setup>/PySide2/translations
+ copydir("{qt_translations_dir}", "{dist_dir}/PySide2/translations",
+ filter=["*.qm"],
+ vars=vars)
# Update rpath to $ORIGIN
if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
self.update_rpath("{dist_dir}/PySide2".format(**vars), executables)
+ def qt_is_framework_build(self):
+ if os.path.isdir(self.qtinfo.headers_dir + "/../lib/QtCore.framework"):
+ return True
+ return False
+
+ def prepare_standalone_package_osx(self, executables, vars):
+ # Get list of built modules, so that we copy only required Qt libraries.
+ pyside_package_dir = vars['dist_dir']
+ built_modules_path = os.path.join(pyside_package_dir, "PySide2", "_built_modules.py")
+
+ with open(built_modules_path) as f:
+ scoped_locals = {}
+ code = compile(f.read(), built_modules_path, 'exec')
+ exec(code, scoped_locals, scoped_locals)
+ built_modules = scoped_locals['built_modules']
+
+ # Directory filter for skipping unnecessary files.
+ def general_dir_filter(dir_name, parent_full_path, dir_full_path):
+ if fnmatch.fnmatch(dir_name, "*.dSYM"):
+ return False
+ return True
+
+ # <qt>/lib/* -> <setup>/PySide2/Qt/lib
+ if self.qt_is_framework_build():
+ framework_built_modules = ['Qt' + name + '.framework' for name in built_modules]
+
+ def framework_dir_filter(dir_name, parent_full_path, dir_full_path):
+ if '.framework' in dir_name:
+ if dir_name in ['QtWebEngine.framework', 'QtWebEngineCore.framework', \
+ 'QtPositioning.framework', 'QtLocation.framework'] and \
+ 'QtWebEngineWidgets.framework' in framework_built_modules:
+ return True
+ if dir_name in ['QtCLucene.framework'] and \
+ 'QtHelp.framework' in framework_built_modules:
+ return True
+ if dir_name not in framework_built_modules:
+ return False
+ if dir_name in ['Headers', 'fonts']:
+ return False
+ if dir_full_path.endswith('Versions/Current'):
+ return False
+ if dir_full_path.endswith('Versions/5/Resources'):
+ return False
+ if dir_full_path.endswith('Versions/5/Helpers'):
+ return False
+ return general_dir_filter(dir_name, parent_full_path, dir_full_path)
+
+ copydir("{qt_lib_dir}", "{dist_dir}/PySide2/Qt/lib",
+ recursive=True, vars=vars,
+ ignore=["*.la", "*.a", "*.cmake", "*.pc", "*.prl"],
+ dir_filter_function=framework_dir_filter)
+ else:
+ if 'WebEngineWidgets' in built_modules:
+ built_modules.extend(['WebEngine', 'WebEngineCore', 'Positioning', 'Location'])
+ if 'Help' in built_modules:
+ built_modules.extend(['CLucene'])
+ prefixed_built_modules = ['*Qt5' + name + '*.dylib' for name in built_modules]
+
+ copydir("{qt_lib_dir}", "{dist_dir}/PySide2/Qt/lib",
+ filter=prefixed_built_modules,
+ recursive=True, vars=vars)
+
+ if 'WebEngineWidgets' in built_modules:
+ copydir("{qt_lib_execs_dir}", "{dist_dir}/PySide2/Qt/libexec",
+ filter=None,
+ recursive=False,
+ vars=vars)
+
+ copydir("{qt_prefix_dir}/resources", "{dist_dir}/PySide2/Qt/resources",
+ filter=None,
+ recursive=False,
+ vars=vars)
+
+ # <qt>/plugins/* -> <setup>/PySide2/Qt/plugins
+ copydir("{qt_plugins_dir}", "{dist_dir}/PySide2/Qt/plugins",
+ filter=["*.dylib"],
+ recursive=True,
+ dir_filter_function=general_dir_filter,
+ vars=vars)
+
+ # <qt>/qml/* -> <setup>/PySide2/Qt/qml
+ copydir("{qt_qml_dir}", "{dist_dir}/PySide2/Qt/qml",
+ filter=None,
+ recursive=True,
+ force=False,
+ dir_filter_function=general_dir_filter,
+ vars=vars)
+
+ # <qt>/translations/* -> <setup>/PySide2/Qt/translations
+ copydir("{qt_translations_dir}", "{dist_dir}/PySide2/Qt/translations",
+ filter=["*.qm"],
+ force=False,
+ vars=vars)
+
def prepare_packages_win32(self, vars):
pdbs = ['*.pdb'] if self.debug or self.build_type == 'RelWithDebInfo' else []
# <install>/lib/site-packages/PySide2/* -> <setup>/PySide2
@@ -1272,7 +1368,10 @@ class pyside_build(_build):
if OPTION_RPATH_VALUES:
final_rpath = OPTION_RPATH_VALUES
else:
- final_rpath = self.qtinfo.libs_dir
+ if OPTION_STANDALONE:
+ final_rpath = "@loader_path/Qt/lib"
+ else:
+ final_rpath = self.qtinfo.libs_dir
osx_fix_rpaths_for_library(srcpath, final_rpath)
else:
diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt
index 53c4216fa..2a8cf15ee 100644
--- a/sources/pyside2/PySide2/CMakeLists.txt
+++ b/sources/pyside2/PySide2/CMakeLists.txt
@@ -7,6 +7,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/global.h.in"
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_built_modules.py.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/_built_modules.py" @ONLY)
+
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_utils.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/_utils.py" @ONLY)
@@ -46,6 +49,8 @@ endforeach()
# install
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py"
DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_built_modules.py"
+ DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/_utils.py
DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_templates.xml
diff --git a/sources/pyside2/PySide2/_built_modules.py.in b/sources/pyside2/PySide2/_built_modules.py.in
new file mode 100644
index 000000000..4e491d081
--- /dev/null
+++ b/sources/pyside2/PySide2/_built_modules.py.in
@@ -0,0 +1,3 @@
+built_modules = list(name for name in
+ "@all_module_shortnames@"
+ .split(";"))
diff --git a/utils.py b/utils.py
index 1762bb8ee..98410838e 100644
--- a/utils.py
+++ b/utils.py
@@ -275,8 +275,8 @@ def makefile(dst, content=None, vars=None):
f.close()
-def copydir(src, dst, filter=None, ignore=None, force=True,
- recursive=True, vars=None):
+def copydir(src, dst, filter=None, ignore=None, force=True, recursive=True, vars=None,
+ dir_filter_function=None):
if vars is not None:
src = src.format(**vars)
@@ -305,8 +305,12 @@ def copydir(src, dst, filter=None, ignore=None, force=True,
dstname = os.path.join(dst, name)
try:
if os.path.isdir(srcname):
+ if dir_filter_function and not dir_filter_function(name, src, srcname):
+ continue
if recursive:
- results.extend(copydir(srcname, dstname, filter, ignore, force, recursive, vars))
+ results.extend(
+ copydir(srcname, dstname, filter, ignore, force, recursive,
+ vars, dir_filter_function))
else:
if (filter is not None and not filter_match(name, filter)) or \
(ignore is not None and filter_match(name, ignore)):