Cannot use Qt6 in classic core22 snap

I am trying to use Qt 6.3 or later in a classic core22 snap:

name: qt-snap-test
title: Qt SNAP test
version: "1.0"
base: core22
summary: Qt SNAP test
description: test snap for Qt

grade: stable
confinement: classic

parts:

  qt:
    source: http://code.qt.io/qt/qt5.git
    # source-tag: "v6.2.4"
    source-tag: "v6.3.0"
    source-depth: 1
    source-submodules: [qtbase,qtwayland]
    plugin: cmake
    cmake-generator: Ninja
    build-attributes:
    - enable-patchelf
    cmake-parameters:
    - -DCMAKE_INSTALL_PREFIX=/
    build-packages:
    - libegl-dev
    - libgl-dev
    - libharfbuzz-dev
    - libjpeg-dev
    - libpng-dev
    - libvulkan-dev
    - libwayland-dev
    # xcb QPA (Qt Platform Abstraction) platform plugin
    - libfontconfig1-dev
    - libfreetype6-dev
    - libx11-dev
    - libx11-xcb-dev
    - libxext-dev
    - libxfixes-dev
    - libxi-dev
    - libxrender-dev
    - libxcb1-dev
    - libxcb-glx0-dev
    - libxcb-keysyms1-dev
    - libxcb-image0-dev
    - libxcb-shm0-dev
    - libxcb-icccm4-dev
    - libxcb-sync-dev
    - libxcb-xfixes0-dev
    - libxcb-shape0-dev
    - libxcb-randr0-dev
    - libxcb-render-util0-dev
    - libxkbcommon-dev
    - libxkbcommon-x11-dev
    stage-packages:
    - libegl1
    - libfontconfig1
    - libfreetype6
    - libgl1
    - libglvnd0
    - libglx0
    - libgraphite2-3
    - libharfbuzz0b
    - libjpeg-turbo8
    - libpcre2-16-0
    - libpng16-16
    - libwayland-cursor0
    - libwayland-egl1
    - libx11-6
    - libx11-xcb1
    - libxau6
    - libxcb-glx0
    - libxcb-icccm4
    - libxcb-image0
    - libxcb-keysyms1
    - libxcb-randr0
    - libxcb-render-util0
    - libxcb-render0
    - libxcb-shape0
    - libxcb-shm0
    - libxcb-sync1
    - libxcb-util1
    - libxcb-xfixes0
    - libxcb-xkb1
    - libxcb1
    - libxdmcp6
    - libxkbcommon-x11-0
    - libxkbcommon0

  mini-cmake-qt:
    after: [qt]
    source: https://github.com/euler0/mini-cmake-qt.git
    source-branch: main
    source-depth: 1
    plugin: cmake
    cmake-generator: Ninja
    build-attributes:
    - enable-patchelf
    cmake-parameters:
    - -DCMAKE_INSTALL_PREFIX=/

apps:
  qt-snap-test:
    command: bin/example
    environment:
      QT_QPA_PLATFORM: wayland;xcb

But when I start this snap, it cannot recognise the platform plugins (wayland, xcb, …):

qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
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.

Abgebrochen (Speicherabzug geschrieben)

Debugging this with QT_DEBUG_PLUGINS=1 qt-snap-test shows that it cannot read the metadata from the shared object plugin files:

qt.core.plugin.factoryloader: looking at "/snap/qt-snap-test/x1/plugins/platforms/libqxcb.so"
qt.core.plugin.loader: Failed to find metadata in lib /snap/qt-snap-test/x1/plugins/platforms/libqxcb.so: '/snap/qt-snap-test/x1/plugins/platforms/libqxcb.so' is not a Qt plugin (metadata not found)
qt.core.plugin.factoryloader: "Failed to extract plugin meta data from '/snap/qt-snap-test/x1/plugins/platforms/libqxcb.so': '/snap/qt-snap-test/x1/plugins/platforms/libqxcb.so' is not a Qt plugin (metadata not found)"
         not a plugin

This used to work with Qt 6.2.4 but it fails with Qt 6.3 and upwards.

It appears that “patchelf” is removing the metadata of the plugins. I wrote a small program to show the Qt plugin metadata: https://github.com/christianrauch/qt_plugin_metadata/blob/main/main.cpp. This project hast to be compiled with a Qt version that is equal or greater than the version of the Qt plugin; in this case Qt 6.3.

Running this program with the XCB plugin (/snap/qt-snap-test/current/plugins/platforms/libqxcb.so) for different snapcraft variations shows that the metadata is missing for some snapcraft configurations.

