yunibase計画

なんかDockerのバージョンを変えたらログインできないなと思っていたら"docker login"コマンドで生成される認証トークンが間違っているからだった。

手で"docker.io"を"https://index.docker.io/v1/"に書き換えるか何度かdocker loginすると直るらしい。難しい。

yunibaseとは

というわけで、yunibase( https://github.com/okuoku/yunibase )が大分形になってきた。yunibaseは、各種OS向けにR6RS/R7RS処理系を含んだアーカイブを定期的に生成するプロジェクトで、今回32bit Linuxを追加してLarcenyを含めた( https://github.com/okuoku/yunibase/commit/d114747fb6146fa1aa3acf0fa4870af131adcf01 )ので、yuniでサポートしていてyunibaseに収録されていない処理系はIronSchemeだけになった。
生成されたアーカイブはDocker Hubで公開( https://hub.docker.com/r/okuoku/yunibase/tags/ )している。今のところ公開しているイメージはDockerだけだし、これらのイメージにはyuniライブラリそのものが含まれていない。なので現状はあんまり実用的ではないが、そのうち、各種処理系を直ぐ試せる環境として整備していこうと考えている。
yunibaseはyuniがサポートしている処理系を全てカバーするのが目標なので、現状10の処理系を収録している。

一つのライブラリをこれだけ多くの処理系に対応させているケースというのもなかなか無い気もするが、実際、これくらいのコストを掛けて処理系を追いかけられるようにしておかないと、処理系側の仕様変更に追従する( https://github.com/okuoku/yuni/issues/29 )のが遅れてしまう。
yunibaseそのものは

上での

  1. 処理系のビルド環境の構築(apt-getとかを呼んで必要なパッケージを自動的に揃える)
  2. 処理系のビルドとテスト(recipeに従って./configureとかを発行する https://github.com/okuoku/yunibase/tree/master/recipes )

をサポートしている。

yunibaseイメージの内容

yunibaseイメージには、基本的に /build/current/処理系名 のディレクトリをprefixとして処理系がインストールされている。

[root@localhost vagrant]# docker run -it --rm -v /yunitest/yuni:/yuni:Z okuoku/yunibase:testing-fedora bash
[root@9a714628d751 build]# find /build/current/chibi-scheme/
/build/current/chibi-scheme/
/build/current/chibi-scheme/bin
/build/current/chibi-scheme/bin/chibi-doc
/build/current/chibi-scheme/bin/chibi-ffi
/build/current/chibi-scheme/bin/chibi-scheme
/build/current/chibi-scheme/bin/snow-chibi
/build/current/chibi-scheme/include
/build/current/chibi-scheme/include/chibi
/build/current/chibi-scheme/include/chibi/bignum.h

yuniのCMakeプロジェクトは、普通にpathから処理系を検索する以外に、yunibaseのディレクトリ構成を仮定して処理系を検出する処理を入れてある。
yunibaseイメージにはnmoshとかCMakeのようなyuni自体のビルドに必要なものも事前にインストールされているので、そのままyuniをビルドしてテストできるようになっている。

yunibaseイメージの使われ方

今のところ、yunibaseイメージはyuniのTravisCIテストで使用している。このため、例えば将来yuniがpullリクエストを受け入れるような事態になった場合でも、パッチ投稿者に全処理系でのレグレッションテストをさせずに済むことになる。
もちろん、パッチ投稿者が事前のテストを望むのなら、yuniにはyunibaseイメージを想定したテストスクリプトが事前に用意してある。DockerをインストールしているLinuxホストであれば"docker run"コマンドを使用して、

[root@localhost vagrant]# docker run -it --rm -v /yunitest/yuni:/yuni:Z okuoku/yunibase:testing-fedora \
cmake -P /yuni/integration/buildhost-yunibase/test-on-root.cmake
-- Copying tree /yuni => /yuniroot
-- Configure (/yuniroot/yuni/integration/buildhost-yunibase)...
-- Build...
-- Setup...
-- Test...
Test project /yuniroot/build
    Start 1: CHIBI_SCHEME-_sanity.sps
1/8 Test #1: CHIBI_SCHEME-_sanity.sps .........   Passed   10.86 sec
    Start 2: GOSH-_sanity.sps
2/8 Test #2: GOSH-_sanity.sps .................   Passed   15.69 sec
    Start 3: RACKET-_sanity.sps
3/8 Test #3: RACKET-_sanity.sps ...............   Passed   10.59 sec
    Start 4: CSI-_sanity.sps

(/yunitest/yuni に yuniがチェックアウトされていることを仮定している)
のように、収録処理系に合わせたFFIスタブのビルドと各種処理系を使用したテストまで一気に行える。DockerのイメージはUbuntu32/64とFedoraを用意しているので、ディストリビューション毎、要するにディストリビューションが採用しているコンパイラ/バイナリパッケージ毎にテストできる。
将来的にはyuniで書かれたアプリケーションを実行する機能も持たせたいと考えている。

yunibaseポリシ

いくつかyunibaseとしてのデザインチョイスが有る。

  • 再現性と移植性

yunibaseはリポジトリを対応OS + 環境でチェックアウトしてくれば直ぐ使え、直ぐに配布イメージを再現できるように配慮している。このため、shellスクリプトすら使わず全てCMakeで書かれている。
CMakeはWindowsでも動くlibarchiveとcurlを統合した汎用スクリプト言語としても使え、かつ、それなりに普及しているというメリットが有る。そのかわり、可読性とか構文とか様々なものが犠牲になっているが。。
再現性は一応GPLのリーガル要求なので(配布イメージはインストールイメージしか含んでいないのでソースコードが無い)、これを実現しつつメンテナンス性も維持できるギリギリのラインを狙った結果とも言える。

ホスト環境からのアイソレーションを前提に設計している。つまり、コンテナランタイムとしてDocker、仮想マシン管理ツールとしてVagrantを採用しビルドプロセスに組み込んでいる。Windowsではyunibase専用のCygwinMinGWを自動的にインストールする。
yunibaseでは、アイソレーションされたビルド環境を"base"、実際のビルド成果物を含むイメージを"image"と呼んでいる。Dockerの場合、yunibuildリポジトリ( https://hub.docker.com/r/okuoku/yunibuild/ )が"base"イメージに相当し、これは専用のDockerfile( https://github.com/okuoku/yunibase/blob/9a7cd17c990bbc3475c2a30dc84a71784d3a6741/hosts/docker-linux/base-ubuntuLTS/Dockerfile )で単にDocker buildすることで作られている。
アイソレーションが重要なのは、yunibaseの構築を開発マシンそのものでも行えるようにするため。yunibaseは処理系のビルドに必要なパッケージを自動的にインストールするため、どうしても環境を汚してしまう。アイソレーションを行うことによりホスト環境に対して遠慮が不要になるので、パッケージの自動インストールなど重要な機能を実現できる。
難しいのはOS Xで、OS Xchroot環境は構築が超面倒なのでどうにかして欲しい。。今のところOS Xをサポートするための良いアイデアは無い。Joyentは自社のSmartOS都合でpkgsrcのOS Xバイナリを提供している( https://pkgsrc.joyent.com/ )ので、yunibaseでそちらを占有しつつ普段の開発はhomebrew -- のような逃げ方は有るかもしれない。

  • Gitによるソース管理とbootstrap

yunibaseに収録している処理系だと、KawaがSubversionSagittariusがHgで、これらはGitミラーを経由して収録している。他の処理系はupstreamを直接submoduleで接続してリビジョン管理している。
ソースツリーはstable / currentの2つに分け、bootstrapに必要な場合のみstable側をビルド/installしてからcurrentをbootstrapしてビルドしている。yuniのテストは常にcurrentに対して行う。要するにyunibaseはstableリリースを無視している。
stableの扱いは悩みどころだが、現状、どの処理系も基本的に(特にR7RSサポートについては)evolvingという状況なので、コスト削減のためにサポート無しとした。そもそもKawaのように現状のリリース版ではyuniを実行できないものも有る。
例えば.tar.gzを直接リポジトリに置くことはしていない。これはトラブルシュートのためにソースコードを直接修正したり、そのdiffを取ったりすることが多いため。昔だったらquitのようなツールがあったが、今やGitで十分。

  • ネットワークの利用

ビルド時は安全諸々の理由でネットワークの利用は避けたかったが、ChickenとRacketではネットワーク上のリポジトリからパッケージをインストールしているし、Larcenyに至ってはstableバイナリをダウンロードしている
カバレッジ分析等のためには当然ソースから全てをビルドする方が有利なので、処理系については可能な限りバイナリを直接取り扱うのを避けたいところ。

  • バイナリパッケージの利用

処理系本体以外のライブラリはOSのパッケージシステムから拾ってくることを原則とした。このため、CentOS6のようにバージョン都合でサポートできていないディストリビューションが存在する。
困るのはWindowsで、Windowsにはパッケージシステムが基本的に存在しないので殆どの処理系が対応できていない。仕方が無いのでMSYS2 + MinGWだけで見切り発射してしまおうかと考えている。

追加したいこと

予算の都合が付けば自動ビルド化したいと考えているが、今のところ1発のビルドでも2時間とか掛かっている(ただし殆どがGuileのビルド時間)ので週一を目安に気が向いたときに回している。
目下設計中なのは3点:

  • ビルドリプレイによる警告の回収

ビルド中に発生している警告類を、ビルドをリプレイすることによって回収しようとしている。clangのようにstatic analyzerが有る場合はそれらを適用することも考えている。
リビジョン間やビルドコンフィギュレーション間で新規に出現する警告をキャッチすることで、バグの捕捉に役立つかもしれない。

適当なWebサイトを作ってソースコードブラウザを用意したい。...本当は自作したくないんだけど、あまり良いサービスが見当たらなかった。
警告なんてビルドのリプレイじゃなくてビルド中に捕捉すれば良いじゃん説は有るが、ソースコードのブラウズ情報を効率的に収集するために結局"gccビルドをclangでリプレイ"しないといけないため、リプレイを前提にしている。

yunibaseではビルド時にテストも実行しているが、これはテストカバレッジの収集を睨んでのこと。
もちろんカバレッジSchemeコードのものも収集したいが、Guile( http://www.gnu.org/software/guile/manual/html_node/Code-Coverage.html#Code-Coverage )くらいしか直接的にサポートしている処理系は無いかもしれない。