From 788a7bfdb1981108efbf55bc42a468ca0e8b102e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 10 Sep 2021 15:48:06 -0700 Subject: [PATCH] QPlugin: move some of the logic from moc's output to qplugin.h This will allow us to make changes in QtCore itself, without having to worry about moc compatibility. The output uses an #ifdef so this version of moc can still be used to compile earlier versions of Qt (usually, in cross-compilation environments). See discussion in the mailing list[1]. [1] https://lists.qt-project.org/pipermail/development/2021-September/041732.html Change-Id: I2de1b4dfacd443148279fffd16a39784c80c5f3b Reviewed-by: Lars Knoll --- src/corelib/plugin/qplugin.h | 76 +++++++++++-- src/tools/moc/generator.cpp | 100 ++++++++++-------- .../corelib/plugin/qplugin/tst_qplugin.cpp | 11 +- 3 files changed, 129 insertions(+), 58 deletions(-) diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 411edba7926..a80ee28c52a 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -65,7 +65,36 @@ inline constexpr unsigned char qPluginArchRequirements() typedef QObject *(*QtPluginInstanceFunction)(); struct QPluginMetaData { - const uchar *data; + static constexpr quint8 CurrentMetaDataVersion = 0; + static constexpr char MagicString[] = { + 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!' + }; + + template + static constexpr void copy(OO (&out)[OSize], II (&in)[ISize]) + { + // std::copy is not constexpr until C++20 + static_assert(OSize <= ISize, "Output would not be fully initialized"); + for (size_t i = 0; i < OSize; ++i) + out[i] = in[i]; + } + + struct Header { + quint8 version = CurrentMetaDataVersion; + quint8 qt_major_version = QT_VERSION_MAJOR; + quint8 qt_minor_version = QT_VERSION_MINOR; + quint8 plugin_arch_requirements = qPluginArchRequirements(); + }; + static_assert(alignof(Header) == 1, "Alignment of header incorrect with this compiler"); + + struct MagicHeader { + char magic[sizeof(QPluginMetaData::MagicString)] = {}; + constexpr MagicHeader() { copy(magic, QPluginMetaData::MagicString); } + Header header = {}; + }; + static_assert(alignof(MagicHeader) == 1, "Alignment of header incorrect with this compiler"); + + const void *data; size_t size; }; typedef QPluginMetaData (*QtPluginMetaDataFunction)(); @@ -104,6 +133,24 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin); # define QT_PLUGIN_METADATA_SECTION #endif +// Since Qt 6.3 +template class QPluginMetaDataV2 +{ + struct Payload { + QPluginMetaData::MagicHeader header = {}; + quint8 payload[sizeof(PluginMetaData)] = {}; + constexpr Payload() { QPluginMetaData::copy(payload, PluginMetaData); } + }; + +#define QT_PLUGIN_METADATAV2_SECTION QT_PLUGIN_METADATA_SECTION + Payload payload = {}; + +public: + operator QPluginMetaData() const + { + return { &payload, sizeof(payload) }; + } +}; #define Q_IMPORT_PLUGIN(PLUGIN) \ extern const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGIN(); \ @@ -134,25 +181,38 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin); } #if defined(QT_STATICPLUGIN) +# define QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) \ + static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##MANGLEDNAME() \ + Q_PLUGIN_INSTANCE(PLUGINCLASS) \ + const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##MANGLEDNAME() \ + { return { qt_plugin_instance_##MANGLEDNAME, qt_plugin_query_metadata_##MANGLEDNAME}; } \ + /**/ # define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ - static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGINCLASSNAME() \ - Q_PLUGIN_INSTANCE(PLUGINCLASS) \ static QPluginMetaData qt_plugin_query_metadata_##PLUGINCLASSNAME() \ { return { qt_pluginMetaData_##PLUGINCLASSNAME, sizeof qt_pluginMetaData_##PLUGINCLASSNAME }; } \ - const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGINCLASSNAME() { \ - return { qt_plugin_instance_##PLUGINCLASSNAME, qt_plugin_query_metadata_##PLUGINCLASSNAME}; \ - } + QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, PLUGINCLASSNAME) +# define QT_MOC_EXPORT_PLUGIN_V2(PLUGINCLASS, MANGLEDNAME, MD) \ + static QT_PREPEND_NAMESPACE(QPluginMetaData) qt_plugin_query_metadata_##MANGLEDNAME() \ + { static constexpr QPluginMetaDataV2 md{}; return md; } \ + QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) #else +# define QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) \ + extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \ + Q_PLUGIN_INSTANCE(PLUGINCLASS) \ + /**/ # define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ extern "C" Q_DECL_EXPORT \ QPluginMetaData qt_plugin_query_metadata() \ { return { qt_pluginMetaData_##PLUGINCLASSNAME, sizeof qt_pluginMetaData_##PLUGINCLASSNAME }; } \ - extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \ - Q_PLUGIN_INSTANCE(PLUGINCLASS) + QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, PLUGINCLASSNAME) +# define QT_MOC_EXPORT_PLUGIN_V2(PLUGINCLASS, MANGLEDNAME, MD) \ + extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QPluginMetaData) qt_plugin_query_metadata() \ + { static constexpr QT_PLUGIN_METADATAV2_SECTION QPluginMetaDataV2 md{}; return md; } \ + QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) #endif #define Q_EXPORT_PLUGIN(PLUGIN) \ diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index a21e44ba8fa..3e838818f98 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -1573,62 +1573,76 @@ void Generator::generatePluginMetaData() if (cdef->pluginData.iid.isEmpty()) return; - fprintf(out, "\nQT_PLUGIN_METADATA_SECTION\n" - "static constexpr unsigned char qt_pluginMetaData_%s[] = {\n" - " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n" - " // metadata version, Qt version, architectural requirements\n" - " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", - cdef->classname.constData()); + auto outputCborData = [this]() { + CborDevice dev(out); + CborEncoder enc; + cbor_encoder_init_writer(&enc, CborDevice::callback, &dev); + CborEncoder map; + cbor_encoder_create_map(&enc, &map, CborIndefiniteLength); - CborDevice dev(out); - CborEncoder enc; - cbor_encoder_init_writer(&enc, CborDevice::callback, &dev); + dev.nextItem("\"IID\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID)); + cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size()); - CborEncoder map; - cbor_encoder_create_map(&enc, &map, CborIndefiniteLength); + dev.nextItem("\"className\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName)); + cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size()); - dev.nextItem("\"IID\""); - cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID)); - cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size()); + QJsonObject o = cdef->pluginData.metaData.object(); + if (!o.isEmpty()) { + dev.nextItem("\"MetaData\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData)); + jsonObjectToCbor(&map, o); + } - dev.nextItem("\"className\""); - cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName)); - cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size()); + if (!cdef->pluginData.uri.isEmpty()) { + dev.nextItem("\"URI\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI)); + cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size()); + } - QJsonObject o = cdef->pluginData.metaData.object(); - if (!o.isEmpty()) { - dev.nextItem("\"MetaData\""); - cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData)); - jsonObjectToCbor(&map, o); - } + // Add -M args from the command line: + for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) { + const QJsonArray &a = it.value(); + QByteArray key = it.key().toUtf8(); + dev.nextItem(QByteArray("command-line \"" + key + "\"").constData()); + cbor_encode_text_string(&map, key.constData(), key.size()); + jsonArrayToCbor(&map, a); + } - if (!cdef->pluginData.uri.isEmpty()) { - dev.nextItem("\"URI\""); - cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI)); - cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size()); - } - - // Add -M args from the command line: - for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) { - const QJsonArray &a = it.value(); - QByteArray key = it.key().toUtf8(); - dev.nextItem(QByteArray("command-line \"" + key + "\"").constData()); - cbor_encode_text_string(&map, key.constData(), key.size()); - jsonArrayToCbor(&map, a); - } - - // Close the CBOR map manually - dev.nextItem(); - cbor_encoder_close_container(&enc, &map); - fputs("\n};\n", out); + // Close the CBOR map manually + dev.nextItem(); + cbor_encoder_close_container(&enc, &map); + }; // 'Use' all namespaces. int pos = cdef->qualified.indexOf("::"); for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) ) fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData()); - fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n", + + fputs("\n#ifdef QT_MOC_EXPORT_PLUGIN_V2", out); + + // Qt 6.3+ output + fprintf(out, "\nstatic constexpr unsigned char qt_pluginMetaDataV2_%s[] = {", + cdef->classname.constData()); + outputCborData(); + fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN_V2(%s, %s, qt_pluginMetaDataV2_%s)\n", + cdef->qualified.constData(), cdef->classname.constData(), cdef->classname.constData()); + + // compatibility with Qt 6.0-6.2 + fprintf(out, "#else\nQT_PLUGIN_METADATA_SECTION\n" + "static constexpr unsigned char qt_pluginMetaData_%s[] = {\n" + " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n" + " // metadata version, Qt version, architectural requirements\n" + " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", + cdef->classname.constData()); + outputCborData(); + fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN(%s, %s)\n" + "#endif // QT_MOC_EXPORT_PLUGIN_V2\n", cdef->qualified.constData(), cdef->classname.constData()); + + fputs("\n", out); } QT_WARNING_DISABLE_GCC("-Wunused-function") diff --git a/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp b/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp index 8b221dfc7a6..14be0082c7f 100644 --- a/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp +++ b/tests/auto/corelib/plugin/qplugin/tst_qplugin.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -138,11 +138,8 @@ void tst_QPlugin::scanInvalidPlugin_data() QTest::addColumn("errMsg"); // CBOR metadata - QByteArray cprefix = "QTMETADATA !1234"; - cprefix[12] = 0; // current version - cprefix[13] = QT_VERSION_MAJOR; - cprefix[14] = QT_VERSION_MINOR; - cprefix[15] = qPluginArchRequirements(); + static constexpr QPluginMetaData::MagicHeader header = {}; + QByteArray cprefix(reinterpret_cast(&header), sizeof(header)); QByteArray cborValid = [] { QCborMap m;