Nix について知りたい (→ PR を送ってみました)

今知りたいこと
Nix 近辺
- NixOS (システムレベルの設定):
-
/etc/nixos/configuration.nix
: システムの設定ファイル
-
-
Flakes:
/etc/nixos/flakes.nix
- Git リポジトリ
- Nix (package manager):
- home-manager: ユーザ環境を塗り替える
- nix-shell: PATH が分離された? bash 専用の? 環境
-
nix-direnv:
nix-shell
の設定ファイルをユーザ環境のシェルに取り込む?
- 配布:
- パッチ?
- dylib や固定パスなど
疑問点
- Nix と Docker の使い分け (どちらも使ったことがない)
- Flakes のキャッシュの効力 (ローカルで、 CI で) (crane など)
- Nix で作られたビルド環境 (
default.nix
やshell.nix
?) にユーザ独自の開発環境 (言語サーバなど) を追加するには - 欲しいバージョンが channel に無かったらどうする

Nix 言語に入門中
NixOS の設定 を終えました。 NixOS ならではの部分をかいくぐってしまったので、ここらで Nix (言語) について調べて基礎的なキャッチアップを図ります。
Nix は無難で実用的な関数型言語という感じがしてきました。動的型付けで遅延評価される点に目を瞑れば……!
主なリソース (予定)
REPL で関数を見る
Lambda 関数
nix-repl> let f = x: x + 1;
in f 1
2
nix-repl> let f = x: y: x * y;
in f 2 3
6
Attribute set を引数に取る関数
nix-repl> let f = { x }: x + 1;
in f { x = 1; }
2
nix-repl> let f = { x, y }: x * y;
in f { x = 2; y = 3; }
6
=
は attribute set の上書きではなく和を作る
x.a = .. ;
の後に x = { .. };
を書いてみます:
nix-repl> { x.a = "a"; x = { b = "b"; }; }
{ x = { ... }; }
おっと遅延評価だ。強制的に評価してみると:
nix-repl> { x.a = "a"; x = { b = "b"; }; }.x
{ a = "a"; b = "b"; }
=
の厳密な解釈が気になります。
値の上書きはできない
Nix で書くのは式であって、逐次実行される手続きではないことが分かります:
nix-repl> { x.a = "a"; x = { a = "b"; }; }.x
error: attribute 'a' already defined at (string):1:20
at «string»:1:3:
1| { x.a = "a"; x = { a = "b"; }; }.x
| ^
:quit
で REPL を閉じる
Haskell (ghci
) と一緒だ……!:
nix-repl> :quit
configuraiton.nix
の基本形式
Attribute set を引数に取って、その一部に対してマッチし、 attribute set を返す関数を書いていたことが分かります:
{ config, pkgs, ... }:
{
name = value;
}

Nix 言語は Nix パッケージマネジャ専用の DSL
Nix Reference Manual が非常に良さそうです。これを読んでいきます。
便利機能
- Path が builtin (path interpolation も可)
-
inherit
で値埋め込み
(これは Rust みたく= ..
を省いたら自動でinhreit
になればいいのに?) -
with
式で名前空間の一時塗り替え
import
わからん!
Derivation
yomuzo!
https://nixos.org/manual/nix/unstable/language/derivations.html
TODO: Nix で作るシェル環境

雑多に
Nix の LSP
今は nil が最先端な気がします? NixOS モジュールの定義には飛べませんでしたが、ひとまず診断情報は出してくれました:
機能豊富みたいないので、ローカルパッケージでは本領発揮しそうです。
実装言語は Rust のようです。ちゃんと構文解析して lowering して名前解決しています。えらい……。 Analysis
とか rust-analyzer っぽい命名だなと思ったら ra のコントリビューの人でした。
Nix Language overview
https://www.youtube.com/watch?v=eCapIx9heBw
- 遅延評価: 1 つのパッケージを 2 回評価しない
- Derivation: Build task
Nix -> Derivation -> Nix store (at derivation hash) -
derivation
は環境設定が詳しすぎるのでmkDerivation
,runCommand
などの代替を使うべき

