jevsnes.git / flake.nix
1{
2  description = "jev plays Zelda — a Rust app that hosts a SNES core and lets Jev (TypeSafe AI) play A Link to the Past, natively and as WASM";
3
4  inputs = {
5    # Same branch as ~/config, and locked to the same rev, so Mesen and
6    # ffmpeg-full are the store paths the system already has rather than a
7    # second 600 MB copy.
8    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

The Rust toolchain comes from fenix, not nixpkgs: the emulator core (jgenesis' snes-core) uses float_algebraic, stabilised after the rustc 1.95 nixos-26.05 carried — measured 2026-09-20, 16 E0658 errors on 1.95, clean on 1.98.1. fenix's stable is the release channel, i.e. the newest released compiler; nix flake update fenix moves it forward.

16    fenix = {
17      url = "github:nix-community/fenix";
18      inputs.nixpkgs.follows = "nixpkgs";
19    };

The estate package set, for buck2 (see the devshell).

22    nix-pkgs.url = "git+ssh://git@github.com/deizel/nix-pkgs";
23    nix-pkgs.inputs.nixpkgs.follows = "nixpkgs";
24  };
26  outputs =
27    {
28      nixpkgs,
29      fenix,
30      nix-pkgs,
31      ...
32    }:
33    let
34      system = "x86_64-linux";
35      pkgs = nixpkgs.legacyPackages.${system};
36      rust = fenix.packages.${system};

One toolchain for both outputs of the app: the native window and the browser build. wasm32-unknown-unknown needs only its rust-std — the linker for it (rust-lld) ships inside rustc.

41      rustToolchain = rust.combine [
42        rust.stable.rustc
43        rust.stable.cargo
44        rust.stable.clippy
45        rust.stable.rustfmt
46        rust.stable.rust-src
47        rust.stable.rust-analyzer
48        rust.targets.wasm32-unknown-unknown.stable.rust-std
49      ];
51      mesen = pkgs.callPackage ./nix/mesen.nix {
52        enforce = {
53          Video.UseSoftwareRenderer = true;
54          Debug.ScriptWindow = {
55            AllowIoOsAccess = true;
56            AllowNetworkAccess = true;
57          };
58        };
59      };
60    in
61    {
62      packages.${system}.mesen = mesen;
63
64      devShells.${system}.default = pkgs.mkShell {
65        packages = [
66          rustToolchain

The build: buck2 drives it, reindeer turns Cargo.lock into the third-party BUCK targets. nix-pkgs' buck2, not nixpkgs': the 2026-08-22 binary drops its forkserver under test load (fixed in facebook/buck2 1cc3e05f85, released 2026-09-15); the nix-pkgs package header has the account.

73          nix-pkgs.packages.${system}.buck2
74          pkgs.reindeer

buck2's system cxx toolchain looks up plain clang/clang++ by name and links with -fuse-ld=lld; without both, every Rust binary and build script fails at the link step. Same wall ~/dashboard and ~/mobo hit.

80          pkgs.clang
81          pkgs.lld

cpal compiles its ALSA host on every Linux build, and alsa-sys's build script finds libasound through pkg-config or panics.

85          pkgs.pkg-config
86          pkgs.alsa-lib

Mesen 2: the reference emulator, for checking our core's RAM and picture against a known-good one.

90          mesen

Capture. -full, not plain ffmpeg: only -full is built with XCB, so only it has the x11grab input device that grabs a WSLg window by id. xwininfo is what finds that id.

95          pkgs.ffmpeg-full
96          pkgs.xwininfo

ROM archives arrive as .7z / .zip.

99          pkgs.p7zip
100          pkgs.unzip
102          pkgs.jq

The zbanks C bot (third-party/c) was written to run under gdb: its assert_bp is an int3 breakpoint. gdb is how its static state (targets, goal scores) is read mid-run.

107          pkgs.gdb

Poking the Jev HTTP API by hand.

110          pkgs.curl

Linting tools/*.sh - code-bash.md requires shellcheck-clean.

113          pkgs.shellcheck

"Which GPU did I actually get": glxinfo/eglinfo and vulkaninfo.

116          pkgs.mesa-demos
117          pkgs.vulkan-tools
118        ];

Reaching the GPU under WSL. wsl.useWindowsDriver links the D3D12 libraries into /run/opengl-driver/lib and stops there: Mesa dlopens libd3d12.so / libdxcore.so by bare name with no runpath to that directory, and with no DRM node it never picks d3d12 unprompted. Without these two, GL, EGL and Vulkan all report llvmpipe; with them, all three report the RTX 3080 (measured 2026-09-20). Vulkan (Dozen) needs only the library path, GL needs both — and GALLIUM_DRIVER without the path is worse than neither: GL programs fail to start.

The rest of the path is for our own window. winit, wgpu and xkbcommon-dl link none of their system libraries: they dlopen them by bare name at startup, so a Nix-built binary finds nothing unless the directories are on the search path.

alsa-lib is the one that IS linked (cpal's ALSA host, a DT_NEEDED on libasound.so.2), but buck2's link writes no runpath to the store, so the loader still has to be told where it is: without it the binary dies before main with "libasound.so.2: cannot open shared object file" (measured 2026-09-20).

140        GALLIUM_DRIVER = "d3d12";
141        LD_LIBRARY_PATH = "/run/opengl-driver/lib:" + pkgs.lib.makeLibraryPath [
142          pkgs.alsa-lib
143          pkgs.wayland
144          pkgs.libxkbcommon
145          pkgs.vulkan-loader
146          pkgs.libGL
147          pkgs.libx11
148          pkgs.libxcursor
149          pkgs.libxi
150          pkgs.libxrandr
151        ];
152      };
153    };
154}