As far as I can tell, Qt requires each plugin to provide a function qt_plugin_instance that returns some required metadata. (As well as some other adjacent machinery.)
Normally, C++ code would provide it by calling the QT_MOC_EXPORT_PLUGIN_V2 macro (the wiki provides some additional context, but it's also pretty outdated; Q_EXPORT_PLUGIN2 doesn't exist anymore), but we obviously can't do that from Rust.
Instead, cxx_qt_build::CxxQtBuild seems to be responsible for generating that stub if it's constructed via new_qml_module (via moc).
Given this repro (and building it with cargo build), I can confirm that it does write the call:
$ rg -uuu QT_MOC_EXPORT_PLUGIN_V2
target/debug/build/cxx-qt-plugin-16d11c48e807be72/out/moc/moc_mymodule_plugin.cpp.cpp
91:#ifdef QT_MOC_EXPORT_PLUGIN_V2
111:QT_MOC_EXPORT_PLUGIN_V2(mymodule_plugin, mymodule_plugin, qt_pluginMetaDataV2_mymodule_plugin)
137:#endif // QT_MOC_EXPORT_PLUGIN_V2and that it does get linked in:
$ nm -gC target/debug/build/cxx-qt-plugin-16d11c48e807be72/out/libcxx-qt-plugin-cxxqt-generated.a | grep qt_plugin_instance
0000000000000000 T qt_plugin_instanceHowever, it doesn't seem to get reexported when linked into the final .so, and so it ends up dropped by the tree shaker:
$ nm -C target/debug/libcxx_qt_plugin.so | grep qt_plugin_instance
[a whole lot of nothing]Which prevents Qt from picking up the plugin.
(Yes, I realize that there's more to be done in terms of actually helping Qt find the plugin, but that doesn't help if it refuses to load it anyway.)
Activity
30So as a workaround, it seems to work if you add a linker version script that force-exports the symbol. Something like:
// build.rs
use cxx_qt_build::{CxxQtBuilder, QmlModule};
fn main() {
CxxQtBuilder::new_qml_module(
QmlModule::new("org.kde.simplemdviewer").plugin_type(cxx_qt_build::PluginType::Dynamic),
)
.file("src/lib.rs")
.build();
// NEW BELOW:
println!("cargo::rustc-link-arg-cdylib=-Wl,--version-script=qt-plugin.version");
}# qt-plugin.version:
QT_PLUGIN_META {
global:
qt_plugin_instance;
};That gets me far enough to let my QML import the module.
So as a workaround, it seems to work if you add a linker version script that force-exports the symbol. Something like:
// build.rs
use cxx_qt_build::{CxxQtBuilder, QmlModule};
fn main() {
CxxQtBuilder::new_qml_module(
QmlModule::new("org.kde.simplemdviewer").plugin_type(cxx_qt_build::PluginType::Dynamic),
)
.file("src/lib.rs")
.build();
// NEW BELOW:
println!("cargo::rustc-link-arg-cdylib=-Wl,--version-script=qt-plugin.version");
}# qt-plugin.version:
QT_PLUGIN_META {
global:
qt_plugin_instance;
};That gets me far enough to let my QML import the module.
So as a workaround, it seems to work if you add a linker version script that force-exports the symbol. Something like:
// build.rs
use cxx_qt_build::{CxxQtBuilder, QmlModule};
fn main() {
CxxQtBuilder::new_qml_module(
QmlModule::new("org.kde.simplemdviewer").plugin_type(cxx_qt_build::PluginType::Dynamic),
)
.file("src/lib.rs")
.build();
// NEW BELOW:
println!("cargo::rustc-link-arg-cdylib=-Wl,--version-script=qt-plugin.version");
}# qt-plugin.version:
QT_PLUGIN_META {
global:
qt_plugin_instance;
};That gets me far enough to let my QML import the module.
That gets me far enough to let my QML import the module.
i get this error after your workaround:
Failed to extract plugin meta data from 'path/to/Plugin/libPlugin.so': 'path/to/Plugin/libPlugin.so' is not a Qt plugin (metadata not found)is this what you get too?
That gets me far enough to let my QML import the module.
i get this error after your workaround:
Failed to extract plugin meta data from 'path/to/Plugin/libPlugin.so': 'path/to/Plugin/libPlugin.so' is not a Qt plugin (metadata not found)is this what you get too?
That gets me far enough to let my QML import the module.
i get this error after your workaround:
Failed to extract plugin meta data from 'path/to/Plugin/libPlugin.so': 'path/to/Plugin/libPlugin.so' is not a Qt plugin (metadata not found)is this what you get too?
That gets me far enough to let my QML import the module.
i get this error after your workaround:
Failed to extract plugin meta data from 'path/to/Plugin/libPlugin.so': 'path/to/Plugin/libPlugin.so' is not a Qt plugin (metadata not found)is this what you get too?
I don't think I saw that error at all, no.
Looking at the QT plugin loader, it seems to be because your ELF is missing the .qtmetadata section, but I'm honestly kind of stumped on where that comes from. I don't seem to have it either, but I do have a section named .note.qt.metadata which only appears if I've called QmlModule::plugin_type(cxx_qt_build::PluginType::Dynamic) in my build.rs. If it helps, you could try comparing against my project (https://codeberg.org/natkr/lampknapp). I'm not making any claims about it being well-written, but it does at least work (on my machine™ :p).
I don't think I saw that error at all, no.
Looking at the QT plugin loader, it seems to be because your ELF is missing the .qtmetadata section, but I'm honestly kind of stumped on where that comes from. I don't seem to have it either, but I do have a section named .note.qt.metadata which only appears if I've called QmlModule::plugin_type(cxx_qt_build::PluginType::Dynamic) in my build.rs. If it helps, you could try comparing against my project (https://codeberg.org/natkr/lampknapp). I'm not making any claims about it being well-written, but it does at least work (on my machine™ :p).
@ahayzen-kdab for me i want to import cxx-qt qml modules from Quickshell and i dont have control of the main.cpp
@ahayzen-kdab for me i want to import cxx-qt qml modules from Quickshell and i dont have control of the main.cpp
@ahayzen-kdab for me i want to import cxx-qt qml modules from Quickshell and i dont have control of the main.cpp
@ahayzen-kdab for me i want to import cxx-qt qml modules from Quickshell and i dont have control of the main.cpp
@ahayzen-kdab I was working on a KDE plasmoid (widgets for the KDE/Plasma desktop, see the Lampknapp repo I linked above). That means that I provide the QML but have no control over the host application (plasmashell), and QML modules are (as far as I can tell) the suggested way to provide native backing code. I ended up roughly following https://develop.kde.org/docs/plasma/widget/c-api/ and https://develop.kde.org/docs/plasma/widget/porting_kf6/ (since the C++ interop tutorial is still targeted at KDE 5).
@ahayzen-kdab I was working on a KDE plasmoid (widgets for the KDE/Plasma desktop, see the Lampknapp repo I linked above). That means that I provide the QML but have no control over the host application (plasmashell), and QML modules are (as far as I can tell) the suggested way to provide native backing code. I ended up roughly following https://develop.kde.org/docs/plasma/widget/c-api/ and https://develop.kde.org/docs/plasma/widget/porting_kf6/ (since the C++ interop tutorial is still targeted at KDE 5).
@ahayzen-kdab I was working on a KDE plasmoid (widgets for the KDE/Plasma desktop, see the Lampknapp repo I linked above). That means that I provide the QML but have no control over the host application (plasmashell), and QML modules are (as far as I can tell) the suggested way to provide native backing code. I ended up roughly following https://develop.kde.org/docs/plasma/widget/c-api/ and https://develop.kde.org/docs/plasma/widget/porting_kf6/ (since the C++ interop tutorial is still targeted at KDE 5).
@ahayzen-kdab I was working on a KDE plasmoid (widgets for the KDE/Plasma desktop, see the Lampknapp repo I linked above). That means that I provide the QML but have no control over the host application (plasmashell), and QML modules are (as far as I can tell) the suggested way to provide native backing code. I ended up roughly following https://develop.kde.org/docs/plasma/widget/c-api/ and https://develop.kde.org/docs/plasma/widget/porting_kf6/ (since the C++ interop tutorial is still targeted at KDE 5).
Right, some of those examples are for Qt 5, as you say, using a QQmlExtensionPlugin etc. The QML modules that are supported in CXX-Qt are the new ones for Qt 6 https://doc.qt.io/qt-6/qtqml-modules-topic.html and https://doc.qt.io/qt-6/qtqml-writing-a-module.html These are then supported by tooling like qmllint and qmlls etc.
These are what you add via qt_add_qml_module in cmake, as long as the resultant .so and qmldir are within your QML import paths then these will be automatically loaded when you do import mymodule and this does not need control over the main.cpp.
But we haven't explicitly looked at Qt plugin support or documented how that works yet, so for now you either need to move to the new QML modules route or use workarounds like above.
Right, some of those examples are for Qt 5, as you say, using a QQmlExtensionPlugin etc. The QML modules that are supported in CXX-Qt are the new ones for Qt 6 https://doc.qt.io/qt-6/qtqml-modules-topic.html and https://doc.qt.io/qt-6/qtqml-writing-a-module.html These are then supported by tooling like qmllint and qmlls etc.
These are what you add via qt_add_qml_module in cmake, as long as the resultant .so and qmldir are within your QML import paths then these will be automatically loaded when you do import mymodule and this does not need control over the main.cpp.
But we haven't explicitly looked at Qt plugin support or documented how that works yet, so for now you either need to move to the new QML modules route or use workarounds like above.
So as a workaround, it seems to work if you add a linker version script that force-exports the symbol. Something like:
That gets me far enough to let my QML import the module.