再挑戦する、かも……
nixpkgs に verification-helper を追加したい! 知識不足を補わなければです。
ローカルでのビルドには成功
動くには動くけれども、これを PR に投げて良いのか検証しなければなりません。
pkgs/development/python-modules/verification-helper/default.nix
{
lib,
buildPythonPackage,
colorlog,
fetchFromGitHub,
importlab,
online-judge-tools,
pyyaml,
setuptools,
toml,
}:
buildPythonPackage rec {
pname = "verification-helper";
version = "5.6.0";
pyproject = true;
src = fetchFromGitHub {
owner = "online-judge-tools";
repo = "oj-verify";
rev = "refs/tags/v${version}";
sha256 = "sha256-Q7uI13kazajeYb3LRK7mMhx0V3hh7CVqnptmQksMLOA=";
};
build-system = [ setuptools ];
# `propagatedBuildInputs` ではなく、ここに入れてしまって良い?
# そもそもこれは何?
dependencies = [
colorlog
importlab
online-judge-tools
pyyaml
setuptools
toml
];
# これは必須みたい
propagatedBuildInputs = [ setuptools ];
# これは何?
pythonImportsCheck = [
"onlinejudge"
"onlinejudge_command"
"onlinejudge_api"
];
# Requires internet access
doCheck = false;
meta = with lib; {
description = "A testing framework for snippet libraries used in competitive programming.";
mainProgram = "oj-verify";
homepage = "https://github.com/online-judge-tools/verification-helper";
license = licenses.mit;
maintainers = with maintainers; [ toyboot4e ];
};
}
$ nix-build -A python3Packages.verification-helper # 初回ビルドだと色々出る
/nix/store/hjw90nfiqhmjhl3170d2rb69qrcn9ph3-python3.12-verification-helper-5.6.0
正直、コントリビュータに丸投げすれば一撃なのは間違いありませんが、今回は自力で仕上げてみましょう。
メモ
-
nix-build -A <package>
ローカルのnixpkgs
の特定のパッケージをビルドする -
pythonImportsCheck
とは
いろいろ読んでみる
- https://nixos.org/manual/nixpkgs/stable/#buildpythonpackage-function
-
https://nixos.org/manual/nixpkgs/stable/#contributing-guidelines
- PyPI 上の名前は online-judge-veirfy-helper だった。こっちを名乗るべき?
- https://wiki.nixos.org/wiki/Python#Nixpkgs_Python_contribution_guidelines
基本機能
- inherits
- extraArgs (だっけ?)
- specialArgs
NixOS modules が引数として取れる値になるらしい

Nixify your Emacs
-
emacs-overlay の
emacsWithPackagesFromUsePackage
のおかげで依存パッケージの Nix 化は自動化される - パッケージは
epkgs.meplaBuild
で手動ビルドできる。また GitHub リポジトリへの参照を nvfetcher で管理すると更新が楽

mozc_tool
の .desktop
ファイルが欲しい
mozc_tool
が /run/current-system/sw/lib
から消えていたので、そもそもちゃんと .desktop
ファイルが生成されるように修正したい。
fork を作成する
fork の動作をローカルでテストする
- 簡単な方法としては、 GitHub に push して Flakes で URL を設定する?
- ローカルのファイルパスで動かしたい気が

メモ
Slack / Discord のメモです。私は無断で転載しました
-
nix-build -A <package>
ローカルのnixpkgs
の特定のパッケージをビルドする -
nixpkgs-review
-
nix build github:nixOS/nixpkgs/refs/pull/<number>head#<package>
ローカルで PR をビルドする -
nixpkgs-review pr --post-result <number>
ローカルで PR をビルドした結果をコメントとして書き込む
-
-
nvfetcher
リポジトリの参照を管理・更新するツール -
nixfmt (
nixfmt-rfc-style
)
確か公式のフォーマッタ -
any-nix-shell
bash 以外のシェルでnix-shell
(nix shell
ではなく) -
Fixpoints, attribute sets and overlays
overlays
の良い説明だとか。 - Nix Package Versions
- nix-update
- nix-output-monitor
- NixOSでStylixを導入してみる
- いい本らしい: https://book.divnix.com/ch00-00-the-nix-package-manager.html
- nixpkgs + fzf: https://github.com/3timeslazy/nix-search-tv
- nix-init で Python パッケージのビルド式を生成
- disko でハード管理?

