Add Nix flake: crane + fenix build, treefmt-nix formatting

Uses flake-parts to organize outputs, with nix/rust.nix defining a
single craneLib/toolchain/commonArgs environment shared by the package
build (nix/rust.nix) and the dev shell (nix/devshell.nix), so both stay
close to identical. Deps are cached separately from the crate itself via
crane's buildDepsOnly, giving incremental rebuilds. Checks cover clippy,
rustfmt, cargo-doc, cargo-audit, and nextest; treefmt-nix wires up
`nix fmt` for Rust/Nix/TOML files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ALEXkc7pTro7tF1WcUuKb
This commit is contained in:
2026-08-10 23:58:00 +08:00
parent 979e77571f
commit 840cea1f10
6 changed files with 393 additions and 0 deletions
+4
View File
@@ -1 +1,5 @@
/target
/result
/result-*
.direnv/
Generated
+154
View File
@@ -0,0 +1,154 @@
{
"nodes": {
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1786376034,
"narHash": "sha256-lgVynYub6uIkZjCFosXWXPCELrtpo8vXbMkiyLWtS7s=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "a27ab8501d9635749817b6d7070e6e241686c98f",
"type": "github"
},
"original": {
"owner": "rustsec",
"repo": "advisory-db",
"type": "github"
}
},
"crane": {
"locked": {
"lastModified": 1785782307,
"narHash": "sha256-MPaRdVkf6zZP5fCPxYCi8Dr4pZzgmXzg8T9nVEbp3Mw=",
"owner": "ipetkov",
"repo": "crane",
"rev": "2c71e194474d13de031d729b729c968ddbe3507f",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1786348189,
"narHash": "sha256-tnTpFky6/GeXVMBO+hLJK2jWS4J8TYloL+rqRME9gAk=",
"owner": "nix-community",
"repo": "fenix",
"rev": "a0b1aef63d7e9c5568958e0d90f6fe48f0f4a7a2",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1785627969,
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1786098110,
"narHash": "sha256-shi1tjDhCGGd0kIgVPNY03V8NBWSTzhMSFDb7IqSoec=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "afb4584a80bbf779ce0f691509ff902d188c2b3d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1785031560,
"narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"advisory-db": "advisory-db",
"crane": "crane",
"fenix": "fenix",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1786285255,
"narHash": "sha256-zJukKxGKLxcO7E6+P49Qe6XaSbbyGG44kFvQYLWaD3M=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "57411b3a0fb0181ed5965f6541d5ed39a9d79d4c",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1785945821,
"narHash": "sha256-NLSyTCW4K4ofhNBllt3omPasm6QpralXH1DBZOc91Dw=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "ae7910970dddc408fe6ab1c8e4b277bb21d72dc0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+80
View File
@@ -0,0 +1,80 @@
{
description = "rust_template Rust project built with crane + fenix, formatted with treefmt";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
crane.url = "github:ipetkov/crane";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
advisory-db = {
url = "github:rustsec/advisory-db";
flake = false;
};
};
nixConfig = {
extra-substituters = [ "https://nix-community.cachix.org" ];
extra-trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
outputs =
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
imports = [
inputs.treefmt-nix.flakeModule
./nix/treefmt.nix
];
perSystem =
{
pkgs,
system,
lib,
...
}:
let
rust = import ./nix/rust.nix {
inherit
inputs
pkgs
system
lib
;
};
in
{
packages.default = rust.package;
checks = rust.checks // {
rust_template = rust.package;
};
apps.default = {
type = "app";
program = "${rust.package}/bin/rust_template";
};
devShells.default = import ./nix/devshell.nix { inherit pkgs lib rust; };
};
};
}
+38
View File
@@ -0,0 +1,38 @@
# Dev shell built from the same craneLib/toolchain as the package build
# (nix/rust.nix), so the environment developers see locally matches what
# `nix build` uses as closely as possible.
{
pkgs,
lib,
rust,
}:
rust.craneLib.devShell {
inherit (rust) checks;
packages =
with pkgs;
[
cargo-nextest
cargo-criterion
cargo-audit
cargo-expand
taplo
bacon
]
++ lib.optionals pkgs.stdenv.isLinux [ mold ]
++ lib.optionals pkgs.stdenv.isDarwin [
libiconv
darwin.apple_sdk.frameworks.Security
darwin.apple_sdk.frameworks.SystemConfiguration
];
shellHook = ''
export RUST_SRC_PATH="${rust.rustToolchain}/lib/rustlib/src/rust/library"
''
+ lib.optionalString pkgs.stdenv.isLinux ''
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER="clang"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-fuse-ld=mold"
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="clang"
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-fuse-ld=mold"
'';
}
+103
View File
@@ -0,0 +1,103 @@
# Shared Rust build environment: toolchain, craneLib, source filtering,
# and cached dependency artifacts. Consumed by both the package build
# (nix/package.nix, implicit below) and the dev shell (nix/devshell.nix)
# so the two stay close to identical.
{
inputs,
pkgs,
system,
lib,
}:
let
fenixPkgs = inputs.fenix.packages.${system};
rustToolchain = fenixPkgs.stable.withComponents [
"cargo"
"clippy"
"llvm-tools"
"rust-analyzer"
"rust-src"
"rustc"
"rustfmt"
];
craneLib = (inputs.crane.mkLib pkgs).overrideToolchain (_: rustToolchain);
src = craneLib.cleanCargoSource ./..;
commonArgs = {
inherit src;
strictDeps = true;
nativeBuildInputs = [
pkgs.pkg-config
]
++ lib.optionals pkgs.stdenv.isLinux [
pkgs.clang
pkgs.mold
];
buildInputs = lib.optionals pkgs.stdenv.isDarwin [
pkgs.libiconv
pkgs.darwin.apple_sdk.frameworks.Security
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
];
};
# Built once, reused by the package build, clippy, doc, and nextest —
# this is what makes incremental rebuilds fast under crane.
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
package = craneLib.buildPackage (
commonArgs
// {
inherit cargoArtifacts;
pname = "rust_template";
doCheck = false; # exercised separately via checks.nextest
}
);
checks = {
clippy = craneLib.cargoClippy (
commonArgs
// {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
}
);
doc = craneLib.cargoDoc (
commonArgs
// {
inherit cargoArtifacts;
env.RUSTDOCFLAGS = "--deny warnings";
}
);
fmt = craneLib.cargoFmt { inherit src; };
audit = craneLib.cargoAudit {
inherit src;
inherit (inputs) advisory-db;
};
nextest = craneLib.cargoNextest (
commonArgs
// {
inherit cargoArtifacts;
partitions = 1;
partitionType = "count";
}
);
};
in
{
inherit
craneLib
commonArgs
cargoArtifacts
package
checks
rustToolchain
;
}
+14
View File
@@ -0,0 +1,14 @@
# treefmt-nix module: wires up `nix fmt` and the `formatting` check.
_: {
perSystem = _: {
treefmt = {
projectRootFile = "flake.nix";
programs.rustfmt.enable = true;
programs.nixfmt.enable = true;
programs.deadnix.enable = true;
programs.statix.enable = true;
programs.taplo.enable = true;
};
};
}