Yves
Yves

Reputation: 187

Custom QML module deployment to Android: QML dependencies missing

Im developing a custom QML module (let's call it MyModule) containing some special types. It is used as precompiled library on other application projects (i.e. the source code is not available to them, it's used via import MyModule 1.0, setting the necessary import paths etc.). The module contains C++-bases QML types as well as QML files that themselves import QtQuick modules like QtQuick.Controls and QtQuick.Window.

When I try to deploy and execute an application developed using MyModule to Android, the dependencies of MyModule are not picked up by androiddeployqt/qmlimportscanner and included in the APK, which results in messages like: module "QtQuick.Controls" is not installed

Is there a way to have these QML dependencies included in the APK without having to create a dummy qml file containing all imports in the applications source directory, so they can be picked up by qmlimportscanner?

Upvotes: 3

Views: 1627

Answers (2)

tanius
tanius

Reputation: 16849

You can place your module MyModule anywhere below the base directory of the Android application that uses MyModule. For example into a folder ./my-module or ./lib/my-module.

How it works

While running qmake / CMake, qmlimportscanner will be run on the folder containing the project definition file. For qmake based projects that's the .pro file and for CMake based projects that's the CMakeLists.txt file. It will look for all *.qml files in this directory and any sub-directories and include their QML dependencies into the Android APK package. At least that is the behavior observed for qmlimportscanner.

You can still manage your QML module MyModule and your Android application in different Git repositories. Just make sure to .gitignore the contents of the ./lib/ folder in your Android application. (You can of course also use more complex approaches like Git submodules.)

 

Alternative Solutions

Here is a list of alternative solutions that I found around the web. Either they did not work for my case or have some disadvantage, but might be useful for your situation.

1. Create a dummy QML file with imports

As also mentioned in the question, this is the usual workaround:

The workaround consists in declaring a resource file on the project dir with a dummy qml file including the necessary imports.

Some more details with an example, from here:

I created a simple .qml file in the same directory as the .pro file. Then I only added the import lines that are used within all my .qml files in other folders. Since this lead to a syntax complaint I also added the text Page {} after the last import line.

import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.4
# [...]

Page { }

Note that this file is not referenced in the .pro file and is of course not part of the project / application at all. It only exists for the purpose that qmlimportscanner includes the correct modules.

2. Use Qt 5.14 and list QML files in .qrc files

Sine Qt 5.14, this is the official resolution of the issue, which was tracked as QTBUG-55259. See there for details. Now, qmlimportscanner will consider all QML files listed in your project's .qrc files as available for import statements. So if your project says import MyModule.Something in a QML file, it will work, as that import can now be resolved.

However, if your module MyModule resides outside of the project's source tree and itself imports a QML module ExternalModule that is not imported by your Android application, that import would still fail. (I hope I got this point right, since I did not test it myself.)

3. Symlink your module's directory from the project source tree

Somebody reported in late 2018 that qmlimportscanner will follow symlinks from the project's source tree to external trees when looking for QML files that contain import statements.

I tried this, but could not confirm that behavior.

4. Call qmlimportscanner with multiple paths

qmlimportscanner is capable of considering multiple root paths (where to scan for QML files that import other QML files) and import paths (where to resolve these imports), like this:

qmlimportscanner -rootPath dir1 -rootPath dir2 -importPath dir3 -importPath dir4

However, this capability is currently not used by androiddeployqt as per QTBUG-75187. So you could only use it if you use an alternative build system for Android anyway. The ECM build system also calls androiddeployqt internally though. Perhaps qt-android-cmake could help.

5. Call qml_import_qml_plugins() from qmake / CMake

Starting with Qt 5.14, you can call qmlimportscanner from your qmake / CMake files. It's only useful for static Qt builds, though:

Runs qmlimportscanner at configure time to find the static QML plugins used and links them to the given target.

Note: When used with a non-static Qt build, this function does nothing.

(source)

At first glance, there does not seem to be a way to hand it multiple locations to look for QML dependencies, so it might not improve anything compared to the current situation. But I did not explore this in detail yet.

Upvotes: 1

Mitch
Mitch

Reputation: 24416

You can try adding the depends keyword to your qmldir file:

Declares that this module depends on another.

Example:

depends MyOtherModule 1.0

This declaration is necessary only in cases when the dependency is hidden: for example, when the C++ code for one module is used to load QML (perhaps conditionally) which then depends on other modules. In such cases, the depends declaration is necessary to include the other modules in application packages.

For some inspiration, check out this one:

http://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/src/imports/controls/qmldir

Upvotes: 4

Related Questions