Nix初心者がZero to Nixをやってみた
この記事は Nix Advent Calendar 2024 17日目の記事になります
この記事は何?
最近Nixをはじめたばかりの筆者がZero to NixでNixに入門した記事です!
チュートリアルを一通りやったので、学習したことの整理という目的で記事を書きました。
Nixってなに?
Nixは純粋関数型パッケージマネージャといわれるパッケージマネージャの一つです。宣言的な記述で再現性の高い開発環境を提供することができます。
NixOSで使われているパッケージマネージャですが、WindowsやmacOS, 他のlinuxでも使うことができます。
また、一度実行するとキャッシュされて2回目以降は高速化するという特徴もあります。
なんでNixを触ろうと思ったのか
一言で言うと「純粋関数型パッケージマネージャ」という言葉に惹かれたからです。
あとはNixOSを触る過程で、これって普段の開発マシン(僕の場合はmacOS)でも使えるのでは?と思いました。
『再現可能な開発環境を提供する』という目的ならDockerがありますが、Dockerは仮想環境を立てる以上、どうしても重たくなってしまう印象があります。また、HDD容量も逼迫する印象です。
パッケージマネージャだけでこの問題が解決すれば、もっと手軽に再現可能な環境を提供できるのでは、というのがNixを触るモチベーションでした。
で、どうやって学べば良いの?
NixOSはNixOSで最強のLinuxデスクトップを作ろうを読みながら構築していたのですが、パッケージマネージャはどうやって勉強するのか悩んでました
そこで、Asahiさんにこんなアドバイスをいただきました。(ありがとうございます!!)
Zero to Nixが Nix初心者にはちょうどよさそうです!ということで、実際にZero to Nixでパッケージマネージャの世界に入門してみることにしました。
Zero to Nixをはじめてみる
Zero to Nixはさきほど説明した通り、Nix初心者向けのチュートリアルです。
Zero to Nixは以下のコンテンツで作られています。
Nixをインストールして実際に環境構築したり、flake化(後述します)したりといった流れになっています。
- Get Nix running on your system
- Run a program with Nix
- Explore Nix development environments
- Build a package using Nix
- Search for Nix packages
- Turn your project into a flake
- Uninstall Nix (if necessary)
- Learn more
それでは、一つ一つ見ていきましょう。
1. Get Nix running on your system
Nixのインストール方法です。
コマンド一発でインストールすることができます。
$ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
2. Run a program with Nix
実際にnixのコマンドを実行してみます。
サンプルだとこんな感じです(実際はカラーのもっとかわいい画像が出てきます)。
$ echo "Hello Nix" | nix run "https://flakehub.com/f/NixOS/nixpkgs/*#ponysay"
___________
< Hello Nix >
-----------
\
\
\
\ █
▄▄▄▄▄ ▄▄▄ ███
▀▄▀█▄█▄█▄▄▄▄▄ ████
▄▄██▄▄█▄▄█▄█ █▄██▄▄█
▄▄▄▄█▄█▄████▄▄ ██▄███▄▀
▄▄▄▄██▄▄▄▄████▄▄ ▄▄██▄▄▄▀
▀▄▄▄▄▄█▄██▄▄▄▀ ▄▄▄▄▄▄▄▄▄█▄▄▄▀▀
▄█▄█▄▄▄▄▄▄█▄▄████████▄█
▀ ██▄▄██▄███████▄█████
████████████████████
██▄██▄▄███▄██▄▄▄███▄▄▄
▄▄▄▄▀██▄██▀▀▀▄▄▄█▄▄▄█▄▄
▄▄▄▀▀ ▄▄██▀ ███ ▀▄▄▄
▄▄▄▄▀ ▄▄▄▀ █▄▄▀ ███
▀▀▀ ▄▄▄▄▄▀ ▄▄▄██
▀▄▄▄▀ ▀▀▀
このコマンドの実行内容は
- flakehubからnixpkgsのNixコードを取得して、ponysayのflake outputをターゲットにした
- ponysay をビルドして、Nix Storeに格納した
- ponysayパッケージから
bin/ponysay
を実行
という流れになっています。
(flakeについてはNix flakesを参照してください。)
このように、nix run
コマンドを使うだけで、installしなくてもコマンドを実行することができます。
3. Explore Nix development environments
この節では、nix develop
を使って開発環境を構築する方法や、このコマンドが何をしているかについて解説しています。
ためしに、以下のコマンドを実行してみます。
$ nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example"
シェルが切り替わるので、type
コマンドを使ってコマンドのパスを確認してみます。
bash-5.2$ type curl
curl is /nix/store/740pphyabn1dmrl58p26xdqfnfckbcir-curl-8.11.0-bin/bin/curl
bash-5.2$ type git
git is /nix/store/w7a374fz8ai3swihnynx4r7hgqsw9xvd-git-2.47.0/bin/git
かなり独特なパスが表示されますね。これは一体何なのでしょうか?
Nixは、再現性を高めるため、インストール先のパスにHashを使用するという特徴があります。
例えば上記のパスは、以下の様に分離することができます。
/nix/store/740pphyabn1dmrl58p26xdqfnfckbcir-curl-8.11.0-bin/bin/curl
---------- -------------------------------- --------------- --------
↑ ↑ ↑ ↑
[Nix store prefix] [Hash path] [package path] [program path]
このHash pathが存在することによって、新しくパッケージをinstallしても独自のパスにinsallされ、他のpackageと衝突することが無くなるのです。
また、nix develop
は起動時に発火するshell hooks
を設定することができます。
$ nix develop "github:DeterminateSystems/zero-to-nix#hook"
Congrats! You just triggered a shell hook for a Nix development environment.
Run "exit" to exit this environment.
Then run "nix develop github:DeterminateSystems/zero-to-nix#hook" again to re-trigger this hook.
環境変数も使えます
$ nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example"
bash-5.2$ echo $FUNNY_JOKE
What kind of phone does a turtle use? A shell phone!
--command
を使えば、外部から直接コマンドを実行することもできます!
$ nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example" --command curl https://example.com
<!doctype html>
<html>
<head>
<title>Example Domain</title>
以下省略
一般的には、nix flake init
でflakeファイルを作ってnix develop
を実行するのがセオリーだそうです
$ nix flake init --template "github:DeterminateSystems/zero-to-nix#haskell-dev"
wrote: /home/nix-haskell-dev/flake.lock
wrote: /home/nix_sandbox/nix-haskell-dev/flake.nix
$ ls
flake.lock flake.nix
$ nix develop
bash-5.2$ type ghc
ghc is /nix/store/92cjsh3z5f8kn16djfi395fch0jm84gz-ghc-9.6.5/bin/ghc
bash-5.2$ ghci
GHCi, version 9.6.5: https://www.haskell.org/ghc/ :? for help
ghci>
4. Build a package using Nix
この節では、Nixのビルド方法について解説しています。
ビルドには、リモートのNixpkgsからビルドする方法と、ローカルにflakeファイルを持ってきてビルドする方法の2つがあります。
リモートからビルドするにはnix build
を使います。
$ mkdir build-nix-package && cd build-nix-package
$ nix build "https://flakehub.com/f/NixOS/nixpkgs/*#bat"
buildしたファイルはresult
として配置されます。これはシンボリックリンクとして配置されます。
$ ls
result
リンク元は先ほどと同じように、Nix特有のパスに配置されていることがわかりますね。
$ readlink result
/nix/store/xpvx99kslai1054085ys1fbmvpz5zv7y-bat-0.24.0
ちゃんと実行できることがわかりますね。
./result/bin/bat hoge.txt
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: hoge.txt
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ hoge
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Haskellのプロダクトもビルドできます。pandocはこんな感じです。
nix build "nixpkgs#pandoc"
./result/bin/pandoc --version
pandoc 3.1.11.1
Features: +server +lua
Scripting engine: Lua 5.4
User data directory: /home/.local/share/pandoc
Copyright (C) 2006-2023 John MacFarlane. Web: https://pandoc.org
This is free software; see the source for copying conditions. There is no
warranty, not even for merchantability or fitness for a particular purpose.
ローカルにflakeファイルを持ってきてビルドすることもできます。
$ mkdir nix-haskell-pkg && cd nix-haskell-pkg
$ nix flake init --template "github:DeterminateSystems/zero-to-nix#haskell-pkg"
wrote: /home/build-nix-package/nix-haskell-pkg/flake.lock
wrote: /home/build-nix-package/nix-haskell-pkg/stack.yaml
wrote: /home/build-nix-package/nix-haskell-pkg/zero-to-nix-haskell.cabal
wrote: /home/build-nix-package/nix-haskell-pkg/.gitignore
wrote: /home/build-nix-package/nix-haskell-pkg/flake.nix
wrote: /home/build-nix-package/nix-haskell-pkg/package.yaml
wrote: /home/build-nix-package/nix-haskell-pkg/src/Main.hs
wrote: /home/build-nix-package/nix-haskell-pkg/src
$ nix build
ローカルに持ってきたflakeファイルの中身はこんな感じです。
{
description = "Haskell example flake for Zero to Nix";
inputs = {
nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz";
};
outputs = { self, nixpkgs }:
let
# Systems supported
allSystems = [
"x86_64-linux" # 64-bit Intel/AMD Linux
"aarch64-linux" # 64-bit ARM Linux
"x86_64-darwin" # 64-bit Intel macOS
"aarch64-darwin" # 64-bit ARM macOS
];
# Helper to provide system-specific attributes
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
packages = forAllSystems ({ pkgs }: {
default = pkgs.haskellPackages.mkDerivation {
pname = "zero-to-nix-haskell";
version = "0.1.0";
src = self;
license = pkgs.lib.licenses.cc-by-sa-40;
executableHaskellDepends = with pkgs.haskellPackages; [
base
];
};
});
};
}
ビルドしたパッケージを実行するとこんな感じです。
$ ./result/bin/zero-to-nix-haskell
Hello from inside a Haskell program built with Nix!
5. Search for Nix packages
nix search
を使うと、パッケージから語句を指定して検索することができます。
例えば、cargo
という単語が含まれるパッケージは以下のように検索できます。
$ nix search "https://flakehub.com/f/NixOS/nixpkgs/*" cargo
* legacyPackages.x86_64-darwin.cargo (1.82.0)
Downloads your Rust project's dependencies and builds your project
* legacyPackages.x86_64-darwin.cargo-about (0.6.4)
Cargo plugin to generate list of all licenses for a crate
...
legacyPackages
っていう単語がでてきますが、これは古いという意味ではありません。
Nixではnix flake show
コマンドの高速化のために、legacyPackages
をつかってpackageを出力しています。
詳しくはこちらを参照してください。
nix flake show
を使う方法もあります。
nix flake show "github:nix-community/nixpkgs-wayland"
github:nix-community/nixpkgs-wayland/31b60284243f8b077894371f1ab3753dc26653c7?narHash=sha256-EUiA7xmVzzc8BpGNltmtimACsK9%2BrM6w12ts3lPemII%3D
├───bundle: unknown
├───devShells
│ ├───aarch64-linux
│ │ └───default omitted (use '--all-systems' to show)
...
Nixpkgsではnix search
を、それ以外ではnix flake show
を使うのが良い様です。
6. Turn your project into a flake
既存プロジェクトからflakeを生成する方法について解説しています。
flake化にはfh
が便利です。
早速使ってみましょう。
$ nix run "https://flakehub.com/f/DeterminateSystems/fh/*" -- init
ビルドしたものを見てみましょう。
$ nix flake show
git+file:///home/fh-init-example-project
├───devShells
│ └───x86_64-darwin
│ └───default: development environment 'nix-shell'
└───schemas: unknown
ちなみに、fh init
はプロジェクトルートのファイルをみて何のプロジェクトかを推論してくれます。
例えば、cargo.toml
があればRust
だと推測してプロンプトで質問を投げかけてくれます。
$ nix run "https://flakehub.com/f/DeterminateSystems/fh/*" -- init
Let's build a Nix flake!
> An optional description for your flake:
> Which systems would you like to support? You selected 1 system: x86_64-darwin
> Which Nixpkgs version would you like to include? latest stable (currently 24.11)
> This seems to be a Rust project. Would you like to initialize your flake with some standard dependencies for Rust? Yes
...
ただし、この機能は今のところ環境構築のみしか使えません。(devShellしか対応していないため)つまりパッケージには使うことができないということです。
fh shell
を使えばシェルを起動して使用することもできます。
$ nix shell "https://api.flakehub.com/f/DeterminateSystems/fh/*.tar.gz"
❯ fh --version
fh 0.1.21
7. Uninstall Nix (if necessary)
uninstallはコマンド一発でできます。
$ /nix/nix-installer uninstall
8. Learn more
もっと学習したい場合は、こちらに学習コンテンツがまとまっています。
個人的にはNixOSを触るのがいちばん良いと思います!
さいごに
駆け足ですが、Zero to Nixの内容をざっとまとめてみました!
これからもっと使いこなして業務でも活かせたら、と思います!
最後になりますが、Nixについて教えてくれたAsahiさん、記事を書く後押しをしてくれたtoybootさん、ありがとうございます!また色々教えてください!
Discussion