How to know, whether our Qt 5 C++ projects make use of dynamically-loaded plug-ins.

I have used Qt Creator, with Qt version 5.7.1, to create some simplistic GUI applications as exercises. And then, I used the tool named ‘linuxdeployqt’, in order to bundle those (compiled) applications into AppImage’s, which should, in principle, run on other users’ Linux computers. (:4)  But, when using these tools, a question arose in my head, which I was also able to deduce the answer to quickly, in the form of, ‘Why does linuxdeployqt exist separately from linuxdeploy? Why does the developer need a tool which bundles library files, but which exists separately, just for C++ Qt applications? Obviously, some AppImages are not even based on that GUI library.’

And the short answer to that question is, ‘Because unlike some other application frameworks, Qt is based heavily on plug-ins, which none of my simpler exercises required explicitly.’ And, what’s so special about plug-ins? Aside from the fact that they extend the features of the application framework, plug-ins have as special property, that the application can decide to load them at run-time, and not with static awareness, at build-time. What this means is that a tool such as ‘linuxdeploy’ will scan the executable, which has been built by whichever compiler and/or IDE the developer used, will find that this executable is linked to certain shared libraries (with static awareness), but will not recognize some of the libraries which that executable needs to run, just because those are plug-ins, which the application will only decide to load at run-time.

Hence, to get the full benefit of using ‘linuxdeployqt’, the developer ‘wants to’ end up in a situation similar to the situation described Here. Granted, the person in question had run in to a bug, but his usage of the feature was correct.

This usage differs from my earlier usage, in that I never fed any ‘extra-plugins’ list to ‘linuxdeployqt’, yet, when I used the tool for creating the AppImage, my (project’s) plug-ins folder was populated with certain libraries, that were also plug-ins. And so, a legitimate question which an aspiring developer could ask would be, ‘How do I know, whether my Qt application loads plug-ins dynamically at run-time, so that I’ll know, whether I also need to specify those when bundling my AppImage?’ After all, it would seem that in certain cases, the plug-ins are in fact loaded with static awareness, which means that ‘linuxdeployqt’ can just recognize that the application is loading them, without the developer having had to make any special declaration of the fact.

One possible answer to that question might be ‘Test the AppImage, and see if it runs.’ But one problem with that answer would be, that if the executable cannot find certain plug-ins as bundled with the AppImage, the danger exists, that it may find those on the Host Machine, and that the application will end up running on some hosts, but not on other hosts, depending on what version of Qt the recipient has installed, and, depending on which plug-ins that recipient also has installed. And so, a better way must exist, for the developer to know the answer to this question.

(Updated 4/05/2021, 8h55… )

(As of 4/04/2021, 13h45… )

AFAICT, one possible answer lies, in whether the developer used this C++ object anywhere in his application. This is the plug-in loader, with which Qt applications are supposed to load Qt plug-ins specifically, which implement the interface ‘QObject’, and do so at run-time. (:1)  If the reader’s application makes such explicit function-calls, then he or she must also tell ‘linuxdeployqt’ about the plug-ins specifically loaded in this way. Long story short, if you wrote code that loads plug-ins dynamically at run-time, you will know that you did so. (:2)  Otherwise, the built-in ability of ‘linuxdeployqt’ to sniff the Qt plug-ins specifically, which your application uses with static awareness, should really be all you need. (:5)

I can go one step further. The version of ‘linuxdeployqt’ which I was using, had an optional flag, named ‘-appimage’. If this flag was not set, then the tool would go further than just, not to output an AppImage. It would then ‘output’ a directory structure which is FileSystem Hierarchy Standards compliant (more or less), which the other usage of the tool did not do, and then, the use of an additional tool, such as maybe ‘linuxdeploy’ is needed, or just, of ‘appimagetool’, to bundle the file-system into an actual AppImage. What the aspiring developer should expect is that, not only did this use of ‘linuxdeployqt’ create a directory-structure which is FHS-compliant, but, IF the parameter ‘-bundle-non-qt-libs’ was explicitly set – which does not need to be set if ‘-appimage’ was set – ‘linuxdeployqt’ nevertheless used its tricks to sniff plug-ins that were loaded by the Qt application statically, so that afterwards, to use ‘linuxdeploy’ should result in an AppImage which is still complete, with statically-known Qt plug-ins.

However, I dimly remember that there were some more issues that needed to be sorted out, when I used ‘linuxdeploy’ on a directory structure, which was first populated with ‘linuxdeployqt’. This is an optional thing to do, because there is no strict rule I’d be aware of, stating that AppImages bearing Qt applications, need to be FHS-compliant. (:3)


 

(Update 4/04/2021, 14h15: )

1:)