/nix/store
がストレージを圧迫している件
500 GB.. 600 GB.. 馬鹿な、まだ増えるだと!?
GC や旧世代削除は定期実行させている
他の件を探しに行きます。
nativeBuildInputs
が原因?
以下のコマンドで GC roots, たぶん mark & sweep における mark の起点となるリンク一覧が得られるようです。
$ nix-store --gc --print-roots
結局 GC のせいでしょうか。nix-direnv
で使った flakes が残っていました。 flake.nix
の中に常に nativeBuildInputs
を使っていたのが不味かったかもしれません。
また古代の home-manager
もなぜか生き残っていました。どうしたんだお前……。
手荒に消してみる
ここで記録が止まっていたら、すべてオシャカになったということです。
$ /nix/var/nix/gcroots/auto
$ nix-collect-garbage -d
勝利!
528893 store paths deleted, 560739.82 MiB freed
消え杉にございます (560 GB).

nix build
にしてみた
Devlog を やーーっと nix build
に入門しました。

Nixify Haskell projects
haskell-flake (flake-parts の方) 単体だと何が起きているのか分からないので、こちらを追ってみます。
(読む前) 実は devShell だけで十分?
Cabal は依存パッケージのロックファイルを生成できるため、 Nix を使わなくてもバージョン固定できます。それでも言語の依存パッケーを Nix で入れるメリットがあるのか気になっています。何か面白いことがあって欲しい!
nix run
はネットワークと非接続の環境で実行されるため、 nix run
したければ nix 化するしかないとか、そんなオチだったりするんでしょうか。みてみます。
公式を見ておく
- haskellPackage は基本 Stackage のスナップショット (最新 LTS) + 追加パッケージ
- (nixpkgs 上で pin された時の) Hackage 上の最新パッケージは <package>-<ver> の形で提供される (デフォルト (LTS) 側が追いつくと消える)
- が、 nixpkgs 外での利用はおすすめしないらしい。あくまで nixpkgs 内で haskell アプリケーション等をビルドするためのものである
- とはいえ pins するのも面倒だしな
- デフォルトの ghc のパッケージセットのみ全体のテストが実行される
- 各パッケージのデフォルト版のみビルドテストするが、他のバージョンは名前があるだけでビルドできない可能性がある
- cabal.plan を元に自動的に依存パッケージを nix 化する仕組みが無いらしい? cabal2nix はあるので意味が取れていない
- haskell.nix は nixpkgs と全く互換性が無いとか
- インクルメンタルに ghc をビルドできるらしい。複数の derivation に分ければ確かに可能か?
- ghcWithPackages は package db を GHC に注入するらしいが、これがパフォーマンス上のメリットがあるやつでは? Package db を調べたい。
- cabal2nix で haskellPackeage.callPackage する例あり
- hls は wrapper 版と直に触れるやつがある
- あとは読んで無いけど下に Idris あり
チュートリアル
- haskellPackages を overlay で拡張し、 callCabal2nix した自分のパッケージを挟む意味はある?
nix run 用のセットアップをしてくれる? - haskellPackages.shellFor を使うと nix を使わずビルドするパッケージを指定できる他、たぶん依存パッケージ入りの ghc を設定してくれる?
む、ここでチュートリアルが終わってしまいました。 Stackage ではなく Hackage を使いたいですが、できるのかl

あまり Haskell で Nix を使う意味が無い気がします。 (devShell 部分を除いて) 再現性は元々担保されていますし (cabal.project.freeze or Stackage), キャッシュが効くと言っても一度ビルドしたらローカルにキャッシュが保存されます。うーん

nixpkgs への PR がマージされました
あまり詳しくなっていませんが、閉めます