diff --git a/.gitignore b/.gitignore index c6d90c5..e85e355 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,9 @@ # the .iss script and .ico are the tracked sources. /packaging/windows/peerspeak.exe /packaging/windows/output/ + +# Nix: the symlink `nix build` drops, and direnv's local cache. flake.nix and +# flake.lock ARE tracked — the lock is what pins the toolchain. +/result +/result-* +/.direnv/ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d339b4d --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1785989512, + "narHash": "sha256-HFQhkQcl5D1hUNoen3SGHCSFCt2Bg6uP+HgbrnA3InQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "445d861c6d31b4af0c79d8d4be2331f762a361d7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-26.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..87ab119 --- /dev/null +++ b/flake.nix @@ -0,0 +1,134 @@ +{ + description = "PeerSpeak — decentralized P2P voice chat (Rust/iroh/PipeWire/Opus/iced)"; + + # Pinned to the same channel the hosts run (nixos-config tracks nixos-26.05), + # so the libraries this shell links and dlopens are built against the same + # release as the PipeWire daemon and Vulkan ICD actually running on the + # machine. Floating to unstable here would reintroduce precisely the + # client/server version skew the pin exists to prevent. + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05"; + + outputs = + { nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + + # Libraries that iced/winit/wgpu open with dlopen at RUNTIME rather than + # linking at build time. Because nothing links them, they never land in + # the binary's rpath — under `cargo run` the loader finds them only + # through LD_LIBRARY_PATH. Leaving them out builds fine and then panics + # at window creation, which is a genuinely confusing failure, so they are + # listed explicitly instead of discovered the hard way. + runtimeLibs = with pkgs; [ + vulkan-loader # wgpu's Vulkan backend (iced's renderer) + libxkbcommon # winit keyboard handling + wayland # wayland-sys, dlopen'd on a Wayland session + libx11 # x11-dl, dlopen'd on the X11 fallback path + libxcursor + libxrandr + libxi + ]; + + # Screen sharing spawns pixelpass as a CHILD PROCESS, and pixelpass in + # turn drives GStreamer as a subprocess. That makes these tools a + # dependency of peerspeak's own test suite, not just of pixelpass: + # `tests/screenshare_host_fault.rs` starts a real pixelpass host, which + # aborts at its preflight if gst-launch-1.0 is missing. + # + # Deliberately duplicated from pixelpass's flake rather than importing it + # as an input. The two projects are mutually optional by design — neither + # is a dependency of the other, and the coupling is a runtime subprocess + # contract. Making one flake consume the other would quietly reintroduce + # exactly the build-level dependency that rule exists to prevent. + screenshareTools = with pkgs; [ + gst_all_1.gstreamer + gst_all_1.gst-plugins-base + gst_all_1.gst-plugins-good + gst_all_1.gst-plugins-bad + gst_all_1.gst-plugins-ugly + gst_all_1.gst-libav + pipewire # pipewiresrc (Wayland capture; ships in this pkg) + ]; + in + { + devShells.${system}.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + rustc + cargo + rustfmt + clippy + + # The supply-chain gates .gitea/workflows/ci.yml runs, so the same + # checks are reproducible locally before a push. These were `cargo + # install`ed on the CachyOS side, which does not carry over — those + # binaries link that distro's glibc and will not run here. + # cargo-deny reads deny.toml; cargo-audit reads .cargo/audit.toml. + cargo-audit + cargo-deny + # Debian packaging (`cargo deb --no-build`). Note the .deb itself + # should still be built inside a Debian/Ubuntu distrobox so the + # binary links that distro's glibc — see the packaging notes in + # Cargo.toml. + cargo-deb + + pkg-config + + # pipewire-sys and libspa-sys generate their bindings with bindgen, + # which needs a real libclang present at build time. + clang + + # audiopus_sys prefers the system libopus via pkg-config but falls + # back to a vendored CMake build; cmake keeps that fallback working + # rather than failing obscurely inside a build script. + cmake + + # build.rs shells out to `git rev-parse --short=8 HEAD` to stamp + # PEERSPEAK_GIT_SHORT into the binary (surfaced in Settings). + git + ] + ++ screenshareTools + ++ [ + pkgs.pulseaudio # `pactl`, used by pixelpass's audio routing + pkgs.mpv # the screen-share viewer + ]; + + buildInputs = + with pkgs; + [ + alsa-lib # alsa-sys, pulled in by rodio/cpal + libopus # audiopus_sys, linked dynamically + pipewire # pipewire-sys + libspa-sys: the Linux audio backend + ] + ++ runtimeLibs; + + # bindgen finds libclang through this variable specifically — having + # clang on PATH is not sufficient. + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs; + + # NixOS keeps every GStreamer plugin in its own store path, so the + # gst-launch-1.0 that pixelpass spawns discovers them ONLY through this + # search path. Same reasoning as hosts/darp5 and hosts/cazen in + # nixos-config. + GST_PLUGIN_SYSTEM_PATH_1_0 = + pkgs.lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" screenshareTools; + + # Only greet an interactive shell. shellHook also runs under + # `nix develop --command …`, where printing this would interleave the + # banner with the command's own output. + shellHook = '' + if [ -t 1 ]; then + echo "peerspeak — rustc $(rustc --version | cut -d' ' -f2) / cargo $(cargo --version | cut -d' ' -f2)" + echo " cargo build --release build" + echo " cargo test lib tests" + echo " cargo clippy --all-targets -- -D warnings lint" + echo + echo "Screen sharing spawns pixelpass as a child process — it must be" + echo "on PATH. Build it from ../pixelpass and add its target/release." + fi + ''; + }; + }; +}