[Interest] Extracting available imports from QML plugins for package dependencies

Fabian Vogt fabian at ritter-vogt.de
Fri Mar 26 13:58:35 CET 2021


Hi,

I'm one of the Qt+KDE package maintainers for openSUSE. For making sure that
all shipped .qml files have their imports available, during package build we
generate a list of provided and required capabilities for packages including
.qml and qmldir files. Those are used by RPM and package management tools for
dependency resolution. In practice it looks like this:

qtgraphicaleffects 5.15 provides qt5qmlimport(QtGraphicalEffects.1) = 15
qtquickcontrols2 5.15 requires qt5qmlimport(QtGraphicalEffects.1) >= 12	

So simply because a .qml file in the qtquickcontrols2 package contains
"import QtGraphicalEffects 1.12", it automatically pulls in qtgraphicaleffects
in the right version.

[1] has a more in-depth explanation on how the capabilities are designed and
implemented.

Generating the list of required QML imports is easy: For each .qml file just
run qmlimportscanner and put it into a format which RPM understands. The
biggest hurdle here is to decide which Qt version a .qml file will be used with
during runtime, as this decides for instance whether qtquickcontrols2 from Qt 5
or Qt 6 is needed.

Finding out what a QML module provides is much harder. The task is basically:
Given a qmldir file, find out which import statements it can satisfy (including
version). This is pretty much the QML engine's import resolution in reverse.
For simple qmldir files which just reference components written in QML, the
information is directly available: For each major version, get the highest
minor version.

Once plugins are involved, it gets more complex. The version information is
now contained in code with arbitrary logic, so the only way to get to it is by
loading the plugin and executing the registration code. qmlplugindump works
like that, but it's unfortunately not suitable for this. It needs both the
URI and major.minor versions, but the task is to find out the available versions
in the first place.

Additionally, the information it dumps lacks some important aspects of
registration, like for instance the versions passed to qmlRegisterModule, which
can bump the maximum available minor version without registering a type for it.
This is for instance how qtquickcontrols2 5.14 achieves that it can be imported
as 5.14, even if no new features were added for that minor version bump.
Without being aware of such calls, the generated capability would only express
"qt5qmlimport(QtQuick.Controls.2) >= 13".

To get around the restrictions of qmlplugindump, I wrote a custom program [2]
which uses the private API (yuck) to load a plugin and get all available URIs
and versions. It also catches qmlRegisterModule manually.

Is there already a better way to do this? If not, could it maybe be added to
qmlplugindump?

This mechanism is used for package builds in openSUSE Tumbleweed for a few
months now and thus pretty much left the PoC phase. So I'd like to get at least
some parts of this upstream, if possible.

[1] Documentation of how the capabilities work:
https://build.opensuse.org/package/view_file/openSUSE:Factory/qml-autoreqprov/README?expand=1

[2] Current program used to dump available exports:
https://build.opensuse.org/package/view_file/openSUSE:Factory/qmlpluginexports/qmlpluginexports.cpp?expand=1

Cheers,
Fabian




More information about the Interest mailing list