In commit 9ea3e371df617725eab1a8beacc05bd5eefdca6f, the setup-hook for qtbase adds itself to propagated-user-env-packages. Normally this isn't a big problem and just cluttering up the system path, but if there are mixed versions of the Qt libraries, things can get pretty nasty when stuff is loaded at runtime.
And that's what happens if you add a random Qt 5.5 program into environment.systemPackages with KDE 5 enabled:
$ nix-store -qR /run/current-system/sw | grep qtbase
/nix/store/kfj94grdfkyn1ryd8kj7vdx470sbp1kb-qtbase-5.6.0
/nix/store/ndhbfkanyg46y8phw9jv93cwg1fm7ryr-qtbase-5.6.0-dev
/nix/store/bx34kkw8yjkh70515hy0wximjvx2dh00-qtbase-5.5.1
/nix/store/yrxzmr1ck2c17znq0knddgllznzq8d10-qtbase-5.5.1-dev
In theory, this shouldn't be a problem, but it turns out that KDE won't start with this, because it's trying to load the libraries at runtime from the system path.
This is what happens during KDE startup (reindented to save horizontal space):
/nix/store/9mpqvbgg7slw3hal78ryrp80xhq8kd8k-plasma-workspace-5.6.4/bin/ksplashqml:
symbol lookup error: /nix/store/bx34kkw8yjkh70515hy0wximjvx2dh00-qtbase-5.5.1/lib/libQt5XcbQpa.so.5:
undefined symbol: _ZN22QWindowSystemInterface19registerTouchDeviceEP12QTouchDevice
The plasma-workspace package only lists qtbase-5.6 in its dependencies and the ksplashqml binary is only linked against qtbase-5.6 as well:
$ nix-store -qR /nix/store/9mpqvbgg7slw3hal78ryrp80xhq8kd8k-plasma-workspace-5.6.4 | grep qtbase
/nix/store/kfj94grdfkyn1ryd8kj7vdx470sbp1kb-qtbase-5.6.0
/nix/store/ndhbfkanyg46y8phw9jv93cwg1fm7ryr-qtbase-5.6.0-dev
So adding _any_ Qt 5.5 program to environment.systemPackages with current KDE 5 will break KDE as of 903582798636ac406dbe7b82c502d59fba7b4293.
I guess we could at least mitigate this by only propagating the plugin stuff into systemPackages (but even that could end up having mixed Qt versions), but I'm neither using KDE nor do I know details about it.
Cc: @ttuegel
Also here are the links from the system path for libQt5XcbQpa:
$ readlink /run/current-system/sw/lib/libQt5XcbQpa.so.5*
/nix/store/bx34kkw8yjkh70515hy0wximjvx2dh00-qtbase-5.5.1/lib/libQt5XcbQpa.so.5
/nix/store/bx34kkw8yjkh70515hy0wximjvx2dh00-qtbase-5.5.1/lib/libQt5XcbQpa.so.5.5
/nix/store/bx34kkw8yjkh70515hy0wximjvx2dh00-qtbase-5.5.1/lib/libQt5XcbQpa.so.5.5.1
/nix/store/kfj94grdfkyn1ryd8kj7vdx470sbp1kb-qtbase-5.6.0/lib/libQt5XcbQpa.so.5.6
/nix/store/kfj94grdfkyn1ryd8kj7vdx470sbp1kb-qtbase-5.6.0/lib/libQt5XcbQpa.so.5.6.0
Linking to #12575
I'm testing a patch for this now.
/participate
The problem here is not actually the Qt libraries themselves because Nix's RPATH magic means each Qt program will always load the correct version, i.e. the libraries in the system path do not affect dynamic loading. The actual problem is with Qt's numerous plugins: these are (or can be) loaded from the system path. This would not be a problem if Qt had any notion of plugin versions, but it does not: Qt just loads whatever plugins it thinks it needs and segfaults be damned.
I originally approached this by making a limited emulation of buildEnv for Qt and KDE programs. Now I realize that is not the right way to go. For one thing, buildEnv already exists. For another, it's not good enough to _extend_ the plugin path every time an environment gets loaded. Every program must run in its _specific_, _pure_ environment.
So, this is what will happen: Qt 5 programs will stop propagating plugins into the user environment, but they will also absolutely, unconditionally stop working unless the package is made to use makeQtWrapper (as a consequence of being run in a more restricted environment). KDE 5 still needs all of its packages to live in a single environment (for other reasons). We will have something like kde5Env (based on buildEnv) and all your KDE 5 packages will have to go in there, or again, they will not work _at all_.
It will take more time for me to patch and document this. This is a very significant change, so I want the documentation especially to be very robust.
If I understand correctly, this would break Qt support for input methods -- people won't be able to use their method of choice because the plugin won't be loaded. GTK had a similar problem, which was fixed by introducing versioned paths for plugins for GTK2 and GTK3. Maybe we want to go down the same road there (provided it won't be very difficult)?
Maybe we want to go down the same road there (provided it won't be very difficult)?
Keep in mind we're talking only about Qt 5; the paths are already versioned between Qt 4 and Qt 5.
Also, this won't break I-Bus support, which is built into Qt 5. Qt upstream would probably also tell you that it's the only input method with their approval, though I recognize this isn't really relevant here.
Qt makes no ABI compatibility guarantees between versions. Even minor versions. Even builds of the same version with different options. So, the only way this could safely work is if plugins went into $out/lib/qt5/<hash>/plugins. But, there's not just one hash we have to worry about; there's the hash of every package that provides a plugin (which is almost all of the Qt modules).
Qt makes no ABI compatibility guarantees between versions. Even minor versions. Even builds of the same version with different options.
Oh, it's _that_ bad -- I've supposed that it could be solved with 5.5, 5.4 etc separation, but it sounds like the things are way worse.
I think the only other input method that we support and that may require a separate plugin then (I'm not sure) is Fcitx (see table in https://github.com/NixOS/nixpkgs/pull/11254). If we can save its support, then it's okay I think.
Also, this won't break I-Bus support, which is built into Qt 5. Qt upstream would probably also tell you that it's the only input method with their approval, though I recognize this isn't really relevant here.
I just had this issue with qt fcitx plugin not working with qt55 applications.
Dedicated issue:#15569
Qt makes no ABI compatibility guarantees between versions.
This is actually not true anymore, see here. I am probably remembering some long-past problems with Qt 4. They _claim_ backwards binary compatibility between minor versions, so installing input methods built with the latest Qt 5 should suffice. For statically-known dependencies, I think we should still isolate dependencies as much as possible.
Yay! Sounds nice, what do you think of patching Qt to search plugins in /run/current-system/sw/lib/qt${major}.${minor} (or alike)?
Yay! Sounds nice, what do you think of patching Qt to search plugins in /run/current-system/sw/lib/qt${major}.${minor} (or alike)?
We could do that. The only thing that should be loaded from $NIX_PROFILES/lib/qt-5.x is input methods, though; Qt upstream claims that building input methods with the latest available Qt is good enough, in which case all Qt 5 versions could simply load input methods from $NIX_PROFILES/lib/qt5. I think I will try the simpler approach first, but keeping such a patch in mind if we encounter trouble.
FWIW, removing /run/current-system/sw/lib/libQt* from the system path allows me to run Krita (built atop 5.6.1) even though I have it "contaminated" with 5.5's modules. This can serve as a workaround (ugly one because you have to patch nixos/modules/config/system-path.nix).
EDIT: with 5.6.1 I need to remove lib/qt5 too. So it's essentially "hack-remove Qt from system path" which would break at least input methods I think. Not recommended.
I came up with this derivation to help track down system packages with Qt dependencies:
let
nixos = import <nixpkgs/nixos> {};
pkgs = import <nixpkgs> {};
inherit (pkgs) lib;
in pkgs.runCommand "test" {} ''
touch $out
${lib.concatMapStringsSep "\n" (pkg:
let bpkg = lib.getBin pkg;
in ''
qtver="$(sed -n 's,.*qtbase-\([^/]*\).*,\1,p' ${bpkg}/nix-support/propagated-user-env-packages 2>/dev/null || true)"
if [ -n "$qtver" ]; then
echo "${bpkg}: $qtver" >> $out
fi
''
) nixos.config.environment.systemPackages}
''
Paste it to a file and nix-build it. Look into result. It may be inaccurate w.r.t. multiple outputs but should do a good job in most cases.
cc @womfoo
Thanks @abbradar!
The issue should be resolved now. Properly-wrapped Qt packages will use their correct plugin versions. Input methods can still be installed in system-wide or per-user paths.
@ttuegel I am not sure if this is related to this, but the way qt55.fcitx-qt5 is built has changed recently.
qt55.fctix-qt5 is not generating $out/lib/qt5/plugins/* anymore while qt56.fcitx-qt5 still does.
I have looked into this and the cause seems to be that the build/platforminputcontext/cmake_install.cmake generated by the buildPhase for the qt55 version contains wrong path references. (/run/user/1000/...)
qt55.fctix-qt5 build/platforminputcontext/cmake_install.cmake
if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")
if(EXISTS "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so" AND
NOT IS_SYMLINK "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
file(RPATH_CHECK
FILE "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so"
RPATH "")
endif()
list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES
"/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)
message(WARNING "ABSOLUTE path INSTALL DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}")
endif()
if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)
message(FATAL_ERROR "ABSOLUTE path INSTALL DESTINATION forbidden (by caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}")
endif()
file(INSTALL DESTINATION "/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts" TYPE MODULE FILES "/tmp/fcitx-qt5/fcitx-qt5-1.0.5/build/platforminputcontext/libfcitxplatforminputcontextplugin.so")
if(EXISTS "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so" AND
NOT IS_SYMLINK "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
if(CMAKE_INSTALL_DO_STRIP)
execute_process(COMMAND "/nix/store/84x0vldsif3v7i7c6i2ckfzp4jyg9bq9-binutils-2.26/bin/strip" "$ENV{DESTDIR}/run/user/1000/tmp.omlWR9uo4A/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
endif()
endif()
endif()
to compare with qt56.fctix-qt5 build/platforminputcontext/cmake_install.cmake
if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")
if(EXISTS "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so" AND
NOT IS_SYMLINK "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
file(RPATH_CHECK
FILE "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so"
RPATH "")
endif()
list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES
"/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)
message(WARNING "ABSOLUTE path INSTALL DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}")
endif()
if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)
message(FATAL_ERROR "ABSOLUTE path INSTALL DESTINATION forbidden (by caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}")
endif()
file(INSTALL DESTINATION "/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts" TYPE MODULE FILES "/tmp/fcitx-qt56/fcitx-qt5-1.0.5/build/platforminputcontext/libfcitxplatforminputcontextplugin.so")
if(EXISTS "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so" AND
NOT IS_SYMLINK "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
if(CMAKE_INSTALL_DO_STRIP)
execute_process(COMMAND "/nix/store/84x0vldsif3v7i7c6i2ckfzp4jyg9bq9-binutils-2.26/bin/strip" "$ENV{DESTDIR}/nix/store/d0n3cxm8qndkpdfxy8da682qzhkbakrr-qtbase-5.6.1-1/lib/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so")
endif()
endif()
endif()
reverting 766ea7c4ca6dc54bd26a28f67a99257bde2ff331 fixes the problem, should I open a new issue?
No, we should actually stop building qt55.fcitx-qt5. The Qt 5.6 version will work with Qt 5.5 applications, but not the other way around. Input method plugins should always just be built with the latest Qt we have.
The Qt 5.6 version will work with Qt 5.5 applications, but not the other way around.
As far as I can tell, qt56.fcitx-qt5 doesn't work with qutebrowser, calibre and probably other applications using PyQt.
As far as I can tell, qt56.fcitx-qt5 doesn't work with qutebrowser, calibre and probably other applications using PyQt.
Of course not, because nothing ever works the way it's supposed to. :frowning:
Please open a new issue about making input method plugins co-installable. I will see about patching the fcitx builder. We don't want to revert that commit.
Thanks, I will open a new issue!
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
Most helpful comment
The issue should be resolved now. Properly-wrapped Qt packages will use their correct plugin versions. Input methods can still be installed in system-wide or per-user paths.