feat(nix): improve flake & derivations

chore: set up project formatting
This commit is contained in:
2025-11-10 12:31:53 +01:00
parent 54c7fdcd1a
commit 8be65d0c7e
8 changed files with 464 additions and 245 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/.direnv
/target
/result
/result-*

127
flake.lock generated
View File

@@ -1,12 +1,62 @@
{
"nodes": {
"crane": {
"locked": {
"lastModified": 1762538466,
"narHash": "sha256-8zrIPl6J+wLm9MH5ksHcW7BUHo7jSNOu0/hA0ohOOaM=",
"owner": "ipetkov",
"repo": "crane",
"rev": "0cea393fffb39575c46b7a0318386467272182fe",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1762757174,
"narHash": "sha256-i2CZAiJNQsC7Wwk8fUZHS130W8HHLbmYqgT6ErYp5Zw=",
"owner": "nix-community",
"repo": "fenix",
"rev": "9ada5aa8ebd5062c8c399ae59c3f77f266216a24",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1757967192,
"narHash": "sha256-/aA9A/OBmnuOMgwfzdsXRusqzUpd8rQnQY8jtrHK+To=",
"lastModified": 1762596750,
"narHash": "sha256-rXXuz51Bq7DHBlfIjN7jO8Bu3du5TV+3DSADBX7/9YQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b6a8526db03f735b89dd5ff348f53f752e7ddc8e",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1762482733,
"narHash": "sha256-g/da4FzvckvbiZT075Sb1/YDNDr+tGQgh4N8i5ceYMg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0d7c15863b251a7a50265e57c1dca1a7add2e291",
"rev": "e1ebeec86b771e9d387dd02d82ffdc77ac753abc",
"type": "github"
},
"original": {
@@ -16,29 +66,62 @@
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"nixpkgs_3": {
"locked": {
"lastModified": 1757989933,
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
"lastModified": 1761236834,
"narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d5faa84122bc0a1fd5d378492efce4e289f8eac1",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"crane": "crane",
"fenix": "fenix",
"nixpkgs": "nixpkgs_2",
"treefmt-nix": "treefmt-nix"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1762722525,
"narHash": "sha256-cM1u88yehAW7S4Y4t7+fDHwmtXSOZUh24ELmAtYH37c=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "21f8445ea523e83cd4f11b0a67a3a5ced2b1f56f",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1762410071,
"narHash": "sha256-aF5fvoZeoXNPxT0bejFUBXeUjXfHLSL7g+mjR/p5TEg=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "97a30861b13c3731a84e09405414398fbf3e109f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}

327
flake.nix
View File

@@ -1,267 +1,148 @@
# This flake file is community maintained
{
description = "Niri: A scrollable-tiling Wayland compositor.";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# NOTE: This is not necessary for end users
# You can omit it with `inputs.rust-overlay.follows = ""`
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
fenix.url = "github:nix-community/fenix";
treefmt-nix.url = "github:numtide/treefmt-nix";
crane.url = "github:ipetkov/crane";
};
outputs =
{
self,
nixpkgs,
rust-overlay,
treefmt-nix,
fenix,
crane,
}:
let
niri-package =
{
lib,
cairo,
dbus,
libGL,
libdisplay-info,
libinput,
seatd,
libxkbcommon,
libgbm,
pango,
pipewire,
pkg-config,
rustPlatform,
systemd,
wayland,
installShellFiles,
withDbus ? true,
withSystemd ? true,
withScreencastSupport ? true,
withDinit ? false,
}:
rustPlatform.buildRustPackage {
pname = "niri";
version = self.shortRev or self.dirtyShortRev or "unknown";
src = lib.fileset.toSource {
root = ./.;
fileset = lib.fileset.unions [
./niri-config
./niri-ipc
./niri-visual-tests
./resources
./src
./Cargo.toml
./Cargo.lock
];
};
postPatch = ''
patchShebangs resources/niri-session
substituteInPlace resources/niri.service \
--replace-fail '/usr/bin' "$out/bin"
'';
cargoLock = {
# NOTE: This is only used for Git dependencies
allowBuiltinFetchGit = true;
lockFile = ./Cargo.lock;
};
strictDeps = true;
nativeBuildInputs = [
rustPlatform.bindgenHook
pkg-config
installShellFiles
];
buildInputs =
[
cairo
dbus
libGL
libdisplay-info
libinput
seatd
libxkbcommon
libgbm
pango
wayland
]
++ lib.optional (withDbus || withScreencastSupport || withSystemd) dbus
++ lib.optional withScreencastSupport pipewire
# Also includes libudev
++ lib.optional withSystemd systemd;
buildFeatures =
lib.optional withDbus "dbus"
++ lib.optional withDinit "dinit"
++ lib.optional withScreencastSupport "xdp-gnome-screencast"
++ lib.optional withSystemd "systemd";
buildNoDefaultFeatures = true;
# ever since this commit:
# https://github.com/YaLTeR/niri/commit/771ea1e81557ffe7af9cbdbec161601575b64d81
# niri now runs an actual instance of the real compositor (with a mock backend) during tests
# and thus creates a real socket file in the runtime dir.
# this is fine for our build, we just need to make sure it has a directory to write to.
preCheck = ''
export XDG_RUNTIME_DIR="$(mktemp -d)"
'';
checkFlags = [
# These tests require the ability to access a "valid EGL Display", but that won't work
# inside the Nix sandbox
"--skip=::egl"
];
postInstall =
''
installShellCompletion --cmd niri \
--bash <($out/bin/niri completions bash) \
--fish <($out/bin/niri completions fish) \
--nushell <($out/bin/niri completions nushell) \
--zsh <($out/bin/niri completions zsh)
install -Dm644 resources/niri.desktop -t $out/share/wayland-sessions
install -Dm644 resources/niri-portals.conf -t $out/share/xdg-desktop-portal
''
+ lib.optionalString withSystemd ''
install -Dm755 resources/niri-session $out/bin/niri-session
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/share/systemd/user
'';
env = {
# Force linking with libEGL and libwayland-client
# so they can be discovered by `dlopen()`
RUSTFLAGS = toString (
map (arg: "-C link-arg=" + arg) [
"-Wl,--push-state,--no-as-needed"
"-lEGL"
"-lwayland-client"
"-Wl,--pop-state"
]
);
};
passthru = {
providedSessions = [ "niri" ];
};
meta = {
description = "Scrollable-tiling Wayland compositor";
homepage = "https://github.com/YaLTeR/niri";
license = lib.licenses.gpl3Only;
mainProgram = "niri";
platforms = lib.platforms.linux;
};
};
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
];
inherit (nixpkgs) lib;
# Support all Linux systems that the nixpkgs flake exposes
systems = lib.intersectLists lib.systems.flakeExposed lib.platforms.linux;
forAllSystems = lib.genAttrs systems;
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
forEachSupportedSystem =
f:
lib.genAttrs supportedSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
self.overlays.default
];
};
ourPackages = lib.filterAttrs (_: v: (v ? niriPackage)) pkgs.niriPackages;
treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix;
treefmt = treefmtEval.config.build.wrapper;
in
f {
inherit
crane
fenix
ourPackages
pkgs
system
treefmt
treefmtEval
;
}
);
in
{
checks = forAllSystems (system: {
# We use the debug build here to save a bit of time
inherit (self.packages.${system}) niri-debug;
});
formatter = forEachSupportedSystem ({ treefmt, ... }: treefmt);
devShells = forAllSystems (
system:
checks = forEachSupportedSystem (
{
pkgs,
treefmtEval,
ourPackages,
...
}:
let
pkgs = nixpkgsFor.${system};
rust-bin = rust-overlay.lib.mkRustBin { } pkgs;
inherit (self.packages.${system}) niri;
testsFrom =
pkg:
pkgs.lib.mapAttrs' (name: value: {
name = "${pkg.pname}-${name}";
inherit value;
}) (pkg.passthru.tests or { });
ourTests = pkgs.lib.foldlAttrs (
acc: name: value:
acc // (testsFrom value)
) { } ourPackages;
in
ourTests
// {
treefmt = treefmtEval.config.build.check self;
}
);
devShells = forEachSupportedSystem (
{
pkgs,
ourPackages,
treefmt,
...
}:
let
ourBuildInputs = lib.unique (
lib.foldlAttrs (
acc: _: v:
acc ++ (v.buildInputs or [ ]) ++ (v.nativeBuildInputs or [ ])
) [ ] ourPackages
);
in
{
default = pkgs.mkShell {
packages = [
# We don't use the toolchain from nixpkgs
# because we prefer a nightly toolchain
# and we *require* a nightly rustfmt
(rust-bin.selectLatestNightlyWith (
toolchain:
toolchain.default.override {
extensions = [
# includes already:
# rustc
# cargo
# rust-std
# rust-docs
# rustfmt-preview
# clippy-preview
"rust-analyzer"
"rust-src"
];
}
))
pkgs.cargo-insta
];
inputsFrom = builtins.attrValues ourPackages;
nativeBuildInputs = [
packages = [
pkgs.rustPlatform.bindgenHook
pkgs.pkg-config
pkgs.wrapGAppsHook4 # For `niri-visual-tests`
pkgs.cargo-insta
treefmt
];
buildInputs = niri.buildInputs ++ [
buildInputs = [
pkgs.libadwaita # For `niri-visual-tests`
];
env = {
# WARN: Do not overwrite this variable in your shell!
# It is required for `dlopen()` to work on some libraries; see the comment
# in the package expression
#
# This should only be set with `CARGO_BUILD_RUSTFLAGS="$CARGO_BUILD_RUSTFLAGS -C your-flags"`
CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;
LD_LIBRARY_PATH = builtins.concatStringsSep ":" (
map (e: "${e.lib or e.out}/lib") (
ourBuildInputs
++ [
pkgs.glib
pkgs.pixman
]
)
);
};
};
}
);
formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
packages = forAllSystems (
system:
let
niri = nixpkgsFor.${system}.callPackage niri-package { };
in
{
inherit niri;
# NOTE: This is for development purposes only
#
# It is primarily to help with quickly iterating on
# changes made to the above expression - though it is
# also not stripped in order to better debug niri itself
niri-debug = niri.overrideAttrs (
newAttrs: oldAttrs: {
pname = oldAttrs.pname + "-debug";
cargoBuildType = "debug";
cargoCheckType = newAttrs.cargoBuildType;
dontStrip = true;
}
);
default = niri;
}
);
packages = forEachSupportedSystem ({ ourPackages, ... }: ourPackages);
overlays.default = final: _: {
niri = final.callPackage niri-package { };
niriPackages = final.callPackage ./scope.nix {
inherit
crane
fenix
self
;
};
};
};
}

