Skip to content

Nix で作る Rust プロジェクト用の開発環境

{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
git-hooks-nix = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
treefmt-nix.url = "github:numtide/treefmt-nix";
crane.url = "github:ipetkov/crane";
};
outputs =
inputs@{ self, flake-parts, crane, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
flake = { };
imports = [
inputs.treefmt-nix.flakeModule
inputs.git-hooks-nix.flakeModule
];
systems = [
"aarch64-linux"
"x86_64-linux"
];
perSystem = { config, system, pkgs, lib, self, ... }:
let
rust-bin = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
craneLib = (crane.mkLib pkgs).overrideToolchain rust-bin;
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = with pkgs; [ ];
nativeBuildInputs = with pkgs; [ ];
};
cargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "deps";
}
);
in
{
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [
inputs.rust-overlay.overlays.default
];
};
treefmt = {
projectRootFile = "flake.nix";
programs.actionlint.enable = true;
programs.nixfmt.enable = true;
programs.rustfmt.enable = true;
programs.taplo.enable = true;
programs.yamlfmt.enable = true;
};
pre-commit = {
settings = {
hooks = {
flake-treefmt = {
enable = true;
name = "flake-treefmt";
entry = lib.getExe config.treefmt.build.wrapper;
pass_filenames = false;
};
clippy.enable = true;
cargo-check.enable = true;
};
settings.rust.check.cargoDeps = pkgs.rustPlatform.importCargoLock {
lockFile = ./Cargo.lock;
};
};
};
packages.default = craneLib.buildPackage (
commonArgs
// {
inherit cargoArtifacts;
pname = "template"; # TODO: rename
version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version;
}
);
devShells.default = pkgs.mkShell {
inputsFrom = [ config.pre-commit.devShell ];
buildInputs = with pkgs; [
cargo-expand
cargo-nextest
rust-bin
];
};
};
};
}

inputs(flake の入力部分)で以下のようなものを取得しています。

  • nixpkgs
  • flake-parts NixOS の設定などで使われている Module System を flake にも統合してくれます。またいい感じにクロスプラットフォームに出来ます(この機能は自前の関数で簡単に実装できたり、もっとシンプルなやつもある)
  • rust-overlay rustc や cargo の nixpkgs とは違うバージョンを提供してくれる flake です。nixpkgs で保守される rustc や cargo は最新版のみなので使用します
  • git-hooks-nix Pre Commit の Git Hooks と Nix プロジェクトをいい感じに統合してくれます
  • treefmt-nix treefmt という複数のフォーマッターをまとめて実行できるツールを Nix に統合してくれます
  • crane Rust のプロジェクトをビルドするのに便利な機能などを提供してくれます

どちらかというと重要なのは inputs よりもこちらです。

systems で perSystem 部分に記述した Atribute Set の生成先を指定します。例として、以下のような Nix 式は

{
outputs =
inputs@{
self,
flake-parts,
...
}:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"aarch64-linux"
"x86_64-linux"
];
perSystem = { ... }: {
hello = "world";
};
};
}

このようになります(多分実際に試すと hello なんてないよみたいな感じで怒られます)。

{
outputs = {
hello.aarch64-linux = "world";
hello.x86_64-linux = "world";
};
}

rust-toolchain.toml から Rust の Derivation( rust-bin )を生成してそこから craneLib を作成します。 それを利用して、commonArgs(crane から生成するパッケージに共通で使う引数郡)と commonArgs と craneLib を使って cargoArtifacts を作成します。 cargoArtifacts はプロジェクトの依存だけの Derivation でこれを作成することで依存関係をキャッシュさせることが出来るようになります。

let
rust-bin = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
craneLib = (crane.mkLib pkgs).overrideToolchain rust-bin;
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = with pkgs; [ ];
nativeBuildInputs = with pkgs; [ ];
};
cargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "deps";
}
);
in
{
# ...
}

treefmt-nix と git-hooks-nix の設定です。 nix flake fmt で treefmt が、nix flake check で clippy と cargo-check が実行されます。コミット時にはどちらも実行されます。

{
treefmt = {
projectRootFile = "flake.nix";
programs.actionlint.enable = true;
programs.nixfmt.enable = true;
programs.rustfmt.enable = true;
programs.taplo.enable = true;
programs.yamlfmt.enable = true;
};
pre-commit = {
settings = {
hooks = {
flake-treefmt = {
enable = true;
name = "flake-treefmt";
entry = lib.getExe config.treefmt.build.wrapper;
pass_filenames = false;
};
clippy.enable = true;
cargo-check.enable = true;
};
settings.rust.check.cargoDeps = pkgs.rustPlatform.importCargoLock {
lockFile = ./Cargo.lock;
};
};
};
}