Installation Qt6 Qml Application Cross Plattform

Hi,

for my Qt6 Qml Application I want to create installers for all major platforms (Linux, Mac Os, Windows).

I do already have a working cmake files which handles the non Qt6 part of my application. Additionally I also managed to deploy an application, which uses the core part of Qt6 (such as QStandardPaths) for instance.

It looks like this:

// Install some Runtime Dependencies
install(TARGETS BeansClient COMPONENT Beans 
  RUNTIME_DEPENDENCIES
    PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
    POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
    DIRECTORIES $<TARGET_FILE_DIR:Qt6::Core>
    FRAMEWORK DESTINATION Beans.app/Contents/Frameworks
)

// Install shared libraries of manually listed dependencies
foreach(lib IN ITEMS CLI11::CLI11 sqlpp11::sqlite3 spdlog::spdlog)
  get_target_property(target_type ${lib} TYPE) 
  if (target_type STREQUAL SHARED_LIBRARY)
    get_target_property(real_lib ${lib} ALIASED_TARGET)
    install(TARGETS ${real_lib} COMPONENT Beans)
  endif ()
endforeach()

But yeah for Qt6 with GUI and qml in particular everythings seems to be more involved. Simply adding for instance

DIRECTORIES $<TARGET_FILE_DIR:Qt6::Core> $<TARGET_FILE_DIR:Qt6::Quick>

helps a little bit. But is not enough. Currently I get the following error message on startup of my app:

qt.core.plugin.factoryloader: checking directory path "/opt/BeansClient 0.3.0/bin/platforms" ...
qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

fish: Job 1, '"/opt/BeansClient 0.3.0/bin/Bea…' terminated by signal SIGABRT (Abort)

