自由のなる木

RSS
公開日
2022-10-16

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 shellwhich パッケージと 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 オプションありがたみがほとんどわからないかもしれないが、個人的には下記の問題を解決する方法が増えたことがありがたいと思っている。

  1. FHS で配置されたファイルを前提とするライブラリの利用
  2. 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 の利便性は格段に高まるだろう。

以上。


  1. 正確には実行時にパスを与えることで zoneinfo ディレクトリの所在を伝えることができるが、特殊なシステムのためだけに特殊なコードは普通は書きたくないものだ。