§2. Nixpkgsを使う
Nixpkgsは、Nixの公式パッケージリポジトリです。約9万個のパッケージのビルド式を提供しており、Nixを利用する際はほぼ必ずNixpkgsを利用することになります。
また、Nixpkgsはパッケージ以外にも、Nix言語の組み込み関数を拡張したライブラリや「第3部: ビルドユーティリティ」で解説するビルド用関数も一緒に提供しており、Nix言語の標準ライブラリのような役割も果たしています。詳しくは『Nix入門』のChapter 10 Nixpkgsを参照してください。
ここではNixpkgsの利用方法を説明します。
Nixpkgsのブランチ
本書ではnixpkgs-unstableを利用します。
- nixos-xx.yy
- LTS
- 執筆時点ではnixos-24.05が最新
- nixpkgs-unstable
- ローリングリリース
- いくつかのテストをパスした更新が適用される
- nixos-unstable
- ローリングリリース
- NixOS用のテストを通過した更新が適用される
- master
- 最も新しいブランチ
- テストされていない
- 利用するべきではない
Nixpkgsの導入
Nixpkgsを導入し、hello
を再エクスポートしてみます。
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
};
outputs =
{ nixpkgs, ... }:
let
# 自分のシステムに合わせて変更してください
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages.${system} = {
hello = pkgs.hello;
};
};
}
ちゃんとエクスポートできていたらOKです。
# outputsの確認
$ nix flake show
path:/path/to/flake?lastModified=<最終変更日時>&narHash=<ハッシュ>
└───packages
└───x86_64-linux
└───hello: package 'hello-2.12.1'
# 実行してみる
$ nix run .#hello
Hello, world!
pkgs
pkgs
という変数にnixpkgs.legacyPackages.<プラットフォーム>
を束縛して使い回すのが慣習です。pkgs.<パッケージ名>
でNixpkgsのパッケージを参照できます。search.nixos.orgで検索して必要なパッケージを指定してください。
複数のプラットフォームに対応する
今のところ私たちのFlakeはx86_64-linux
用のパッケージしかエクスポートしていません。aaarch64-linux
やaarch64-darwin
からも利用できるように複数のプラットフォームに対応させたいのですが、わざわざpackages.aarch64-linux.hello
やpackages.aarch64-darwin.hello
と書くのは冗長なので、flake.nix
の記述を簡潔にするためのユーティリティ関数を使いましょう。
flake-utilsを使う
flake-utilsは、Flake関連のユーティリティ関数を提供しているFlakeで、今回はeachDefaultSystem
関数を使います。
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages = {
hello = pkgs.hello;
};
}
);
}
outputsでプラットフォームをベタ書きする必要がなくなりました。eachDefaultSystem
には文字列を引数に取り、AttrSetを返す関数を渡します。その関数の引数(上の例ではsystem
)にはプラットフォーム名が渡されます。
eachDefaultSystem :: (String -> AttrSet) -> AttrSet
outputsを確認すると、複数のプラットフォーム向けにパッケージがエクスポートされていることがわかります。
# outputsの確認
$ nix flake show
path:/path/to/flake?lastModified=<最終変更日時>&narHash=<ハッシュ>
└───packages
├───aarch64-darwin
│ └───hello omitted (use '--all-systems' to show)
├───aarch64-linux
│ └───hello omitted (use '--all-systems' to show)
├───x86_64-darwin
│ └───hello omitted (use '--all-systems' to show)
└───x86_64-linux
└───hello: package 'hello-2.12.1'
Intel/AMD/ARMのLinux、Intel/AppleシリコンのmacOS向けにhello
パッケージがエクスポートされています。前述のプラットフォーム以外にも対応したい場合はeachSystem
関数などを使うといいでしょう。
ユーティリティ関数の自作
flake-utilsは依存ゼロのFlakeなので、全ての関数がNix言語の組み込み関数のみで実装されています。
# 一部省略している
{
defaultSystems = [
"aarch64-darwin" # 64-bit ARM macOS
"aarch64-linux" # 64-bit ARM Linux
"x86_64-darwin" # 64-bit x86 macOS
"x86_64-linux" # 64-bit x86 Linux
];
eachSystem =
systems: f:
let
op =
attrs: system:
let
ret = f system;
op =
attrs: key:
attrs
// {
${key} = (attrs.${key} or { }) // {
${system} = ret.${key};
};
};
in
builtins.foldl' op attrs (builtins.attrNames ret);
in
builtins.foldl' op { } systems;
eachDefaultSystem = eachSystem defaultSystems;
}
……Nix言語に慣れていない人には難しいかもしれません。
Nixpkgs libを利用すればより短いコードで似たような関数を実装できます。
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
};
outputs =
{ nixpkgs, ... }:
let
supportSystems = [
"aarch64-darwin" # 64-bit ARM macOS
"aarch64-linux" # 64-bit ARM Linux
"x86_64-darwin" # 64-bit x86 macOS
"x86_64-linux" # 64-bit x86 Linux
];
forAllSystems = nixpkgs.lib.genAttrs supportSystems;
in
{
packages = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
hello = pkgs.hello;
}
);
};
}
eachDefaultSystem
と違って、outputsの各attributeごとにforAllSystems
を使用する必要がありますが、概ね同じことができます。
ポイントはnixpkg.lib.genAttrs
です。Nixpkgsはパッケージだけでなく、Nix言語のライブラリも提供しています。
genAttrs :: [ String ] -> (String -> Any) -> AttrSet
どれを使うべきか?
一番よく見かけるのはflake-utilsですが、自前の関数を用意している人も多いです。
また、flake-partsというflake-utilsよりも高機能なライブラリもあります。
本書では最も簡潔に記述できるflake-utilsを利用することにします。