Nix を使ってプログラムをビルドする方法のメモ書きです。
基本の流れ
ビルドの基本の流れを概観します。
まず、プロジェクトディレクトリの中に、default.nix
ファイルを作成します。
> mkdir myproject && cd myproject
myproject> touch default.nix
次に、default.nix の中にstdenv.mkDerivation
を書きます。
mkDerivation
は Nix でパッケージをつくる時に使う関数です。
with import <nixpkgs> {};
stdenv.mkDerivation { ... }
default.nix
を書き終えたらnix-build
コマンドを実行して、パッケージをビルドします。
myproject> nix-build
ビルドが終わると、プロジェクトディレクトリの中にresult
というフォルダができます。
この中にビルドしたバイナリなどが入ります。
最小サンプル
以下の C ソースコードをビルドします。
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, world\n");
}
default.nix
はこのように書きます。
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "hello";
src = ./hello.c;
buildCommand = ''
gcc -Wall -O2 -o hello $src
install -m555 -Dt $out/bin hello
'';
}
この時点でプロジェクトの構成はこんな感じです
myproject> tree
.
├── default.nix
└── hello.c
れっつびるど
myproject> nix-build
these derivations will be built:
/nix/store/l46b92csn5d9ics7zfs9yb51gb5n6y4r-hello.drv
building '/nix/store/l46b92csn5d9ics7zfs9yb51gb5n6y4r-hello.drv'...
/nix/store/0a3f6ddksy17jmfzasj2np999cgkrs8a-hello
ビルドするとresult
ディレクトリができました。
myproject> tree -l
.
├── default.nix
├── hello.c
└── result -> /nix/store/0a3f6ddksy17jmfzasj2np999cgkrs8a-hello
└── bin
└── hello
実際のところ、result
は nix/store/0a3f...-hello
へのシンボリックです。
バイナリを実行してみます。
myproject> result/bin/hello
Hello, world
うまく動きました。
default.nix
のに書かれていることを読む
default.nix
でやっていることは、stdenv.mkDerivation
に必要な引数を渡しているだけです。
mkDerivation
の引数には、
name
src
buildCommand
が渡されています。
name
はパッケージの名前、src
はビルドするソースコードです。
buildCommand
では、ビルドに必要なシェルスクリプトを書きます。
buildCommand
の中では、$src
として引数のキーsrc
に渡したパスを参照できます。
また、$out
には、ビルドしたあとのパス(/nix/store/0a3f...-hello
)が入ってます。
default.nix
のリファクタリング
mkDerivation
の他の機能を見るために、さっきのdefault.nix
を書き換えていきます。
まず、name
の代わりにpname
とversion
を使います。
こうすると、パッケージの名前が自動で<pname>-<version>
という形式になります。
with import <nixpkgs> {};
stdenv.mkDerivation {
pname = "hello";
version = "0.1.0";
src = ./hello.c;
buildCommand = ...;
}
それから、buildCommand
をdefault.nix
に直書きするのをやめて、別ファイルにビルドスクリプトを書きます。
source $stdenv/setup
gcc -Wall -O2 -o hello $src
install -m555 -Dt $out/bin hello
1 行目のsource $stdenv/setup
は、Nix でのビルド環境をセットアップするために必要です。
ビルドスクリプトを書いたら、スクリプトのパスをbuilder
に渡します。
with import <nixpkgs> {};
stdenv.mkDerivation {
pname = "hello";
version = "0.1.0";
src = ./hello.c;
builder = ./builder.sh;
}
れっつびるど
myproject> nix-build
these derivations will be built:
/nix/store/j2vxh4xm6jarf0jnm9x3sywmr1nvxdhg-hello-0.1.0.drv
building '/nix/store/j2vxh4xm6jarf0jnm9x3sywmr1nvxdhg-hello-0.1.0.drv'...
/nix/store/i8l2rmiy708sz6n486yasgzshp044kpd-hello-0.1.0
default.nix を書き換えるとハッシュが変ったのがわかります。
さっきビルドしたものが上書きされるわけではなく、別ファイルに新しいビルドが作られます。
シンボリックを張りなおせば、いつでもさっきのビルドに戻せそうですね。
ウェブからソースをとってきてビルドする
ローカルのソースコードを使うのはやめて、ウェブ上のソースコードをダウンロードしてビルドしてみます。
GNU/hello をビルドしてみましょう。プロジェクトのページはこちらです。
ソースをダウンロードするためにfetchzip
を使います。
stdenv.mkDerivation {
...
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz";
sha256 = "1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l";
};
...
}
fetchzip
は、ウェブから圧縮されたソースをダウンロードしてローカルに展開します。
引数にはurl
だけでなくsha256
などのハッシュが必要です。
ハッシュを計算するために、nix-prefetch-url
コマンドを使います。
圧縮ファイルの場合は --unpack
オプションが必要です。
myproject> nix-prefetch-url --unpack 'https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz'
unpacking...
[0.7 MiB DL]
path is '/nix/store/xsh1xgry2vsf6674jhc26q8k1bv4p76s-hello-2.10.tar.gz'
1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l
GNU/hello は./configure; make; make install
でビルドできます。
このような典型的なビルドのときは、mkDerivation
がよしなにビルドしてくれるので、手でbuilder.sh
を書く必要はありません。
default.nix
の全体は以下のようになります。
stdenv.mkDerivation rec {
pname = "hello";
version = "2.10";
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/${pname}-${version}.tar.gz";
sha256 = "1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l";
};
}
細かいことですが、Nix での文字列補完には${}
を使います。rec
も忘れないでください。
れっつびるど
myproject> ls
default.nix
myproject> nix-build
these derivations will be built:
/nix/store/q64l9f6d29zivah3sqrxlhpw8wmz3jrk-source.drv
/nix/store/936zpfpmzxliinc69a49yn4dh67pgj6x-hello-2.10.drv
...
以上、基本的なビルド方法でした。
Nix のビルドは特定の環境に依存しないので、default.nix
一枚あればどの PC でも同じようにビルドできます。
ぶっちゃけこの安心感のためだけに Nix を使う価値があるんじゃないかと思います。
より詳しい解説へのリンク
mkDerivation についてもっと詳しく知りたければ、マニュアルと Nix Pills が参考になります。
fetchzip
のほかにも、GitHub や GitLab からソースをダウンロードする関数も用意されています。
それらもマニュアルで解説されています。
しかし、残念ながら、Nixのマニュアルはまだ発展途上です。従って、マニュアルには数多くの内容がまだ欠けています。
実際にプログラムを Nix でビルドするときは、Nixpkgs のリポジトリのソースがとても参考になります。
また、mkDerivation
を自在に扱うためには、stdenv/setup
のソースにも目を通しておくといいです。