aboutsummaryrefslogtreecommitdiffstats
path: root/sources
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 /sources
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>
Diffstat (limited to 'sources')
-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
5 files changed, 155 insertions, 5 deletions
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)