GNU GuixにCrystalをインストールしようとして、まだ納得の行く形でできていない、という内容です。
あまり整理された内容ではないため、こういうことをする人もいる、程度に読み流してください。
設定にしくじっていなければCrystal Advent Calendar 2023に登録できているはずです。
昨日の記事はkojix2さんのCrystalでDeepLを使うコマンドラインツールを書いたでした。バイナリが配布されているのはありがたいですね。また、doc
サブコマンドを指定すると--input
オプションで指定されたファイルの内容が翻訳対象になるようです。
一昨日の記事はCrystalでクリップボードを扱う安直なShardを書いた話でした。クリップボードはOSや環境により仕様が様々なので、統一的に扱えるレイヤーには需要があるでしょう。ここから更に、例えばHTMLで持っている表現や画像のデータを扱えるようにする方法を考えると面白そうです。
Guixとは
GNU GuixはLinux系のOSの1つです。似た雰囲気のディストリビューションとしてはNixOSがあります。GNUとあるように純粋な自由ソフトウェアで構成されている点も特徴です。なおそうした背景もあり、本記事でも原則としてThe GNU General Public Licenseのバージョン3以降及びGNU Free Documentation Licenseのバージョン1.3以降の元に使用が許諾されます。
GuixはFilesystem Hierarchy Standard (FHS) に従っていません1。つまり、通常はルートパス以下にあるディレクトリやファイルが、Guixには無いことがよくあります。Rubyで読み込む共有オブジェクトファイルはlib
ディレクトリにあるのがお決まりで、C言語で利用するヘッダファイルはinclude
ディレクトリにあるのが定番ですが、Guixでは簡単に見付からない訳です。
それでどうやって生きているのかというと、こうです。まず、ファイルが悉皆/gnu/store
ディレクトリ以下に配置されます。BashやGCC、Apache HTTPdであればその設定ファイルもあり得ます。そうして保管されている資材のうち、必要なものへのリンクの集合を環境とするのです。例えば私の手元では環境変数C_INCLUDE_PATH
が以下のようになっています。
$ echo $C_INCLUDE_PATH
/home/gemmaro/.guix-home/profile/include:/home/gemmaro/.guix-profile/include
前者のディレクトリは以下のようになっています。
$ ls -l /home/gemmaro/.guix-home/profile/include
total 860
lrwxrwxrwx 1 root root 78 Jan 1 1970 aio.h -> /gnu/store/dpfxpfyghkc19wz8jwaw31llhnvn8ngx-gcc-toolchain-13.2.0/include/aio.h
lrwxrwxrwx 1 root root 82 Jan 1 1970 aliases.h -> /gnu/store/dpfxpfyghkc19wz8jwaw31llhnvn8ngx-gcc-toolchain-13.2.0/include/aliases.h
lrwxrwxrwx 1 root root 81 Jan 1 1970 alloca.h -> /gnu/store/dpfxpfyghkc19wz8jwaw31llhnvn8ngx-gcc-toolchain-13.2.0/include/alloca.h
...
そういう訳で、世の中のあらゆるソフトウェアは簡単にはコンパイルできませんし、いわゆるパッケージ管理ツールは期待通りに動きません。Node.jsのNPM然り、JavaのMaven然りです。こうしたパッケージについてはGuix用のパッケージを作成して使うことになります。Rubyだとgemそれぞれを必要なバージョン毎に作るのです2。パッケージを定義する際は、メタデータの記録はもちろんのこと、例えばシェバングは/gnu/store
以下のファイルへの絶対パスに展開されますし、止むを得ず未収録のソフトウェアへの参照部分はパッチで取り除かれます。
追い討ちを掛けるようですが、いわゆるバイナリからのインストールはしにくくなっています。よしもし実行を試みたとて、大抵は必要なライブラリが見付からない旨のエラーで失敗するのがオチです。Guixの思想としては多分、ソフトウェアはすべからくソースからコンパイルされるべし、と。それもまた一興のように思われます。
以上の様相を踏まえて先に進みます。
きっかけ
GuixでふとCrystalを触ってみたいと思ったときがありました。公式の情報を見てみましょう。
Crystalの公式サイトはCrystal - A language for humans and computersで、ここからInstallページに遷移すると様々なOS向けのインストールの手順が載っています。残念ながらGuixは載っていません。
それでGuixでパッケージを検索3したところ、まだ無いようでした。無いものは作るしかないので手を動かし始めました。
先のインストールのページには「from sources」と「from .tar.gz
」とがありました。前者のページの説明には以下とあります。
- Install the latest Crystal release. To compile Crystal, you need Crystal :).
ところが今は、この初めの一歩が踏み出せません。前述した通り、宗教上の都合があるからです。
八つになりし年、父に問うて曰く、「佛は、いかなるものにか候ふらむ」といふ。
父が曰く、「佛には、人のなりたるなり」と。
又問ふ、「人は、何として佛にはなり候ふやらむ」と。
父又、「佛の教によりてなるなり」と答ふ。
又問ふ、「教へ候ひける佛をば、何が教へ候ひける」と。
又答ふ、「それも又、さきの佛の教によりて、なり給ふなり」と。
又問ふ、「その教へ始め候ひける、第一の佛は、いかなる佛にか候ひける」といふ時、父「空よりやふりけむ。土よりやわきけむ」といひて笑ふ。
「問ひつめられて、え答へずなり侍りつ」と。
諸人に語りて興じき。『つれつれ草:新註対訳』池辺義象著、第二百四十三段。
模索
Crystalのリポジトリはcrystal-lang/crystalです。ここを漁ってみたところ、どうもmaster
や更新が新しそうなブランチはCrystal自体を使っているようでしたが、唯一ruby
ブランチはRubyがあれば良さそう、ということに気付きました。
以下はコミット04964442a
に向けたmanifest.scm
です。このファイルは何かというと、そのディレクトリで使用するパッケージを書いたもので、guix shell
とすると環境構築がなされます。Rubyで言うと、gemのパスが通ったりする訳です。
(specifications->manifest (list "ruby@3.1" "ruby-llvm" "ruby-levenshtein-ffi"
"ruby-rspec" "ruby-graphviz" "ruby-diff-lcs"))
当然これだけでは不充分であり、その他のファイルも勘で埋めたり削ったりといった具合で、manifest.scm
に挙げたパッケージもこれが全部ではありません。なお、ruby-llvm
、ruby-levenshtein-ffi
、ruby-graphviz
はGuix公式チャンネルに登録されていなかったため、自作する必要がありました。これらのgemが必要とする依存関係にも無いものがあり、そのまた依存関係にも無いものがあり、といった具合で、再帰的に作っていったように記憶しています。
ruby-llvm
のmanifest.scm
は以下の通り。
(specifications->manifest (list "ruby@3.1"
"ruby-debug"
"ruby-ffi-gen"
"ruby-minitest"
"ruby-minitest-reporters"
"ruby-rubocop-performance"
"ruby-simplecov"
"llvm"
"glibc"))
ruby-levenshtein-ffi
のmanifest.scm
は以下の通り。
(specifications->manifest (list "ruby@3.1" "ruby-ffi" "ruby-rspec"))
それぞれパッチなどもありましたが、当座の供養はここまでに、折を見てGuix公式のチャンネル(Rubyで言うところのRubyGems.orgのようなもの)に投稿できる程度に仕立てたいと思っています。
ともあれこうして闇雲に作業をしていると、これでは埒が開かないとようやく気が付き始めます。それで既にこうした試みがなされているのではないかと探してみると、ありました。Guix issue tracker / Add ruby-for-crystal.がそれです。そこではCrystalをブートストラップするための古いRubyのパッケージが追加されているのですが、驚いたのはcrystal-lang/bootstrap-scriptというリポジトリの存在でした。先達のあらまほしきことなり。
やや脱力気味でそちらのスクリプトをいじり始めましたが、あちらこちらをコメントアウトしてみたり、pcl
なるパッケージが要るのかどうか、といったところで中座しているところです。
背信
先に触れたように、Guixは純粋に自由ソフトウェアで成り立っているOSです。故に所収されているパッケージもまた自由ソフトウェアとして適う使用許諾のものが選定されています。Crystalも原理的にはソースからのコンパイルが可能ではあるので、将来パッケージが追加される可能性はあるでしょう。
ただ、ちょっとCrystalを動かしてみたいという当初の思い付きからすると、時間を掛け過ぎてしまった嫌いがあるのは認めざるを得ません。実はGuixにはいくつかの裏道があり、不自由なソフトウェアを使うことはできなくはないのです。
その1つとしてバイナリをそのまま保管してパスを通してしまう作戦があります。後ろめたさを感じなくはないパッケージがあるチャンネルとしてnonguixがあり、そこからはbinary-build-system
という構築方法が提供されています。日本語の記事としてはROCKTAKEY’s site/Guixのパッケージをつくってみよう-その3: どうしてもビルドできないときはバイナリを直接使ったパッケージを作るに丁寧に書かれています。
この構築システムを使った例は以下のようになります。先に掲げた公式のインストールのページに「from .tar.gz
」のリンクがありましたが、これはGitHubのリリースページからバイナリをダウンロードできるということでした。
(define-module (gemmaro packages crystal)
#:use-module ((guix licenses)
#:prefix license:)
#:use-module (guix download)
#:use-module (guix packages)
#:use-module (nonguix build-system binary))
(define-public crystal
(let ((revision "1"))
(package
(name "crystal")
(version "1.10.1")
(source
(origin
(method url-fetch)
(uri (string-append
"https://github.com/crystal-lang/crystal/releases/download/"
version
"/crystal-"
version
"-"
revision
"-linux-x86_64.tar.gz"))
(sha256
(base32 "12rl28ndinby96jdnc3iz0m3kyhpll81yaf00yxd2lrnbmsy6hhp"))))
(build-system binary-build-system)
(home-page "https://crystal-lang.org/")
(synopsis "Crystal programming language")
(description
"The Crystall language is a programming language for humans and
computers. It's syntax is heavily inspired by Ruby's. Crystal is
statically type checked and has built-in type inference.")
(license license:asl2.0))))
インストールして動かしてみましょう。一応うまくいっていそうですね。
$ crystal --version
Crystal 1.10.1 [c6f3552f5] (2023-10-13)
LLVM: 15.0.7
Default target: x86_64-unknown-linux-gnu
おわりに
Crystalコンパイラはセルフホストされる方式を採る言語処理系であり、使えるようにするまでのハードルは低くないことが分かりました。実はRustにも同じことが言えて、Guix blog / Bootstrapping Rustという記事があります。セルフホストはメタフィクション的な魅力こそあれ、Yorick Peterse / A decade of developing a programming languageにあるように現実的な課題はあるようです。
話変わってshards
という便利そうなプログラムも同梱されていました。ですがきっと、ライブラリ1つ1つを手ずからGuixのパッケージとして仕立てるのが筋というものでしょう。Guixの姿勢としては恐らく、出所不明のパッケージが一切合切降ってくるのは看過できない、と。そう言われるとそうかもしれません。
Crystalをインストールする冒険はまだまだ続きます。
-
Guix Blog / The Filesystem Hierarchy Standard Comes to Guix Containersにあるように、このFHSを模擬する仕組みは一応あります。 ↩
-
Rustだとフィーチャーの組み合わせ毎になるので大変です。ただ、引数を取るパッケージが出てくるかもしれないという話がどこかにあった気がします。 ↩
-
Emacsの構成が済んでいれば
M-x guix p n
として検索できます。web上でもGNU Guix / Packagesというページから簡単に検索できます。 ↩