Deploying to Linux with CMake

A while ago, I blogged about our CMake deployment API that allows you to enhance the cmake --install step with instructions that deploy Qt libraries, plugins and assets. At the time of writing, Linux was not supported yet.  But not anymore: Qt 6.5 comes with deployment support for Linux!

Quick recap how it looks like to deploy a simple Qt application. Consider the following project.

cmake_minimum_required(VERSION 3.22)
project(MyApp)

find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()

qt_add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE Qt::Widgets)

Now, we install the application and enhance the installation step. Our goal is to have a (mostly) self-contained directory after installation.

# Install the executable into "${CMAKE_INSTALL_PREFIX}/bin".
install(TARGETS MyApp
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

# Generate the deployment script for the target MyApp.
qt_generate_deploy_app_script(
TARGET MyApp
FILENAME_VARIABLE deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)

# Call the deployment script on "cmake --install".
install(SCRIPT ${deploy_script})

We build and install the project:

$ qt-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/my-application ..
[output omitted]
$ cmake --build .
[output omitted]
$ cmake --install .
-- Install configuration: "Release"
-- Installing: /tmp/my-application/bin/myapp
-- Set runtime path of "/tmp/my-application/bin/myapp" to "$ORIGIN:$ORIGIN/../lib"
-- Writing /tmp/my-application/bin/qt.conf
-- Running generic Qt deploy tool on /home/someone/playground/myapp/build/myapp
-- Installing: /tmp/my-application/lib/libQt6Core.so.6
-- Installing: /tmp/my-application/lib/libQt6Core.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6DBus.so.6
-- Installing: /tmp/my-application/lib/libQt6DBus.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6EglFSDeviceIntegration.so.6
-- Installing: /tmp/my-application/lib/libQt6EglFSDeviceIntegration.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6EglFsKmsSupport.so.6
-- Installing: /tmp/my-application/lib/libQt6EglFsKmsSupport.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6Gui.so.6
-- Installing: /tmp/my-application/lib/libQt6Gui.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6OpenGL.so.6
-- Installing: /tmp/my-application/lib/libQt6OpenGL.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6Widgets.so.6
-- Installing: /tmp/my-application/lib/libQt6Widgets.so.6.6.0
-- Installing: /tmp/my-application/lib/libQt6XcbQpa.so.6
-- Installing: /tmp/my-application/lib/libQt6XcbQpa.so.6.6.0
-- 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

After the myapp executable is installed into /tmp/my-application/bin/, the deployment script handles the following tasks:

  • Create a qt.conf file next to the executable that contains information about the directory layout. This is needed at runtime to find the plugins and assets. See the qt.conf documentation for details.
  • Inspect the executable and used Qt plugins with CMake's built-in file(GET_RUNTIME_DEPENDENCIES) to determine which Qt libraries to deploy.
  • Install the necessary Qt plugins and Qt libraries.

The installation directory can now be copied to a different machine, and the application should still work.

Packaging the installation folder

After installation, we have a nice self-contained directory that can be packaged and shipped. For the sake of example, we'll create a .deb package using cpack.

Add the following at the end of the project file:

set(CPACK_PACKAGE_NAME my-app)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My amazing application")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set(CPACK_VERBATIM_VARIABLES ON)
set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/myapp")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Manfred Maintainer <mm@example.com>")
set(CPACK_DEBIAN_PACKAGE_DEPENDS libc6 libstdc++6 libgcc-s1)
include(CPack)

Re-configure the project and run cpack:

$ cpack -G DEB
CPack: Create package using DEB
CPack: Install projects
CPack: - Install project: MyApp []
CPack: Create package
CPack: - package: /home/someone/projects/myapp/build/my_app-1.0-Linux.deb generated.

That's already it. The package has been wrapped and is ready to be shipped.

You can inspect the content of the package with
dpkg -c my_app-1.0-Linux.deb
and install with
sudo dpkg -i my_app-1.0-Linux.deb
.

Summary

We have seen how to deploy a simple Qt application on Linux using Qt 6.5's CMake deployment API. There's no dedicated linuxdeployqt tool. The current solution wholly relies on CMake's built-in functionality.

These are the platforms that are covered by Qt's CMake deployment API:

  • Windows
  • macOS
  • Linux

For Android and iOS there's still the need to call androiddeployqt / macdeployqt manually - or you let Qt Creator handle these details.

The relocatable installation folder can be conveniently wrapped into a package using tools like cpack.

Please report any issues you find while playing with this at our bug tracker.


Blog Topics:

Comments