guix shell
コマンドに --emulate-fhs
オプションが追加された
先週の話になるが、guix shell
コマンドに --emulate-fhs
オプションが追加された。
このオプションは guix shell --container
と組み合わせて使用するオプションで、名前の通り、guix shell --container
で作成するコンテナ環境で FHS (Filesystem Hierarchy Standard) をエミュレートする。
先日の記事でも触れている通り、Guix は FHS に準拠しない独自のファイルレイアウトを採用しているが、この機能を利用すれば、Guix が FHS に準拠しないことで生じる不便な部分を簡単に解決することができる。
issue の内容を見ると、最初のパッチは今年の 6 月時点で提出されており、今月になってようやく master ブランチにマージされたようだ。
コミュニティからは多くの喜びの声が上がっており、マージされたことを祝福するコメントもあった。待望の機能改善だったと言える。
--emulate-fhs
オプションの有無による違い
--emulate-fhs
オプションの有無による挙動の違いを簡単に確認してみよう。
guix shell
で which
パッケージと coreutils
パッケージを利用できるコンテナを作成し、ls
コマンドのパスを出力してみる。
--emulate-fhs
オプションを指定しない場合の出力結果は下記になる。
guix shell --container which coreutils -- which ls
/gnu/store/44w9bmwdv9sjcggb7d13grf2ksmsisnp-profile/bin/ls
ls
コマンドのパスは、一般的なディストリビューションのそれと異なり、Guix のストアのパスであることが確認できる。
一方、--emulate-fhs
オプションを指定した場合の出力結果は下記になる。
guix shell --container --emulate-fhs which coreutils -- which ls
/bin/ls
一般的なディストリビューションと同様に /bin/ls
が出力される。
内部ではホスト側のファイルシステムが FHS に沿う形で上手くコンテナにマッピングされたり、マッピングで解決できない部分は FHS に沿う形でSymlink を作成したりすることで、この動作を実現しているようだ。
--emulate-fhs
オプションが解決すること
上記だけでは --emulate-fhs
オプションありがたみがほとんどわからないかもしれないが、個人的には下記の問題を解決する方法が増えたことがありがたいと思っている。
- FHS で配置されたファイルを前提とするライブラリの利用
- FHS で配置されたファイルを前提とするコンパイル済みバイナリの利用
FHS で配置されたファイルを前提とするライブラリの利用
例えば、Ruby のライブラリ (Gem) に TZInfo というライブラリがある。
このライブラリは、Ruby でタイムゾーンを扱う時に使用するもので、Unix-like なシステムでは zoneinfo ディレクトリに存在するファイルを利用してタイムゾーンの計算などをする。
Guix は Unix-like なオペレーションシステムであるが、FHS に準拠しないため、TZInfo が zoneinfo ディレクトリ (/usr/share/zoneinfo
) の存在を見つけることができない。1
したがって、Guix でパッケージ化されている Ruby のライブラリや Ruby 製のソフトウェアで TZInfo に依存するものは、TZInfo::Data
というライブリもパッケージの依存ライブラリとして指定している。
このライブラリは zoneinfo ファイルを持たない Windows などで利用することを想定したライブラリで、本来は Unix-like なオペレーションシステムであれば不要なものだが、Guix は FHS に準拠しないため必要になってしまっている。
--emulate-fhs
オプションが使えるようになると、プロジェクト固有の環境を --emulate-fhs
オプション付きで起動すれば、その環境では TZInfo を通常の Unix-like なシステムと同様に使用できるようになる。
guix shell --container --emulate-fhs --network
export GEM_HOME=$PWD/gem
gem install tzinfo
ruby -rtzinfo -e'p TZInfo::Timezone.all_identifiers'
["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", "Africa/Asmara", ..., "US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa", "UTC", "Universal", "W-SU", "WET", "Zulu"]
上記のスクリプトは --emulate-fhs
オプションを指定しない場合、TZInfo::DataSourceNotFound
という例外を送出してエラーになる。
guix shell
にはマニフェストファイルを指定することができるため、実際にはプロジェクト固有のパッケージ構成を記述したマニフェストファイルを作成し、それを渡して環境を立ち上げることになるだろう。
FHS で配置されたファイルを前提とするコンパイル済みバイナリの利用
例えば静的サイトジェネレータとしてシェアの大きい Hugo などのツールは、ソースコードからビルドすることが比較的難しく、Guix の公式リポジトリにはパッケージとして収録されていない。
一般的な Unix-like なシステムであれば、Hugo が公式でリリースしているコンパイル済みバイナリを入手すれば、すぐに使い始めることができるのだが、FHS に準拠していない Guix ではそのまま利用することができないため、これまでは、入手したバイナリの ELF ヘッダや RPATH などを PatchELF で書き変えるか、Guix ではない別のシステムのコンテナなどを利用して実行する必要があった。
--emulate-fhs
オプションが使えるようになると、FHS を前提として生成されたバイナリの実行も簡単になる。また、コンテナで起動するため安全でもある。
wget https://github.com/gohugoio/hugo/releases/download/v0.104.3/hugo_extended_0.104.3_linux-amd64.tar.gz
tar -xf hugo_extended_0.104.3_linux-amd64.tar.gz
guix shell --container --emulate-fhs gcc:lib -- ./hugo version
hugo v0.104.3-58b824581360148f2d91f5cc83f69bd22c1aa331+extended linux/amd64 BuildDate=2022-10-04T14:25:23Z VendorInfo=gohugoio
上記のスクリプトは --emulate-fhs
オプションを指定しない場合、libstdc++.so.6
などの共有ライブラリが見つからずエラーになる。
常に --emulate-fhs
オプションを指定してコンテナを起動することで解決するのが適切かどうかはコンテキストによるが、解決方法の選択肢が増えることは喜ばしい。
guix shell
は Guix の機能の中でも特に頻繁に利用する機能で、Guix を利用する理由のひとつにもなりうる機能なので、guix shell の利便性は格段に高まるだろう。
以上。
-
正確には実行時にパスを与えることで zoneinfo ディレクトリの所在を伝えることができるが、特殊なシステムのためだけに特殊なコードは普通は書きたくないものだ。 ↩