10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

# Nix でHello, Worldをビルドする方法

Posted at

Nix を使ってプログラムをビルドする方法のメモ書きです。

基本の流れ

ビルドの基本の流れを概観します。
まず、プロジェクトディレクトリの中に、default.nixファイルを作成します。

> mkdir myproject && cd myproject

myproject> touch default.nix

次に、default.nix の中にstdenv.mkDerivationを書きます。
mkDerivationは Nix でパッケージをつくる時に使う関数です。

default.nix
with import <nixpkgs> {};
stdenv.mkDerivation { ... }

default.nixを書き終えたらnix-buildコマンドを実行して、パッケージをビルドします。

myproject> nix-build

ビルドが終わると、プロジェクトディレクトリの中にresultというフォルダができます。
この中にビルドしたバイナリなどが入ります。

最小サンプル

以下の C ソースコードをビルドします。

hello.c
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("Hello, world\n"); 
}

default.nixはこのように書きます。

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

実際のところ、resultnix/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の代わりにpnameversionを使います。
こうすると、パッケージの名前が自動で<pname>-<version>という形式になります。

deafult.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
  pname = "hello";
  version = "0.1.0";  
  src = ./hello.c;
  buildCommand = ...;
}

それから、buildCommanddefault.nixに直書きするのをやめて、別ファイルにビルドスクリプトを書きます。

builder.sh
source $stdenv/setup

gcc -Wall -O2 -o hello $src
install -m555 -Dt $out/bin hello

1 行目のsource $stdenv/setupは、Nix でのビルド環境をセットアップするために必要です。
ビルドスクリプトを書いたら、スクリプトのパスをbuilderに渡します。

default.nix
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を使います。

default.nix
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の全体は以下のようになります。

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のソースにも目を通しておくといいです。

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?