Vagrantでのテスト環境に対応 / docker build諦め / Kawa対応
DockerだとAMD64なLinuxばっかりになってしまうので、Vagrantを使って仮想マシンを使用したyuniのビルドとテストに対応してみた。
CygwinからWindowsネイティブというか何というかのVagrantを使うのもかなり微妙だが。。基本的にup / halt / ssh-configにだけ使って、rsyncを使ってファイルをコピーしたりssh経由でビルドコマンドを発行したりは直接Cygwinのsshを使っている。というのは、CygwinからWindowsプログラムをCTRL+Cで止めても止まらないことが多いため。ちょっとビルドシステムに組込むのは厳しいと感じた。
VagrantはRubyで書かれており、これを適当な方法で1つの.exeにパッケージしている。この手のプログラムはCygwinからは良く制御できない。(winpty等を使って適当にコンソールをアサインするといった方法があるが、そもそもVagrantがコンソールハンドリングを実装していない。)
ポータビリティの難しさ
というわけで、これからVagrantを使ってFreeBSD/NetBSDと対応していこうと思った矢先に、yunibaseに収録された殆んどのSchemeはそのままではFreeBSD上でビルドできないという難しい状態に。。
- Gaucheは0.9.4以降に入ったコミットでLinux/Cygwin/MacOS以外がダメ https://github.com/okuoku/yunibase/issues/9
- Sagittariusは標準パッケージのBoehmGCとリンクできなかった
- Chickenはコンパイラが gcc であることを仮定している(FreeBSD10以降はclangなのでccしかない)
- GuileはbootstrapにGNU M4が必要
- Vicareはlibcがglibcであることを前提としている
で、chibi-schemeとRacket、nmoshしか残らなかった。
- https://github.com/okuoku/yunibase/blob/086c6c5244001777a409fc44bfa5887d445b79a5/README.md#others - チェックが少い
更にchibi-schemeはyuniのFFIを使うと何故かヒープを破壊して落ちる( https://github.com/okuoku/yuni/issues/37 )ので、FreeBSDでまともに動作するyuni環境はRacketとnmoshしか無い。
これはかなり不味い。yunibaseの構造、つまり、コンパイラやCMakeだけをインストールしたboxでイメージを作ってテスト、というデザインではこういうトラブルが発生したときに結局デバッグ環境を構築する必要が出てきてしまう。エディタ環境すら無いのでprintfデバッグすら絶望的と言える。
... そもそも論としてはFreeBSDに対応して嬉しいのかという本質的な問題は有る。使わないOSへのポータビリティは単純に負債になるし、Linux + glibcに非ずんばOSに非ずというのは、まぁ一面的に正しいのが実情。前回( http://d.hatena.ne.jp/mjt/20160209/p1 )書いたようにMuslは大きなdesign winを獲得しつつあるけど。
Docker buildをやめた
従来、yunibaseのdockerイメージ(ビルド済の各種Scheme処理系が入ったテスト環境)ではdocker buildコマンドを使ってイメージをビルドしていた。ただ、docker buildはちょっとこういう目的には向かない仕組みで:
- ビルド中にファイルを消してもイメージのサイズは減らない
- COPYを分割すると分割したぶんだけイメージが作成されてしまう
というわけで、イメージの中にビルド中に生成された.o等が丸ごと残るという状況だった。
- 2.3GiB(実際の中身は1GiB未満)
さすがにこれは不味いので普通にdocker runとdocker commitで作るように変更したところ、イメージのサイズは7 layer 879MiBまで減少した。
複数のイメージを纏めるdocker squashのようなツールもいくつか有るが、あまり外部ツールに依存するのも考えものなので素直な実装とした。できればDockerの標準機能でレイヤへのコミットのタイミングは制御できると良いのだけれど。。
(これに学んだのでVagrantでのビルドにはPackerを使っていない。)
Kawa対応
Javaで動作するScheme処理系であるところのKawaに対応した。というか以前から対応コードは書いていた( http://d.hatena.ne.jp/mjt/20141213/p1 )のだけれど、なかなか上手く動かなくて結局MLに泣き付く事態になった。
- https://github.com/okuoku/yuni/issues/33
- values手続きを分岐の先にある末尾コンテキストに書くとコンパイルできなくなる。
- (fix: https://sourceware.org/viewvc/kawa?limit_changes=0&view=revision&revision=8704 )
- https://github.com/okuoku/yuni/issues/34
- full-tailcalls(完全な末尾再帰サポート)を有効にすると、値を消費しない手続きが実行されないことがある。
- (fix: https://sourceware.org/viewvc/kawa?view=revision&revision=8705 )
- https://github.com/okuoku/yuni/issues/35
- no-inline(インライン化の禁止)で、R7RSライブラリのrenameが機能しなくなることがある。
結局3件ともKawaのバグで、うち2件はupstreamでは既に修正されている。Issue 35はno-inlineを指定しなければ大丈夫なのでyuniとしては問題ない。
今回は手動で再現例の最小化をしたけど、yuniのライブラリは単純にライブラリ構文のlibraryをdefine-libraryに置換し、beginで囲めば正当なR7RSライブラリになるようにしているので、自動的にこれを行うツールを用意しとけばよかった。。
ちゃんとテストも通ったので、yunibaseにKawaも追加している。Javaの実行環境は地味に巨大なので、専用のDocker imageを用意することにした。ビルドはantで行っている( https://github.com/okuoku/yunibase/commit/2966fcc4f179d9d6dec510afa6b8b48326a5e5d5#diff-af6fc4c6dbd3cf6a04be16e591772e6eR5 )。Kawaのautotoolsビルドにはtexinfo6.0が必要で、これはDocker official imageのDebianでは使えない。TravisCIでのテストにも足しておいた( https://travis-ci.org/okuoku/yuni/builds/109000534 )。