Run an eternally precisely specified package in Guix
If Baldur’s Gate 3 stops working because Wine updated to version 10,
you can try to hack something together or find a flatpak someone else
maintains for you and keep playing. Or you can learn about Guix
inferior packages and gain a tool for truly eternal package
definition. There are tons of reasons why you may want that, but “my
game stopped working!” is as good as any other :-)
.
Inferiors are packages from other versions of Guix composed together with an up to date system. Along with guix shell –container they enable you to create precisely defined setups.
So when the game stopped working, I turned to my existing run-in-container setup and tracked down the commit just before wine was updated to version 10:
git log --oneline -A1 | grep wine | less
1c434fb17c4 gnu: wine: Update to 10.0. c788962baee gnu: conda: Add missing dependency.
Then I added an inferior channel from the exact version of the Guix package setup and used the inferior in the manifest definition:
(use-modules (gnu) (guix inferior) (guix channels) (srfi srfi-1)) (define channels (list (channel (name 'guix) (url "https://git.savannah.gnu.org/git/guix.git") (commit ;; The commit with wine64@9 "c788962baeeb6bf021b30c9ad774d472f592fdb5")))) (define inferior (inferior-for-channels channels)) ;; the manifest (result of the manifest) (packages->manifest (append (lookup-inferior-packages inferior "wine64") ;; simplified parsing (append-map specification->package `( "grafx2" ... ))))
I could now describe how to use this to have a setup that ensures that your companies’ system will keep working1 if dependencies turn volatile (and maybe add your own persistent cache for the package sources to ensure that these also stay available) — along with the advantage that the dependencies are actually verified and built from sources down to the most remote libraries from just a minimal seed — but I’d rather show you the full example how to keep Baldur’s Gate 3 working.2
The downside is: this is a big file, because it contains my whole manifest.
The upside is: this is complete, ready to be dropped into
~/.local/bin/baldurs-gate-3
and made executable with
chmod +x ~/.local/bin/baldurs-gate-3
, so you won’t
have to guess what may be missing:
#!/usr/bin/env bash # -*- mode:scheme -*- set -ex xhost +SI:localuser:"$USER" exec -a "$0" guix shell --verbosity=3 \ --container --network --emulate-fhs \ --preserve='^DISPLAY$' --preserve='^XAUTHORITY$' --preserve='^USER$' \ --preserve='^GDM.*$' --preserve='^PULSE_.*$' --preserve='^XDG.*$' \ --preserve='^DBUS_.*$' --preserve='^SHELL$' \ --expose=$XAUTHORITY --expose=/var/run/dbus --expose=/sys/class/input \ --expose=/dev/dri \ $(if test -e /dev/video0; then --expose=/dev/video0; fi) \ $(if test -e /dev/video1; then --expose=/dev/video1; fi) \ --expose=/etc/bashrc --expose=/sys/devices --expose=/sys/dev \ --expose=/sys/bus/pci --expose=/run/user/"$(id -u)"/pulse \ --expose="$HOME/Downloads" \ --share=/dev/snd/seq --share=/dev/shm --share=/proc \ --share=/var --share=/run --share=/tmp/ \ --share="$HOME/.local" --share="$HOME/.config" \ --manifest="$0" \ --share=/mnt/schatten/baldurs-gate --share="$HOME/.wine" \ -- bash -c ' cd "/mnt/schatten/baldurs-gate/Baldurs Gate 3/bin" && wine64 bg3.exe ' # )) !# ;; manifest Scheme code used by Guix thanks to ;; https://www.futurile.net/2023/04/29/guix-shell-virtual-environments-containers/ (use-modules (gnu) (guix inferior) (guix channels) (srfi srfi-1)) (define channels (list (channel (name 'guix) (url "https://git.savannah.gnu.org/git/guix.git") (commit ;; The commit with wine64@9 "c788962baeeb6bf021b30c9ad774d472f592fdb5")))) (define inferior (inferior-for-channels channels)) (packages->manifest (append (lookup-inferior-packages inferior "wine64") (append-map (lambda (spec) (cond ((pair? spec) (let ((pkg (car spec)) (output (cadr spec))) (if (string? pkg) (list (specification->package+output pkg output)) (list (values pkg output))))) ((string? spec) (list (specification->package spec))) (else (list spec)))) `("0ad" "abcde" "adanaxisgpl" "adb" "adwaita-icon-theme" "aha" "alsa-lib" "alsa-plugins" "amb-plugins" "anki" "ant" "apertium" "apr" "apr-util" "arandr" "arc-icon-theme" "arc-theme" "aria2" "ark" "aspell" "aspell-dict-de" "aspell-dict-en" "aspell-dict-eo" "aspell-dict-es" "atool" "audacity" "autoconf" "automake" "autotalent" "bash" "bastet" "bc" "bind" "binutils" "blender" "borg" "breeze-icons" "bzip2" "cabal-install" "cabextract" "calibre" "cdparanoia" "cdrtools" "chez-scheme" "clang" "cloc" "clojure" "cmake" "coreutils" "cryptsetup" "cups" "curl" "daikichi" "dav1d" "dbus" "dbus-c++" "dbus-glib" "dconf-editor" "ddrescue" "diffutils" "dlib" "docker" "docker-cli" "docker-compose" "dosfstools" "dunst" "ecryptfs-utils" "efibootmgr" "electrum" "emacs-ag" "emacs-auctex" "emacs-bbdb" "emacs-compat" "emacs-dart-mode" "emacs-dash" "emacs-dashboard" "emacs-deadgrep" "emacs-desktop-environment" "emacs-dumb-jump" "emacs-elfeed" "emacs-elfeed-org" "emacs-elpher" "emacs-emms" "emacs-exwm" "emacs-geiser-guile" "emacs-ghub" "emacs-helm-emms" "emacs-helm-exwm" "emacs-helm-firefox" "emacs-magit" "emacs-next" "emacs-org-contrib" "emacs-org-edit-latex" "emacs-ox-rss" "emacs-pdf-tools" "emacs-pinentry" "emacs-tldr" "emacs-transient" "emacs-typo" "emacs-use-package" "emacs-web-mode" "emacs-xref" "espeak" "espeak-ng" "evince" "expat" "fastboot" "fd" "fdisk" "fdupes" "festival" "fetchmail" "ffmpeg@6" "ffmpegthumbs" "fil-plugins" "file" "filelight" "firefox-decrypt" "flameshot" "flare-game" "flatpak" "flex" "folks" "font-abattis-cantarell" "font-adobe-source-code-pro" "font-adobe-source-han-sans" "font-adobe-source-sans-pro" "font-adobe-source-serif-pro" "font-adobe100dpi" "font-adobe75dpi" "font-alias" "font-anonymous-pro" "font-anonymous-pro-minus" "font-arabic-misc" "font-awesome" "font-bitstream-vera" "font-blackfoundry-inria" "font-cns11643-swjz" "font-comic-neue" "font-cronyx-cyrillic" "font-culmus" "font-dec-misc" "font-dejavu" "font-dosis" "font-dseg" "font-fantasque-sans" "font-fira-code" "font-fira-mono" "font-fira-sans" "font-fontna-yasashisa-antique" "font-ghostscript" "font-gnu-freefont" "font-gnu-unifont" "font-go" "font-google-material-design-icons" "font-google-noto" "font-google-roboto" "font-hack" "font-hermit" "font-ibm-plex" "font-inconsolata" "font-iosevka" "font-iosevka-aile" "font-iosevka-etoile" "font-iosevka-slab" "font-iosevka-term" "font-iosevka-term-slab" "font-ipa-mj-mincho" "font-isas-misc" "font-jetbrains-mono" "font-lato" "font-liberation" "font-linuxlibertine" "font-lohit" "font-mathjax" "font-meera-inimai" "font-micro-misc" "font-misc-cyrillic" "font-misc-ethiopic" "font-misc-misc" "font-mononoki" "font-mplus-testflight" "font-mutt-misc" "font-opendyslexic" "font-public-sans" "font-rachana" "font-sarasa-gothic" "font-schumacher-misc" "font-screen-cyrillic" "font-sil-andika" "font-sil-charis" "font-sil-gentium" "font-sony-misc" "font-sun-misc" "font-tamzen" "font-terminus" "font-tex-gyre" "font-un" "font-util" "font-vazir" "font-winitzki-cyrillic" "font-wqy-microhei" "font-wqy-zenhei" "font-xfree86-type1" "fontconfig" "foomatic-filters" "fortunes-jkirchartz" "freetype" "freexl" "fzf" "g2reverb" "gambit-c" "gcc-toolchain" "gdal" "gdb" "geos" "gettext" "ghc" "ghc-pandoc" "ghostscript" "ghostscript-with-cups" "ghostscript-with-x" "giflib" "gimp" "gimp-resynthesizer" "git" "git-lfs" "glib-networking" "glibc" "gmic" "gmic-qt-gimp" "gmtp" "gnome-chess" "gnome-screenshot" "gnome-shell-extension-appindicator" "gnome-shell-extension-clipboard-indicator" "gnome-shell-extension-dash-to-dock" "gnome-shell-extensions" "gnome-system-monitor" "gnome-themes-extra" "gnucash" "gnupg" "gnuplot" "go" "goaccess" "godot" "gpgme" "gpicview" "gpodder" "grafx2" "gramps" "graphicsmagick" "graphviz" "groff" "grpc" "gst-libav" "gst-plugins-bad" "gst-plugins-base" "gst-plugins-good" "gst-plugins-ugly" "gstreamer" "gtypist" "guile" "guile-charting" "guile-chickadee" "guile-colorized" "guile-dsv" "guile-fibers" "guile-gi" "guile-hoot" "guile-json" "guile-ncurses" "guile-opengl" "guile-pfds" "guile-readline" "guile-websocket" "guile-wisp" "guile-zstd" "gv" "gxtuner" "gzip" "haunt" "help2man" "higan" "hplip" "htop" "hyperrogue" "icecast" "icecat" "icoutils" "iftop" "imagemagick" "inkscape" "iotop" "java-junit" "java-tomcat" "jpegoptim" "jq" "json-c" "kawa" "kde-frameworkintegration" "kdeconnect" "kdenlive" "keepassxc" "keyutils" "kmag" "kodi-cli" "konsole" "krita" "ktouch" "kwindowsystem" "kxstitch" "ladspa" "lbzip2" "ledger" "less" "lftp" "lgogdownloader" "libaio" "libaom" "libavif" "libb2" "libcdio" "libffi" "libgccjit" "libgcrypt" "libgeotiff" "libglvnd" "libgsf" "libheif" "libidn" "libidn2" "libjpeg-turbo" "libltdl" "libnotify" "libpng" "libreoffice" "libsodium" "libsoup" "libspatialite" "libtiff" "libtool" "libusb" "libwebp" "libx11" "libxcb" "libxcomposite" "libxext" "libxkbcommon" "libxml2" "libxrandr" "libxslt" "libxt" "libxtst" "libyaml" "libzip" "lilypond" "lm-sensors" "lugaru" "lxqt-themes" "lynx" "maim" "make" "mate-themes" "mcp-plugins" "meld" "mercurial" "mlt" "mosh" "mp3splt" "mpg123" "mpg321" "mplayer" "mpv" "mrrescue" "mu" "mumble" "mumi" "mupdf" "mypaint" "nano" "nasm" "netcdf" "nethack" "nmap" "no-more-secrets" "node" "obs" "okular" "openal" "openconnect" "opencv" "openh264" "openshot" "openssh" "openssl" "opensurge" "orca" "oxygen-icons" "p7zip" "pango" "paperkey" "parallel" "parted" "password-store" "pavucontrol" "pcre" "pdfgrep" "pdfposter" "peek" "perl" "perl-net-dbus" "perl-x11-protocol" "pinentry" "pioneer" "pipewire" "pkg-config" "plantuml" "plasma-framework" "playonlinux" "pngcrush" "pngquant" "po4a" "poco" "poppler" "postgresql" "ppp" "procmail" "progress" "proj.4" "protonup-ng" "psutils" "pulseaudio" "pv" "python" "python-aiohttp" "python-aiorpcx" "python-ansi2html" "python-bz2file" "python-cython" "python-dateutil" "python-dbus" "python-defusedxml" "python-dulwich" "python-evdev" "python-hg-evolve" "python-libnacl" "python-matplotlib" "python-pip" "python-pyblake2" "python-pycairo" "python-pygame-sdl2" "python-pygments" "python-pylint" "python-pyqt" "python-pyyaml" "python-pyzmq" "python-qrcode" "python-scipy" "python-setuptools" "python-tldr" "python-virtualenv" "python-wheel" "qpwgraph" "quassel" "quaternion" "r" "r-readr" "r-slam" "r-wordcloud" "racket" "radare2" "readline" "recordmydesktop" "recutils" "redis" "redshift" "retroarch" "rev-plugins" "ripgrep" "rmlint" "rsync" "rtorrent" "ruby" "ruby-ffi" "ruby-rake" "rxvt-unicode" "s6-dns" "samba" "sane-backends" "sbcl" "screen" "screengrab" "scribus" "scrot" "scummvm" "sdl2" "secrets" "sed" "setxkbmap" "sfxr" "shotcut" "signal-desktop" "signing-party" "simple-scan" "smartmontools" "snapscreenshot" "spatialite-gui" "sshfs" "ssss" "ste-plugins" "steam" "strace" "subversion" "swig" "syncthing" "syncthing-gtk" "teeworlds" "tenacity" "texinfo" "texlive-adjustbox" "texlive-ae" "texlive-amsfonts" "texlive-anyfontsize" "texlive-baskervaldx" "texlive-baskervillef" "texlive-bibunits" "texlive-bin" "texlive-boisik" "texlive-bold-extra" "texlive-capt-of" "texlive-ccicons" "texlive-changepage" "texlive-charter" "texlive-chemfig" "texlive-cm-super" "texlive-comment" "texlive-contour" "texlive-dashrule" "texlive-drm" "texlive-ec" "texlive-enumitem" "texlive-environ" "texlive-epiolmec" "texlive-everypage" "texlive-fmtcount" "texlive-fontawesome" "texlive-fontinst" "texlive-fourier" "texlive-fpl" "texlive-hyphen-complete" "texlive-imakeidx" "texlive-inconsolata" "texlive-isodate" "texlive-iwona" "texlive-kantlipsum" "texlive-lastpage" "texlive-latex-fonts" "texlive-lh" "texlive-lipsum" "texlive-makecmds" "texlive-mathalpha" "texlive-mathpazo" "texlive-mdwtools" "texlive-metafont" "texlive-mflogo" "texlive-mflogo-font" "texlive-minted" "texlive-morefloats" "texlive-ms" "texlive-newtx" "texlive-newtxsf" "texlive-newunicodechar" "texlive-palatino" "texlive-placeins" "texlive-polyglossia" "texlive-pygmentex" "texlive-rsfs" "texlive-scheme-medium" "texlive-scholax" "texlive-shorttoc" "texlive-tcolorbox" "texlive-textpos" "texlive-threeparttable" "texlive-threeparttablex" "texlive-tikz-timing" "texlive-tikzposter" "texlive-times" "texlive-titlesec" "texlive-tocloft" "texlive-todonotes" "texlive-txfonts" "texlive-utfsym" "texlive-wallpaper" "texlive-worldflags" "texlive-wrapfig" "texlive-xurl" "texlive-zapfding" "the-silver-searcher" "time" "timidity++" "tinyscheme" "tipp10" "tome4" "toot" "tor" "torbrowser" "torsocks" "transmission" "trash-cli" "ttf2eot" "ttfautohint" "tuxpaint" "uget" "unzip" "util-linux" "v4l-utils" "vco-plugins" "virt-manager" "vlc" "wah-plugins" "wesnoth" "wget" "wine64" "wireplumber" "wireshark" "workrave" "wxwidgets" "x265" "xauth" "xcb-util" "xcb-util-image" "xcb-util-keysyms" "xclip" "xclock" "xdg-dbus-proxy" "xdg-desktop-portal-gtk" "xdg-user-dirs" "xdg-utils" "xdotool" "xev" "xfce4-clipman-plugin" "xfce4-pulseaudio-plugin" "xhost" "xinit" "xkbcomp" "xkeyboard-config" "xkill" "xlsfonts" "xmodmap" "xmoto" "xorg-server" "xournalpp" "xpdf" "xrandr" "xsane" "xset" "xterm" "yasm" "yt-dlp" "zenity" "zip" "zlib" ;; specific outputs ("alsa-plugins" "pulseaudio") ("bind" "utils") ("gcc-toolchain" "debug") ("openjdk" "jdk") ("zstd" "lib") ;; disabled for excessive build times: ;; "rust" ;; "rust-avif-serialize" ;; "rust-cbindgen" ;; ("rust" "cargo") ;; workaround to get gcc-lib: (,(@@ (gnu packages gcc) gcc) "lib") ;; "chromium-embedded-framework" ; libcef ;; "firefox" ;; "intel-vaapi-driver" ;specific to my set-up ;; "openjdk" ))))
And that’s it. A complete system in which just the wine64 package is pulled from an earlier, eternal version definition that’s bootstrapped from source code using a minimal seed.
Feel free to trim this down to what’s actually needed — I’m pretty
sure 10% of this manifest would suffice — and send it to me; I’ll
gladly update this article then. But for now I’m finally going back to
actually play BG3 :-)
.
Footnotes:
Limitation: This only works as long as Guix stays operational and keeps the shell
and --manifest
API stable.
I installed Baldur’s Gate 3 via lgogdownloader available via the nonguix channel.