A slightly better explanation of how, actually, to work with dynamically-loaded plugins in Qt reveals, that even though the ”QPluginLoader::instance()’ method returns a pointer to an object of type ‘QObject’, in order for the returned plug-in pointer actually to be useful, it must implement custom pure virtual functions, and not only virtual functions part of ‘QObject’. For that reason, a developer who is actually working with plug-ins should define his or her own interface, and then cast the pointer returned by the method above, to a pointer to that interface. At that point, the developer can define his or her own methods, that he or she wishes the plug-in to implement.

To do that, one actually uses the template-function ‘qobject_cast<>()‘.


 

(Update 4/04/2021, 19h25: )

2:)

I suppose then, that my reader could still be left wondering (as I am), why the poster on the BB above needed to load ‘libqsvg.so’ and ‘libqsvgicon.so’ explicitly. One reason might be, the possibility of using Qt classes such as This one. What’s to observe about this class is that, in the .PRO (-Project) File, the use of this class requires that:

QT += svg

Be declared. In other words, IF my reader’s Qt project requires that this declaration include anything beyond ‘core’ and ‘gui’, then in each case, specific research needs to be carried out, before bundling the compiled, working application into an AppImage, using ‘linuxdeployqt’.

However, my project file-system’s plug-ins subdirectory ended up being populated with the plug-in ‘imageformats/libqsvg.so’ automatically, without my having to instruct ‘linuxdeployqt’ to do so. So, this specific question really remains unanswered.


 

(Update 4/04/2021, 19h35: )

3:)

IIRC, The special issue which I needed to resolve, when creating the FHS-compliant version of ‘Creator_Test6′ was, that by itself, my version of ‘linuxdeployqt’ was not able to build a(n empty) skeleton file-system, into which it would deploy the Qt-application. Instead, what I needed to do was, to use the ‘CMakeLists.txt‘ script which I had manually created, but to custom-recompile my own project, with a local directive to install the application into Prefix:

/home/dirk/Programs/ld_test/usr

Without actually needing to become root, I could then give the command ‘make install‘.

As expected, ‘bin/‘, ‘lib/‘, and ‘share/‘ subdirectories appeared there, courtesy of CMake, and the Install Manifest indicated that exactly one file had been installed: The executable !  Hence, at that point in time, zero libraries or plug-ins were installed (to the FileSystem Hierarchy skeleton). But then, I was able to use ‘linuxdeployqt‘, without-appimage‘, but with-bundle-non-qt-libs‘, still leaving me to my own devices, actually to build the AppImage. And this command needs to be given from whichever directory, ‘usr/‘ is a subdirectory of. It resulted in the additional subdirectories ‘usr/plugins‘ and ‘usr/translations‘ being generated and linked to.


 

(Update 4/04/2021, 16h55: )

I suppose that one more question which my reader could ask might be, ‘So I can see that linuxdeployqt bundled the required plug-ins into a specific directory, before the actual AppImage was built. How can I know, that the executable will load them from that directory, once the AppImage has been compressed, and not, as was previously stated, from the recipient’s host directories?’

And my answer to that is, that I’ve actually run a sampling of my AppImages, including the FHS-compliant one, on my Linux-enabled Chromebook, that has an Intel/AMD-x86_64-compatible chipset. They all run, yet it’s to be expected, that ChromeOS has no inherent usage of Qt… And, it’s written that if the Qt Libraries version which a project was compiled with was, say, 5.7.1, then the executable will refuse to load any plug-ins with a higher version number.


 

(Update 4/04/2021, 20h25: )

4:)

I suppose that, in order better to understand this entire posting, there is a fact which the reader should also know about AppImages, but which some readers might not know. An AppImage is a disk-image, which in the case of modern examples is formatted with the ‘SquashFS’ File System. One of the things Linux computers love to do is, to mount disk images, without requiring that those be burned or written onto physical disks first. Even under Linux, it’s more the norm that users need root privileges to do so, but AppImages just happen to be an exception, which any user may run, and therefore mount first.

Therefore, if a developer is writing a Qt application, with the target of deploying it as an AppImage, then one stage in the development process is, that the tool used to do so find the libraries the application depends on, bundle those into a small, local file system, and then compress the entire file-system, including the actual application, into the AppImage.

While this is really a separate stage in the development of an application, one need the developer is keenly aware of is, of his or her eventual AppImage to be self-sufficient in supplying those libraries, to whatever extent is feasible. Therefore, any tool which is meant to create AppImages, but which fails to recognize that certain libraries need to be bundled, represents a special problem.

 


 

(Update 4/05/2021, 8h55: )

5:)

Actually, if the reader did in fact write custom Qt plug-ins, say, for some large project, I’ve learned that the correct way to include them in an AppImage, is not even with the ‘-extra-plugins=‘ argument, but rather, using the ‘-executable=‘ argument, as explained Here.

Dirk

 

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>