Different qmldir needed for static and dynamic plugins? Or am I doing something wrong?
I'm building an QQuickView-based app which uses a plugin for (currently) OSX and iOS. (And FWIW I use qmake and build scripts rather than Creator. On Qt 5.7 or later).
The plugin has both some C++ defined QML elements, and some .qml defined ones.
For the OSX build, I find my plugin's qmldir needs to look something like:
module MyPlugin plugin MyPlugin MyPluginThingQml 1.0 MyPluginThingQml.qml
but for iOS with its static-linked plugins I need to change it to
module MyPlugin classname MyPlugin MyPluginThingQml 1.0 MyPluginThingQml.qml
(I forget the runtime error if I keep on trying to use the version with
pluginkeyword in static builds... something about namespaces).
So that works nicely, but it's a bit of a pain to have to maintain two different qmldir with such a minor difference. Is this expected, or am I doing something wrong or missing something which would make things simpler? It kind of makes sense that the dynamic plugin metadata would want to reference a (dynamic) library with the compiled C++, while the static one wants to reference a class already compiled in... but (to me) it seems a shame the static/dynamic-ness of the build has to creep into the qmldir. So maybe there's a better way of doing things?
(Update: I actually just added
sed -i .tmp 's|classname MyPlugin|plugin MyPlugin|' MyPlugin/imports/MyPlugin/qmldir
to my build-for-OSX script, and vice-versa in my build-for-iOS script. I don't really consider that a good solution though).
Likely not the best answer but you could use the QQmlFileSelector class. You still have duplication but at least it's handled more transparently.
@SGaist Hmmm interesting; had never noticed that functionality before! However I consider duplication worse than my sed-based solution.
Although what might be quite nice would be QQmlFileSelector's syntax in the qmldir file. Then I could write
module MyPlugin +macx/plugin MyPlugin +ios/classname MyPlugin MyPluginThingQml 1.0 MyPluginThingQml.qml
In the absolute, not changing the syntax would be more logical.
Did you check the bug report system to see if there was something related ?
Haven't seen anything relevant in the Qt Jira.
One thing which increases my conviction that I'm doing something wrong somewhere:
If I look in an iOS build at one of Qt's own plugins, I see things like Release-iphoneos/MyPluginHost.app/qt_qml/QtQuick.2/qmldir containing:
module QtQuick plugin qtquick2plugin classname QtQuick2Plugin typeinfo plugins.qmltypes designersupported
Aha! I thought.... it looks like you can just give both plugin and classname, even though in the static build there are no lib files for the
plugin's argument to be referencing.
But then when I tried that myself (on iOS):
module MyPlugin plugin MyPlugin classname MyPlugin MyPluginThingQml 1.0 MyPluginThingQml.qml
in my own plugin's qmldir... at runtime it tells me
qrc:///main.qml:2:1: module "MyPlugin" plugin "MyPlugin" not found
and fails. A mystery to me why though. (Although I note that on OSX with dynamic lib plugin, the dual keywording is fine).
Update... aha; think I've cracked it. Combination of a couple of misunderstandings about some things (QTBUG-47827 and what you're supposed to specify for
QMAKE_MOC_OPTIONS += -Muri=...), more info later when confirmed.
Should just use
QMAKE_MOC_OPTIONS += -Muri=MyPluginin the plugin's .pro. At some point I'd picked up the idea the "uri" was supposed to be some longer thing like com.example.MyPlugin. Not specifying this at all results in a runtime error
static plugin for module "QtQuick" with name "MyPlugin" has no metadata URIon static platforms.
In static builds: don't include an explicit
qobject_cast<QQmlExtensionPlugin*>(qt_static_plugin_MyPlugin().instance())->registerTypes("MyPlugin");as well as a
Q_IMPORT_PLUGIN(MyPlugin). With the uri shortened, keeping this generates some message about a namespace clash; presumably before I was actually registering a separate com.example.MyPlugin and MyPlugin. Somehow I'd got into the habit of doing this from some info in QTBUG-28357 and/or QTBUG-47827... will go back and take a look at those and add some info.
Then the qmldir with both
classname MyPluginlines works.
So my "am I doing something wrong?" instinct was right.
Thanks for this excellent feedback ! :)
Arrgh... just when I thought stuff was working well.
On Android, it seems there is some strange difference between C++-only plugins and plugins with some QML.
A C++-only plugin, I can install the qmldir to
assets/imports/MyPlugin/qmldir, static link the plugin's .a lib... and that Just Works same as it does on iOS (can't remember if I needed to do an
addImportPath(...)on the engine or not).
But a plugin which also has some in-the-same-director QML referenced from its qmldir just gives me a
module "MyPlugin" is not installedwhen I try and import it. Despite the fact the app has no problem logging out the metadata if I include
qInfo() << qt_static_plugin_MyPlugin().metaData();
Tried Qt 5.7 and 5.9.1; same behaviour.
Googling around finds me some things like this mention at http://doc.qt.io/qt-5/deployment-android.html
ANDROID_EXTRA_PLUGINS: This variable can be used to specify different resources that your project has to bundle but cannot be delivered through the assets system, such as qml plugins.
which seems to imply that QML plugins don't work in assets for some reason. However I've no idea what to add to ANDROID_EXTRA_PLUGINS to get my own plugins to work. But have just seen https://bugreports.qt.io/browse/QTBUG-60022 .
Hmmm... am I even correct in assuming Android plugins should be static (as on iOS)? The bug referenced above mentions copying .so shared libs.
No, the iOS static build is (or rather was) a platform constraint. Apple has relaxed it but Qt is not yet available pre-built dynamically because it needed some more work to be as well managed as the static build.
@SGaist OK poking around with "normal" (non-static) plugins on Android and I've convinced myself androiddeployqt's code for handling ANDROID_EXTRA_PLUGINS is broken. More detail appended to https://bugreports.qt.io/browse/QTBUG-60022 but the short story is the androiddeployqt code is doing (in my case) QDir::filePath("MyPlugin/dummy.so") on a QDir with path "MyPlugin" and gets "MyPlugin/MyPlugin/dummy.so" (despite there being absolutely no MyPlugin/MyPlugin anywhere)... and at least the way I'm using androiddeployqt it'd make more sense if that was returning "MyPlugin/dummy.so").
Not entirely sure if there isn't something peculiar about how I'm using androiddeployqt though; maybe there are circumstances where that code would be correct if only was using some extra options everyone else just happens to use but me (although I'm guessing build-your-own-plugins is quite rare, and even rarer on mobile). Will see if the bug gets any attention, but given it was originally submitted against the documentation maybe I should submit a new one. (It's not clear to me whether the original reporter got anything to work, or gave up.)