NixOS works: document the full FHS recipe, drop the nix-ld dead end
Build PCM package / build (push) Successful in 5s

First successful Linux field test (NixOS 26.05, Plasma 6, KiCad 10).
Getting there peeled three layers the error messages misattribute:
the FHS env itself (libgthread), then libs the xcb-cursor hint hides
(the xcb-util family, and libzstd for libQt6Core - in zstd.out, not
zstd's default output), and finally Plasma's QT_PLUGIN_PATH feeding
the system Qt's platform plugins to the wheel's bundled Qt, a
private-ABI mismatch that fails exactly like a missing library.

New docs/NIXOS.md carries the verified buildFHSEnv wrapper, the
steam-run quick test (now with env -u QT_PLUGIN_PATH), and a guide
for chasing future missing sonames. README upgrades Linux from
untested to works and links the doc. The in-plugin error no longer
suggests programs.nix-ld: the venv python is a Nix binary, so the
nix-ld shim never engages for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Berwn
2026-07-23 16:08:15 +07:00
parent 64676624e6
commit a4945f419c
3 changed files with 151 additions and 27 deletions
+20 -24
View File
@@ -31,13 +31,13 @@ SWIG API. Requires KiCad **10.0.1+**.
## Setup (one-time)
The plugin is developed and tested on **Windows**; **macOS works**
(field-tested on KiCad 10 after a round of mac-specific fixes).
**Linux is expected to work but is untested so far** — the code and
the dependency stack have been audited (KiCad builds the plugin a
private Python venv from `requirements.txt` on every platform, from
pre-built wheels only, no compiler needed), but nobody has run the
plugin there yet. Reports welcome! Steps 14 are the same everywhere;
OS specifics are spelled out per step and in *Platform notes* below.
(field-tested on KiCad 10 after a round of mac-specific fixes), and
**Linux works** (field-tested on NixOS — the hardest Linux to run pip
wheels on; mainstream FHS distributions should be no harder, reports
welcome). KiCad builds the plugin a private Python venv from
`requirements.txt` on every platform, from pre-built wheels only, no
compiler needed. Steps 14 are the same everywhere; OS specifics are
spelled out per step and in *Platform notes* below.
1. **Enable the API server**: KiCad → Preferences → Plugins → check
*Enable KiCad API*.
@@ -87,27 +87,23 @@ OS specifics are spelled out per step and in *Platform notes* below.
Plot and dialog windows may open **behind** the KiCad window (they
are raised best-effort) — check the Dock if nothing seems to appear
after a solve.
- **Linux** — **untested** (audited only, same caveat). The venv uses
- **Linux** — **works** (field-tested on NixOS, KiCad 10; mainstream
distributions are audited but not yet field-tested). The venv uses
the system Python (3.9+), so the stack matches your distribution.
On **ARM64 (aarch64)** there are no pyamg wheels —
`requirements.txt` skips pyamg there and the solver falls back to
Jacobi-CG: same results, noticeably slower on large grids.
**NixOS**: pip's Linux wheels link against standard FHS library
paths, which NixOS does not provide — PySide6 fails with
`libgthread-2.0.so.0: cannot open shared object file`. The plugin
cannot fix this from inside its venv (KiCad installs wheels only);
run KiCad inside an FHS environment. `steam-run kicad` alone is
**not** enough: its runtime predates Qt 6.5's hard requirement for
`libxcb-cursor0`, so Qt aborts with *"no Qt platform plugin could
be initialized"*. Supply that one library on top:
```sh
XCBCUR=$(nix build --no-link --print-out-paths nixpkgs#xcb-util-cursor)
QT_QPA_PLATFORM=xcb LD_LIBRARY_PATH=$XCBCUR/lib steam-run kicad
```
or build a dedicated FHS wrapper with `buildFHSEnv` whose
`targetPkgs` include `kicad`, `xcb-util-cursor`, `glib`,
`fontconfig`, `freetype`, `dbus`, `libGL`, `libxkbcommon`,
`wayland` and the `xorg` X11/xcb libraries.
**NixOS**: **works** (field-tested on NixOS 26.05, Plasma 6). pip's
Linux wheels link against standard FHS library paths, which NixOS
does not provide — PySide6 fails with `libgthread-2.0.so.0: cannot
open shared object file`. The plugin cannot fix this from inside
its venv (KiCad installs wheels only); run KiCad inside an FHS
environment built with `buildFHSEnv`, and — on KDE Plasma — unset
`QT_PLUGIN_PATH`, which otherwise poisons the wheel's bundled Qt
with the system's Qt plugins. The tested wrapper (exact package
list incl. the non-obvious `zstd.out` and xcb-util family), a
`steam-run` quick test, and a debugging guide are in
[docs/NIXOS.md](docs/NIXOS.md).
## Usage
+128
View File
@@ -0,0 +1,128 @@
# Running Fill Resistance on NixOS
KiCad builds the plugin a private venv from pip wheels
(`requirements.txt`). Those Linux wheels — PySide6 in particular —
`dlopen` system libraries at standard FHS paths (`/usr/lib`), which
NixOS does not provide. Nothing inside the venv can fix that; KiCad
must be **launched in an environment that supplies the libraries**.
This page gives a verified, declarative setup (field-tested on
NixOS 26.05, KiCad 10, Plasma 6 on Wayland) and maps each failure
mode to its cause, because the error messages are misleading.
## The three failure layers
You will hit these in order; each fix below removes one.
1. **`libgthread-2.0.so.0: cannot open shared object file`**
(shown in the plugin's own error figure) — no FHS environment at
all. PySide6's bundled Qt cannot load glib & friends.
2. **`qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is
needed…` / `no Qt platform plugin could be initialized`** —
FHS present but incomplete. Beyond the obvious `xcb-util-cursor`,
the bundled Qt needs the whole **xcb-util family** (`icccm`,
`image`, `keysyms`, `render-util`, `util`) and **`libzstd`**
(a hard dependency of `libQt6Core`; note that `pkgs.zstd`'s
default output ships only the CLI — the library lives in
`zstd.out`). Qt prints the xcb-cursor hint for *any* missing
dependency of the platform plugin, so don't trust it literally.
3. **`Could not load the Qt platform plugin "wayland"/"xcb" in ""
even though it was found`** with all libraries present — the
desktop session (KDE Plasma does this) exports **`QT_PLUGIN_PATH`**
pointing at the system's Qt plugin directories. That variable takes
precedence over the wheel's bundled plugins, so the venv's PySide6
(its own Qt, e.g. 6.11.1) tries to load platform plugins built
against the system Qt (e.g. 6.11.0) — a private-ABI mismatch that
fails exactly like a missing library. The fix is to **unset
`QT_PLUGIN_PATH`** for KiCad and everything it spawns. KiCad
itself is wxWidgets/GTK and does not use the variable.
## Recommended setup: an FHS wrapper
Wrap KiCad in `buildFHSEnv` so a plain `kicad` launch carries
everything. Home-manager example (`home.nix`); for a system-wide
install put the same package in `environment.systemPackages` in
`configuration.nix` instead:
```nix
{ pkgs, ... }:
let
kicad-fhs = pkgs.buildFHSEnv {
name = "kicad";
targetPkgs = p: with p; [
kicad glib fontconfig freetype dbus libGL libxkbcommon
xcb-util-cursor wayland zlib zstd.out
xorg.libX11 xorg.libxcb xorg.libXext xorg.libXrender
xorg.libSM xorg.libICE xorg.libXrandr xorg.libXi
xorg.libXcursor xorg.libXfixes
# xcb-util family needed by PySide6's bundled xcb platform plugin
xorg.xcbutil xorg.xcbutilwm xorg.xcbutilimage
xorg.xcbutilkeysyms xorg.xcbutilrenderutil
];
# Plasma exports QT_PLUGIN_PATH (system Qt plugin dirs); the venv's
# bundled Qt chokes on those mismatched plugins. KiCad is wx/GTK,
# so dropping the variable is safe.
profile = "unset QT_PLUGIN_PATH";
runScript = "kicad";
};
in
{
home.packages = [ kicad-fhs /* replaces bare pkgs.kicad */ ];
# buildFHSEnv ships no .desktop file; restore the menu launcher.
xdg.desktopEntries.kicad = {
name = "KiCad";
exec = "kicad %F";
icon = "kicad";
categories = [ "Development" "Electronics" ];
mimeType = [ "application/x-kicad-project" ];
};
}
```
Rebuild, **fully quit any running KiCad** (a running instance keeps
its old environment), relaunch, click the Ω button. A one-line
`Could not load the Qt platform plugin "wayland"` warning may remain
if the wayland client stack is unhappy — it is harmless; Qt falls
back to xcb (XWayland) and the dialog appears.
## Quick test without a rebuild
`steam-run` provides a broad FHS that is one library short
(`libxcb-cursor`, required by Qt ≥ 6.5) and, on Plasma, still leaks
`QT_PLUGIN_PATH`:
```sh
XCBCUR=$(nix build --no-link --print-out-paths nixpkgs#xcb-util-cursor)
env -u QT_PLUGIN_PATH QT_QPA_PLATFORM=xcb \
LD_LIBRARY_PATH=$XCBCUR/lib steam-run kicad
```
(Fish: `set XCBCUR (nix build --no-link --print-out-paths
nixpkgs#xcb-util-cursor)`, then the same `env …` line.)
## Debugging further failures
If a new wheel version needs another library, reproduce the plugin's
Qt startup *outside* KiCad against the wrapper's library set — the
venv survives at
`~/.cache/kicad/10.0/python-environments/th.co.b4l.fill-resistance`:
```sh
ROOTFS=$(nix-store -qR "$(readlink -f "$(command -v kicad)")" \
| grep fhsenv-rootfs | head -1)
env -i HOME=$HOME DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY \
LD_LIBRARY_PATH=$ROOTFS/usr/lib64 QT_DEBUG_PLUGINS=1 \
~/.cache/kicad/10.0/python-environments/th.co.b4l.fill-resistance/bin/python3 \
-c 'from PySide6.QtWidgets import QApplication; \
print(QApplication([]).platformName())'
```
A missing library shows up as a plain `ImportError: libfoo.so.N:
cannot open shared object file` — map the soname to its nixpkgs
attribute (`nix-locate libfoo.so.N`, from `nix-index`) and add it to
`targetPkgs`. If instead every library loads and only the platform
plugin fails, compare the environment of the *running* KiCad
(`tr '\0' '\n' < /proc/$(pgrep -x kicad)/environ`) for Qt variables
leaking in from the session — `QT_PLUGIN_PATH` above was found
exactly this way.
+3 -3
View File
@@ -53,9 +53,9 @@ def main() -> None:
f"A compiled dependency cannot load its system "
f"libraries: {e}\nThe plugin venv is built from pip "
f"wheels, which expect standard (FHS) library paths. "
f"On NixOS, run KiCad inside an FHS environment (e.g. "
f"steam-run) or enable programs.nix-ld with Qt's "
f"runtime libraries - see README, Platform notes."
f"On NixOS, run KiCad inside an FHS environment "
f"(buildFHSEnv wrapper, or steam-run for a quick "
f"test) - see docs/NIXOS.md in the plugin repo."
)
try:
kicad, board = board_io.connect()