A recent blog entry (Qt's CMake deployment API) already suggest that depoying Qt application and QML applications in particular is a little bit more involved. Maybe @jobor can help here, as you are the author of this article and seem to be very involved in the project. Hope it’s okay that I tag you!

So from my understanding for Windows / Mac Os something like

qt_generate_deploy_qml_app_script(TARGET BeansClient FILENAME_VARIABLE deploy_script)
install(SCRIPT ${deploy_script}) 

would work but well it’s only working on Windows and Mac Os. So I still need a solution for Linux. How can I currently do it on Linux?

And I also wonder what are the general steps making this works / what makes Qt6 difficult to deploy. The first step getting the shared libs installed did seem quite easy:

// Install some Runtime Dependencies
install(TARGETS BeansClient COMPONENT Beans 
  RUNTIME_DEPENDENCIES
    PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
    POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
    DIRECTORIES $<TARGET_FILE_DIR:Qt6::Core> $<TARGET_FILE_DIR:Qt6::Quick>
    FRAMEWORK DESTINATION Beans.app/Contents/Frameworks
)

This install stuff like libQt6QmlModels.so.6 libQt6QmlModels.so.6 libQt6QmlModels.so.6 …

From the blog post I got that qml files need to be installed as well. Although I don’t quite understand why. Aren’t qml files compiles in Qt6 in the application?

What else needs to be done? Some Input would be great.

Do you have a timeline when Qt6 deployment will work out of the box for Linux as well? So I can see if it’s even worth the effort to come up with my own solution right now.

The xcb error you’re seeing is that the Qt installation is missing its plugins. I don’t know of a reliable way to get these without using the DeployQt module that understands these things. In ParaView, I’ve just hard-coded the set of plugins that we need and install them specifically.

Cc: @craig.scott

You mean something like libqxco.so, which is expected to be in a plugins subfolder according to Qt for Linux/X11 - Deployment | Qt 6.4?

Can you link some Code of ParaView where you did copy them?

I suspect that it’s not all that directly useful to you because of abstraction differences, but here:

Coincidentally, I was about to blog about this: Deploying to Linux with CMake

tl;dr: With Qt 6.5 the Qt CMake deployment API works for Linux too.

1 Like

@jobor Wow that’s amazing! I directly tested it out with the released beta version.

On Windows everything is working perfectly. On Linux it does a few things that look right like copying the shared libs and the QML modules. But what is still missing is the plugins folder. According to the blog post it should do this:

-- Installing: /tmp/my-application/plugins/egldeviceintegrations/libqeglfs-emu-integrat...
-- Installing: /tmp/my-application/plugins/egldeviceintegrations/libqeglfs-kms-egldevic...
-- Installing: /tmp/my-application/plugins/egldeviceintegrations/libqeglfs-x11-integrat...
-- Installing: /tmp/my-application/plugins/imageformats/libqgif.so
-- Installing: /tmp/my-application/plugins/imageformats/libqico.so
-- Installing: /tmp/my-application/plugins/imageformats/libqjpeg.so
-- Installing: /tmp/my-application/plugins/xcbglintegrations/libqxcb-egl-integration.so
-- Installing: /tmp/my-application/plugins/xcbglintegrations/libqxcb-glx-integration.so
-- Installing: /tmp/my-application/plugins/platforms/libqxcb.so 

Everything else is available from what I can see. I do have a qml application, so I use:

qt_generate_deploy_qml_app_script(TARGET BeansClient FILENAME_VARIABLE deploy_script)
install(SCRIPT ${deploy_script} COMPONENT Beans)

And then I install this Component either with cmake install or with cpack and an installer.

Here is how it looks with QtIFW installed. No plugins folder as you can see.

I tried manually copying the patforms folder from my Qt Installation and got the following error:

qt.core.library: "/opt/BeansClient 0.3.0/bin/platforms/libqxcb.so" cannot load: Cannot load library /opt/BeansClient 0.3.0/bin/platforms/libqxcb.so: (/usr/lib/libQt6XcbQpa.so.6: undefined symbol: _ZN11QBasicMutex15destroyInternalEP13QMutexPrivate, version Qt_6)
qt.core.plugin.loader: QLibraryPrivate::loadPlugin failed on "/opt/BeansClient 0.3.0/bin/platforms/libqxcb.so" : "Cannot load library /opt/BeansClient 0.3.0/bin/platforms/libqxcb.so: (/usr/lib/libQt6XcbQpa.so.6: undefined symbol: _ZN11QBasicMutex15destroyInternalEP13QMutexPrivate, version Qt_6)"
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.

Edit: I do have Qt6 on my system installed as well. Maybe the error message here is because of the version mismatch. Because /usr/lib/libQt6XcbQpa.so.6 is my system Qt. But it should actually use the one in the self contained install folder, which itself should then be the Qt Version used in find_package.

Maybe there is also an issue then with:

install(TARGETS BeansClient COMPONENT Beans 
  RUNTIME_DEPENDENCIES
    PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
    POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
    FRAMEWORK DESTINATION Beans.app/Contents/Frameworks
) 

This will find the System QT Libs as well I think, because they are in a standard linux path. Is there a conflict here? How to deal with it?

My installed QT Version is in some non standard path and I use Qt6_ROOT on the CLI to make cmake find it. ´

I reused the other minimal reproducible project and added a sample deployment here.

My Commands

cmake --preset="default"  -DQt6_ROOT="/home/leon/dev/Qt/6.5.0/gcc_64/" 
cmake --build --preset="default"
sudo cmake --install "build" --config Release

test.zip (2.4 MB)

I just tried with 6.5.0 beta1 and test.zip, and plugins are deployed:

-- Installing: /tmp/test-install/bin/apptest
...
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_messages.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_local.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_server.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_debugger.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_inspector.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_native.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_nativedebugger.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_preview.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_profiler.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_quickprofiler.so
-- Installing: /tmp/test-install/plugins/qmltooling/libqmldbg_tcp.so
-- Installing: /tmp/test-install/plugins/networkinformation/libqglib.so
-- Installing: /tmp/test-install/plugins/networkinformation/libqnetworkmanager.so
-- Installing: /tmp/test-install/plugins/tls/libqopensslbackend.so
-- Installing: /tmp/test-install/plugins/egldeviceintegrations/libqeglfs-emu-integration.so
-- Installing: /tmp/test-install/plugins/egldeviceintegrations/libqeglfs-kms-egldevice-integration.so
-- Installing: /tmp/test-install/plugins/egldeviceintegrations/libqeglfs-x11-integration.so
-- Installing: /tmp/test-install/plugins/imageformats/libqgif.so
-- Installing: /tmp/test-install/plugins/imageformats/libqico.so
-- Installing: /tmp/test-install/plugins/imageformats/libqjpeg.so
-- Installing: /tmp/test-install/plugins/iconengines/libqsvgicon.so
-- Installing: /tmp/test-install/plugins/imageformats/libqsvg.so
-- Installing: /tmp/test-install/plugins/xcbglintegrations/libqxcb-egl-integration.so
-- Installing: /tmp/test-install/plugins/xcbglintegrations/libqxcb-glx-integration.so
-- Installing: /tmp/test-install/plugins/platforms/libqxcb.so
...

Can you create an install trace with cmake --trace-expand --trace-redirect=cmake_install.cmake.trace -Pcmake_install.cmake please?

Sure! I just saw that i zipped not correctly yesterday. I meant to give you the subfolder qml_cycle in that zip only. Sorry for the confusion there.

Here is the trace:
cmake_install.cmake.trace (346.3 KB)

As far as I can tell __QT_DEPLOY_PLUGINS is empty (qtbase/Qt6CoreDeploySupport.cmake at 1f142d903835a86996a00c2ee32689c7fb10c1e4 · qt/qtbase · GitHub)

They probably should be populated here (qtbase/QtPublicPluginHelpers.cmake at 514027f43fa526103da548f86d647ac33e288474 · qt/qtbase · GitHub).

The target, which is QmlCycleBugExec is not marked for deployment, so the function returns early.

And it seems the code to set this property (qtbase/Qt6CoreMacros.cmake at 30efb24d45e61f3329484c8dd416ee6cde25739b · qt/qtbase · GitHub) is called after the previous code as far as I can tell from my little debugging.

I hope I’m on the right track and that might help. I just commented out this early return for test and now the plugins are generated.

Edit: My actual project can now be sucessfully deployed with this “Fix”. So we just need to find out how to properly fix it :smiley: Or why it’s working on your system, but not on mine.

Thanks, your analysis is correct. To fix your project for now, put the qt_generate_deploy_qml_app_script call into the CMakeLists.txt that creates QmlCycleBugExec.

I’ve created [QTBUG-109741] Running qt_generate_deploy_app_script(tgt) must be run in the same directory scope as tgt - Qt Bug Tracker to track this.