Details
-
Suggestion
-
Resolution: Unresolved
-
P2: Important
-
None
Description
https://doc.qt.io/qt-6/qqmlengineextensionplugin.html#details says that, for a static plugin, we need to maintain a "synthetic volatile pointer" to the qml_register_types_<URI> function to prevent it from being optimized away by the linker. However, this alone is not sufficient in the scenario where the plugin and the executable are in separate CMake projects (see the attached example).
Code
MyModule/CMakeLists.txt
# ... set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../qml) qt6_add_qml_module(MyPlugin STATIC URI MyModule PLUGIN_TARGET MyPlugin NO_PLUGIN_OPTIONAL NO_GENERATE_PLUGIN_SOURCE CLASS_NAME MyWrongPluginClassName # <-- NOTE: qmldir classname is different from the real class name SOURCES mycustomqmlplugin.h QML_FILES MyItem.qml )
MyApp/CMakeLists.txt
# ... set(MY_QML_DIR ${CMAKE_SOURCE_DIR}/../qml) target_link_libraries(appMyApp PRIVATE Qt6::Quick ${MY_QML_DIR}/MyModule/libMyPlugin.a )
MyModule/mycustomplugin.h (adapted from https://doc.qt.io/qt-6/qtquick-imageprovider-example.html )
// ... //#define USE_UNDOCUMENTED_SYMBOL_KEEPERS <-- Uncomment this to make it work #ifdef USE_UNDOCUMENTED_SYMBOL_KEEPERS #include <QtCore/qtsymbolmacros.h> QT_DECLARE_EXTERN_SYMBOL_VOID(qml_register_types_MyModule) QT_DECLARE_EXTERN_RESOURCE(qmlcache_MyPlugin) QT_DECLARE_EXTERN_RESOURCE(qmake_MyModule) // Not strictly necessary? QT_DECLARE_EXTERN_RESOURCE(MyPlugin_raw_qml_0) #else extern void qml_register_types_MyModule(); #endif class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) public: void initializeEngine(QQmlEngine *engine, const char *uri) override { Q_UNUSED(uri); engine->addImageProvider("colors", new ColorImageProvider); #ifdef USE_UNDOCUMENTED_SYMBOL_KEEPERS QT_KEEP_SYMBOL(qml_register_types_MyModule) QT_KEEP_RESOURCE(qmlcache_MyPlugin) QT_KEEP_RESOURCE(qmake_MyModule) // Not strictly necessary? QT_KEEP_RESOURCE(MyPlugin_raw_qml_0) #else volatile auto registration = &qml_register_types_MyModule; Q_UNUSED(registration) #endif } };
MyApp/main.cpp
// ... #include <QQmlEngineExtensionPlugin> Q_IMPORT_QML_PLUGIN(ImageProviderExtensionPlugin) int main(int argc, char *argv[]) { // ... }
MyApp/Main.qml
import QtQuick import MyModule Window { width: 640 height: 480 visible: true Column { MyItem {} Image { source: "image://colors/yellow" } Image { source: "image://colors/red" } } }
Steps to test
- Load MyModule/CMakeLists.txt and build it
- Load MyApp/CMakeLists.txt, manually adjust the target_link_libraries() path if necessary, and build + run it
Bad Outcomes
- As-is, the project builds fine on Windows-MSVC/WASM/iOS but fails at runtime:
QQmlApplicationEngine failed to load component qrc:/qt/qml/MyApp/Main.qml:10:9: MyItem is not a type
Defining USE_UNDOCUMENTED_SYMBOL_KEEPERS and rebuilding the plugin will fix this problem.
- The plugin fails to link when building with GCC (Windows/Linux):
ld.exe: <ROOT>/MyApp/../qml/MyModule/libMyPlugin.a(mocs_compilation.cpp.obj):mocs_compilation.cpp:(.text$_ZN18ColorImageProviderD1Ev[_ZN18ColorImageProviderD1Ev]+0xd): undefined reference to `__imp__ZN19QQuickImageProviderD2Ev'
Other observations
- If both the plugin and the executable are part of the same CMake project, then CMake seems to do some extra magic:
- Preserving qml_register_types_<URI> is enough. The other symbols aren't needed.
- Q_IMPORT_QML_PLUGIN() is not needed.
- The correct CLASS_NAME is needed (in contrast, the wrong class name in the attached example seems to be inconsequential).
- qmake_MyModule does not seem to be needed in this example, but our auto-generated plugin code contains it. Why?
Suggestions
- Document the QT_DECLARE_EXTERN_SYMBOL_X() and QT_KEEP_X() macros
- Bonus: Provide convenience macros that simply take the dotted-URI and target name as inputs?
- Expand https://doc.qt.io/qt-6/qqmlengineextensionplugin.html#details to discuss the other symbols/resources and the new macros
- Expland https://doc.qt.io/qt-6/qtquick-imageprovider-example.html to:
- Include the bits needed for using it as a static plugin (note that some platforms, like iOS and WASM, require static plugins)
- Include a complete executable project to consume the plugin
- Provide a mechanism (or documentation, if it's already possible) to make the example work with GCC
Attachments
Issue Links
- relates to
-
QTBUG-94592 Reconsider image provider setup and ownership
-
- Reported
-