aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2017-11-07 16:14:11 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2017-11-15 15:47:09 +0000
commite455d995be989cbdfef2bcd54fd7057a9b036b52 (patch)
tree38a2865cb44350696d60fd15cd5b46a9278d17d9
parent49fb9494ba6589cacda3c9e76fc354650a9fd87e (diff)
Make standalone installations relocatable
This is achieved by registering a qt.conf file with a Prefix pointing to a directory relative to the loaded PySide2 module (e.g. QtCore). Thus Qt does not crash due to not finding platform plugins. Because this change would affect tests, which are ran before the PySide package is installed, a new environment variable called PYSIDE_DISABLE_INTERNAL_QT_CONF is introduced. This variable disables the registration of the internal qt.conf file, thus it will not point to a not yet created location, which will allow tests to run as before. Change-Id: I5a96037adfafe1f08ea57535aa4a2a0d1660dfaf Task-number: PYSIDE-558 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--setup.py21
-rw-r--r--sources/pyside2/PySide2/QtCore/typesystem_core_common.xml8
-rw-r--r--sources/pyside2/libpyside/CMakeLists.txt7
-rw-r--r--sources/pyside2/libpyside/pyside.cpp130
-rw-r--r--sources/pyside2/libpyside/pyside.h13
-rw-r--r--sources/pyside2/tests/CMakeLists.txt2
6 files changed, 176 insertions, 5 deletions
diff --git a/setup.py b/setup.py
index 55b7676d8..d9a245f73 100644
--- a/setup.py
+++ b/setup.py
@@ -61,6 +61,15 @@ without rebuilding entire PySide2 every time:
# Then we create bdist_egg reusing PySide2 build with option --only-package
python setup.py bdist_egg --only-package --qmake=c:\Qt\4.8.5\bin\qmake.exe --cmake=c:\tools\cmake\bin\cmake.exe --opnessl=c:\libs\OpenSSL32bit\bin
+You can use the option --qt-conf-prefix to pass a path relative to the PySide2 installed package,
+which will be embedded into an auto-generated qt.conf registered in the Qt resource system. This
+path will serve as the PrefixPath for QLibraryInfo, thus allowing to choose where Qt plugins
+should be loaded from. This option overrides the usual prefix chosen by --standalone option, or when
+building on Windows.
+To temporarily disable registration of the internal qt.conf file, a new environment variable called
+PYSIDE_DISABLE_INTERNAL_QT_CONF is introduced. You should assign the integer "1" to disable the
+internal qt.conf, or "0" (or leave empty) to keep use the internal qt.conf file.
+
For development purposes the following options might be of use, when using "setup.py build":
--reuse-build option allows recompiling only the modified sources and not the whole world,
shortening development iteration time,
@@ -241,6 +250,7 @@ OPTION_SKIP_CMAKE = has_option("skip-cmake")
OPTION_SKIP_MAKE_INSTALL = has_option("skip-make-install")
OPTION_SKIP_PACKAGING = has_option("skip-packaging")
OPTION_RPATH_VALUES = option_value("rpath")
+OPTION_QT_CONF_PREFIX = option_value("qt-conf-prefix")
if OPTION_QT_VERSION is None:
OPTION_QT_VERSION = "5"
@@ -854,6 +864,17 @@ class pyside_build(_build):
if self.build_type.lower() == 'debug':
cmake_cmd.append("-DPYTHON_DEBUG_LIBRARY=%s" % self.py_library)
+ if extension.lower() == "pyside2":
+ pyside_qt_conf_prefix = ''
+ if OPTION_QT_CONF_PREFIX:
+ pyside_qt_conf_prefix = OPTION_QT_CONF_PREFIX
+ else:
+ if OPTION_STANDALONE:
+ pyside_qt_conf_prefix = '"Qt"'
+ if sys.platform == 'win32':
+ pyside_qt_conf_prefix = '"."'
+ cmake_cmd.append("-DPYSIDE_QT_CONF_PREFIX=%s" % pyside_qt_conf_prefix)
+
if extension.lower() == "shiboken2":
cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes")
if sys.version_info[0] > 2:
diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
index 951d943ee..65f68882d 100644
--- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
+++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
@@ -74,6 +74,10 @@
<function signature="qrand()" />
<function signature="qsrand(uint)" />
+ <inject-code class="native" position="beginning">
+ #include &lt;pyside.h&gt;
+ </inject-code>
+
<template name="use_stream_for_format_security">
// Uses the stream version for security reasons
// see gcc man page at -Wformat-security
@@ -1039,12 +1043,10 @@
Shiboken::Conversions::registerConverterName(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], "str");
Shiboken::Conversions::registerConverterName(SbkPySide2_QtCoreTypeConverters[SBK_QTCORE_QLIST_QVARIANT_IDX], "QVariantList");
+ PySide::registerInternalQtConf();
PySide::init(module);
Py_AtExit(QtCoreModuleExit);
</inject-code>
- <inject-code class="native" position="beginning">
- #include &lt;pyside.h&gt;
- </inject-code>
<inject-code class="native" position="beginning">
// Define a global variable to handle qInstallMessageHandler callback
diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt
index 1cddebc07..de29269a4 100644
--- a/sources/pyside2/libpyside/CMakeLists.txt
+++ b/sources/pyside2/libpyside/CMakeLists.txt
@@ -111,6 +111,13 @@ if(QML_SUPPORT)
target_compile_definitions(pyside2 PUBLIC PYSIDE_QML_SUPPORT=1)
endif()
+if(PYSIDE_QT_CONF_PREFIX)
+ set_property(SOURCE pyside.cpp
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS
+ PYSIDE_QT_CONF_PREFIX=${PYSIDE_QT_CONF_PREFIX})
+endif()
+
#
# install stuff
#
diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp
index 17366ce6e..b223edc6c 100644
--- a/sources/pyside2/libpyside/pyside.cpp
+++ b/sources/pyside2/libpyside/pyside.cpp
@@ -61,14 +61,20 @@
#include <typeinfo>
#include <cstring>
#include <cctype>
-#include <QStack>
+#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
#include <QSharedPointer>
+#include <QStack>
static QStack<PySide::CleanupFunction> cleanupFunctionList;
static void* qobjectNextAddr;
+extern bool qRegisterResourceData(int, const unsigned char *, const unsigned char *,
+ const unsigned char *);
+
namespace PySide
{
@@ -387,5 +393,127 @@ void setQuickRegisterItemFunction(QuickRegisterItemFunction function)
}
#endif // PYSIDE_QML_SUPPORT
+// Inspired by Shiboken::String::toCString;
+QString pyStringToQString(PyObject *str) {
+ if (str == Py_None)
+ return QString();
+
+#ifdef IS_PY3K
+ if (PyUnicode_Check(str)) {
+ const char *unicodeBuffer = _PyUnicode_AsString(str);
+ if (unicodeBuffer)
+ return QString::fromUtf8(unicodeBuffer);
+ }
+#endif
+ if (PyBytes_Check(str)) {
+ const char *asciiBuffer = PyBytes_AS_STRING(str);
+ if (asciiBuffer)
+ return QString::fromLatin1(asciiBuffer);
+ }
+ return QString();
+}
+
+static const unsigned char qt_resource_name[] = {
+ // qt
+ 0x0,0x2,
+ 0x0,0x0,0x7,0x84,
+ 0x0,0x71,
+ 0x0,0x74,
+ // etc
+ 0x0,0x3,
+ 0x0,0x0,0x6c,0xa3,
+ 0x0,0x65,
+ 0x0,0x74,0x0,0x63,
+ // qt.conf
+ 0x0,0x7,
+ 0x8,0x74,0xa6,0xa6,
+ 0x0,0x71,
+ 0x0,0x74,0x0,0x2e,0x0,0x63,0x0,0x6f,0x0,0x6e,0x0,0x66
+};
+
+static const unsigned char qt_resource_struct[] = {
+ // :
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+ // :/qt
+ 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,
+ // :/qt/etc
+ 0x0,0x0,0x0,0xa,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,
+ // :/qt/etc/qt.conf
+ 0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0
+};
+
+bool registerInternalQtConf()
+{
+ // Guard to ensure single registration.
+#ifdef PYSIDE_QT_CONF_PREFIX
+ static bool registrationAttempted = false;
+#else
+ static bool registrationAttempted = true;
+#endif
+ static bool isRegistered = false;
+ if (registrationAttempted)
+ return isRegistered;
+ registrationAttempted = true;
+
+ // Allow disabling the usage of the internal qt.conf. This is necessary for tests to work,
+ // because tests are executed before the package is installed, and thus the Prefix specified
+ // in qt.conf would point to a not yet existing location.
+ bool disableInternalQtConf =
+ qEnvironmentVariableIntValue("PYSIDE_DISABLE_INTERNAL_QT_CONF") > 0 ? true : false;
+ if (disableInternalQtConf) {
+ registrationAttempted = true;
+ return false;
+ }
+
+ PyObject *pysideModule = PyImport_ImportModule("PySide2");
+ if (!pysideModule)
+ return false;
+
+ // Querying __file__ should be done only for modules that have finished their initialization.
+ // Thus querying for the top-level PySide2 package works for us whenever any Qt-wrapped module
+ // is loaded.
+ PyObject *pysideInitFilePath = PyObject_GetAttrString(pysideModule, "__file__");
+ Py_DECREF(pysideModule);
+ if (!pysideInitFilePath)
+ return false;
+
+ QString initPath = pyStringToQString(pysideInitFilePath);
+ Py_DECREF(pysideInitFilePath);
+ if (initPath.isEmpty())
+ return false;
+
+ // pysideDir - absolute path to the directory containing the init file, which also contains
+ // the rest of the PySide2 modules.
+ // prefixPath - absolute path to the directory containing the installed Qt (prefix).
+ QDir pysideDir = QFileInfo(QDir::fromNativeSeparators(initPath)).absoluteDir();
+ QString setupPrefix;
+#ifdef PYSIDE_QT_CONF_PREFIX
+ setupPrefix = QStringLiteral(PYSIDE_QT_CONF_PREFIX);
+#endif
+ QString prefixPath = pysideDir.absoluteFilePath(setupPrefix);
+
+ // rccData needs to be static, otherwise when it goes out of scope, the Qt resource system
+ // will point to invalid memory.
+ static QByteArray rccData = QByteArray("[Paths]\nPrefix = ") + prefixPath.toLocal8Bit();
+ rccData.append('\n');
+
+ // The RCC data structure expects a 4-byte size value representing the actual data.
+ int size = rccData.size();
+
+ for (int i = 0; i < 4; ++i) {
+ rccData.prepend((size & 0xff));
+ size >>= 8;
+ }
+
+ const int version = 0x01;
+ isRegistered = qRegisterResourceData(version, qt_resource_struct, qt_resource_name,
+ reinterpret_cast<const unsigned char *>(
+ rccData.constData()));
+
+ return isRegistered;
+}
+
+
+
} //namespace PySide
diff --git a/sources/pyside2/libpyside/pyside.h b/sources/pyside2/libpyside/pyside.h
index 3619e2875..becb92208 100644
--- a/sources/pyside2/libpyside/pyside.h
+++ b/sources/pyside2/libpyside/pyside.h
@@ -150,6 +150,19 @@ PYSIDE_API QuickRegisterItemFunction getQuickRegisterItemFunction();
PYSIDE_API void setQuickRegisterItemFunction(QuickRegisterItemFunction function);
#endif // PYSIDE_QML_SUPPORT
+/**
+ * Given A PyObject repesenting ASCII or Unicode data, returns an equivalent QString.
+ */
+PYSIDE_API QString pyStringToQString(PyObject *str);
+
+/**
+ * Registers a dynamic "qt.conf" file with the Qt resource system.
+ *
+ * This is used in a standalone build, to inform QLibraryInfo of the Qt prefix (where Qt libraries
+ * are installed) so that plugins can be successfully loaded.
+ */
+PYSIDE_API bool registerInternalQtConf();
+
} //namespace PySide
diff --git a/sources/pyside2/tests/CMakeLists.txt b/sources/pyside2/tests/CMakeLists.txt
index 2b7e3b0e1..34a2e2501 100644
--- a/sources/pyside2/tests/CMakeLists.txt
+++ b/sources/pyside2/tests/CMakeLists.txt
@@ -43,7 +43,7 @@ else()
set_tests_properties(${TEST_NAME} PROPERTIES
TIMEOUT ${CTEST_TESTING_TIMEOUT}
WILL_FAIL ${EXPECT_TO_FAIL}
- ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH}")
+ ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH};PYSIDE_DISABLE_INTERNAL_QT_CONF=1")
endmacro()
add_subdirectory(pysidetest)