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 plugin keyword 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).

  • Lifetime Qt Champion


    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


  • Lifetime Qt Champion

    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

    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.

  • OK sorted:

    • Should just use QMAKE_MOC_OPTIONS += -Muri=MyPlugin in 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 URI on 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 plugin MyPlugin and classname MyPlugin lines works.

    So my "am I doing something wrong?" instinct was right.

  • Lifetime Qt Champion

    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 installed when 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.

  • Lifetime Qt Champion

    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.)

Log in to reply

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.