For source-tag: "v6.2.4" with enable-patchelf:

has metadata?: YES
IID : org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3
MetaData : <Object>
archlevel : 0.000000
className : QXcbIntegrationPlugin
debug : False
version : 393728.000000

For source-tag: "v6.3.0" with enable-patchelf:

has metadata?: NO

And for source-tag: "v6.3.0" without enable-patchelf:

has metadata?: YES
IID : org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3
MetaData : <Object>
archlevel : 1.000000
className : QXcbIntegrationPlugin
debug : False
version : 393984.000000

However, without enable-patchelf the snap will not run because of wrong rpaths etc.

How do I prevent snapcraft to remove the metadata of the Qt plugins with enable-patchelf?

This one should help: Tide/snap/snapcraft.yaml at eec3d5ad703daa2e956aad4ca3c4dfd3200a5bd9 · fredldotme/Tide · GitHub

What it does is setting the interpreter and the rpath manually. Also take note that qt6 is custom-built here, it has the rpath set via CMAKE_INSTALL_RPATH. Otherwise this approach should also work with Qt6 from the Ubuntu archive.

Also take a look at the cleanup part for removing graphics-related libraries before shipping the classic Snap.

EDIT: I know this is in response to an old issue, but better late than not at all.

Do you have a concrete snapcraft definition for the “mini-cmake-qt” example above? The example that you linked is also just using enable-patchelf. What is the effect of setting the interpreter and how does this related to enable-patchelf?

I would appreciate a minimal snapcraft example of a classic snap that builds Qt and an application on top of it.

I don’t as I see this as an exercise on your end. Also, note that some might use enable-patchelf while some parts might not and have their interpreter setup done explicitly.

I take it that you maintain the Tide snap and went through this to discover all the loose ends of getting Qt to build in a classic snap. But I find proper documentation on this (patching ELF files for classic snaps) very scarce.

It’s the first time I see --set-interpreter used manually in a snapcraft definition. I see this being used in the documentation at Classic linter | Snapcraft documentation, but there is also saying that this is done automatically for core22 with snapcraft 7.3 when enable-patchelf is set. I can also see from the log that the interpreter is set for some ELF files:

Patch ELF file: 'bin/qmake'
  Interpreter='/snap/core22/current/lib64/ld-linux-x86-64.so.2'
  Current rpath=['$ORIGIN/../lib']
  Proposed rpath=['$ORIGIN/../usr/lib/x86_64-linux-gnu', '$ORIGIN/../lib', '/snap/core22/current/lib/x86_64-linux-gnu']

but I can also see that it is not set for other ELF files:

Patch ELF file: 'plugins/platforms/libqeglfs.so'
  Current rpath=['$ORIGIN/../../lib']
  Proposed rpath=['$ORIGIN/../../usr/lib/x86_64-linux-gnu', '$ORIGIN/../../lib', '/snap/core22/current/lib/x86_64-linux-gnu']
Patch ELF file: 'plugins/platforms/libqlinuxfb.so'
  Current rpath=['$ORIGIN/../../lib']
  Proposed rpath=['$ORIGIN/../../usr/lib/x86_64-linux-gnu', '$ORIGIN/../../lib', '/snap/core22/current/lib/x86_64-linux-gnu']

From the documentation this behaviour is not clear. Why is the interpreter not set for all ELF files?

I prefer to better understand what causes this, why the interpreter has to be set manually again after enable-patchelf, and why this issue only surfaces with Qt 6.3 onwards. Just trying out solutions until something works, without understanding what causes the problem and how it is solved, is not very satisfying or sustainable, IMHO.

Eventually, I managed to get this working by not using enable-patchelf for Qt related things, but replacing this by setting the linker flags manually:

    cmake-parameters:
    - -DCMAKE_INSTALL_RPATH=\$ORIGIN/../lib:\$ORIGIN/../lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}:\$ORIGIN/../../lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}:\$ORIGIN:\$ORIGIN/${CRAFT_ARCH_TRIPLET_BUILD_FOR}:/snap/core24/current/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}
    build-environment:
    - LDFLAGS: "-Wl,-dynamic-linker=/snap/core24/current/lib64/ld-linux-x86-64.so.2"

Still, the linter is marking a couple of issues, such as:

- classic: usr/lib/libQt6Core.so.6.8.1: ELF interpreter should be set to '/snap/core24/current/lib64/ld-linux-x86-64.so.2'. (https://snapcraft.io/docs/linters-classic)