202
nix/pkgs/niri/default.nix Normal file
View File

@@ -0,0 +1,202 @@
{
pkgs,
autoPatchelfHook,
cairo,
craneLib,
dbus,
glib,
installShellFiles,
lib,
libGL,
libdisplay-info,
libgbm,
libinput,
libxkbcommon,
pango,
pipewire,
pixman,
pkg-config,
rustPlatform,
seatd,
stdenv,
systemd,
wayland,
withDbus ? true,
withDinit ? false,
withScreencastSupport ? true,
withSystemd ? true,
}:
let
cargoToml = builtins.fromTOML (builtins.readFile ../../../Cargo.toml);
mkToolchain =
p:
let
targetMap = {
aarch64-linux = "aarch64-unknown-linux-gnu";
x86_64-linux = "x86_64-unknown-linux-gnu";
};
target-toolchain = p.niriPackages.fenix.targets.${targetMap.${stdenv.hostPlatform.system}}.stable;
toolchain = p.niriPackages.fenix.stable;
in
p.niriPackages.fenix.combine [
toolchain.rustc
toolchain.cargo
toolchain.rust-src
toolchain.rustfmt
toolchain.clippy
target-toolchain.rust-std
];
craneLib' = craneLib.overrideToolchain mkToolchain;
craneArgs =
let
buildFeatures = builtins.concatStringsSep "," (
lib.optional withDbus "dbus"
++ lib.optional withDinit "dinit"
++ lib.optional withScreencastSupport "xdp-gnome-screencast"
++ lib.optional withSystemd "systemd"
);
in
{
nativeBuildInputs = [
autoPatchelfHook
installShellFiles
pkg-config
rustPlatform.bindgenHook
];
runtimeDependencies = [
libGL
wayland
];
src =
let
niriFilter = path: type: builtins.match ".*niri-source/(resources|src).*" path != null;
srcFilter = path: type: (niriFilter path type) || (craneLib'.filterCargoSources path type);
in
lib.cleanSourceWith {
src = builtins.path {
path = ../../../.;
name = "niri-source";
};
filter = srcFilter;
name = "source";
};
# ever since this commit:
# https://github.com/YaLTeR/niri/commit/771ea1e81557ffe7af9cbdbec161601575b64d81
# niri now runs an actual instance of the real compositor (with a mock backend) during tests
# and thus creates a real socket file in the runtime dir.
# this is fine for our build, we just need to make sure it has a directory to write to.
preCheck = ''
export XDG_RUNTIME_DIR="$(mktemp -d)"
export LD_LIBRARY_PATH=${
builtins.concatStringsSep ":" (
map (e: "${e.lib or e.out}/lib") (
craneArgs.buildInputs
++ [
glib
pixman
]
)
)
}
'';
buildInputs = [
cairo
dbus
libGL
libdisplay-info
libinput
seatd
libxkbcommon
libgbm
pango
wayland
]
++ lib.optional (withDbus || withScreencastSupport || withSystemd) dbus
++ lib.optional withScreencastSupport pipewire
# Also includes libudev
++ lib.optional withSystemd systemd;
cargoExtraArgs =
"--locked --no-default-features"
+ (lib.optionalString (buildFeatures != "") " --features ${buildFeatures}");
# These tests require the ability to access a "valid EGL Display", but that won't work
# inside the Nix sandbox
cargoTestExtraArgs = "-- --skip=::egl";
strictDeps = true;
env = {
RUSTFLAGS = "-Dwarnings";
};
};
craneBuildArgs = craneArgs // {
inherit cargoArtifacts;
};
cargoArtifacts = craneLib'.buildDepsOnly craneArgs;
in
craneLib'.buildPackage (
craneBuildArgs
// {
pname = cargoToml.package.name;
version = cargoToml.workspace.package.version;
postPatch = ''
patchShebangs resources/niri-session
substituteInPlace resources/niri.service \
--replace-fail '/usr/bin' "$out/bin"
'';
postInstall = ''
install -Dm644 resources/niri.desktop -t $out/share/wayland-sessions
install -Dm644 resources/niri-portals.conf -t $out/share/xdg-desktop-portal
''
+ lib.optionalString withSystemd ''
install -Dm755 resources/niri-session $out/bin/niri-session
install -Dm644 resources/niri{.service,-shutdown.target} -t $out/share/systemd/user
'';
postFixup = ''
autoPatchelf $out/bin
installShellCompletion --cmd niri \
--bash <($out/bin/niri completions bash) \
--fish <($out/bin/niri completions fish) \
--nushell <($out/bin/niri completions nushell) \
--zsh <($out/bin/niri completions zsh)
'';
passthru = {
rustToolchain = mkToolchain pkgs;
providedSessions = [ "niri" ];
tests = {
cargo-test = craneLib'.cargoTest craneBuildArgs;
cargo-clippy = craneLib'.cargoClippy craneBuildArgs;
};
};
meta = {
description = "Scrollable-tiling Wayland compositor";
homepage = "https://github.com/YaLTeR/niri";
license = lib.licenses.gpl3Only;
mainProgram = "niri";
platforms = lib.platforms.linux;
maintainers = [
lib.maintainers.naxdy
];
};
}
)

22
scope.nix Normal file
View File

@@ -0,0 +1,22 @@
{
crane,
fenix,
generateSplicesForMkScope,
makeScopeWithSplicing',
self,
}:
makeScopeWithSplicing' {
otherSplices = generateSplicesForMkScope "niriPackages";
extra = final: {
inherit self;
craneLib = crane.mkLib final;
};
f = final: {
fenix = final.callPackage fenix { };
callPackage' = pkg: attrs: (final.callPackage pkg attrs) // { niriPackage = true; };
niri = final.callPackage' ./nix/pkgs/niri { };
};
}

25
treefmt.nix Normal file
View File

@@ -0,0 +1,25 @@
{ pkgs, ... }:
let
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
in
{
programs.nixfmt.enable = true;
programs.taplo.enable = true;
programs.rustfmt = {
enable = true;
package = pkgs.niriPackages.niri.passthru.rustToolchain;
edition = cargoToml.workspace.package.edition;
};
programs.typos = {
enable = true;
configFile = "${./typos.toml}";
includes = [
"*.md"
"*.nix"
"*.rs"
];
};
}

View File

@@ -1,2 +1,5 @@
[default.extend-words]
datas = "datas"
arange = "arange"
localed = "localed"
WRONLY = "WRONLY"