Samtoolsとmrubyでタヌキを表示する

```
　　　　__,-─-､__
　　　（〆-─-ヽ)
　　　 （ ´・ω・｀ ）  < Samtools ト mruby デ タヌキ ヲ ヒョウジ スル！
　　　/ 　,r‐‐‐､ヽ
　 　 し　ｌ　 x　）J
　　　_.'､ ヽ　 ノ.人
　(_((__,ノＵ´U. (酒)

```

## はじめに

前回までのあらすじ。Samtoolsにタヌキコマンドを追加して、タヌキを表示した。

https://qiita.com/kojix2/items/8f3c67acc1ae56fbb9cb

今回の記事はこの続きになります。

## mrubyとは？

[mruby](https://github.com/mruby/mruby)は組み込み用の小さなRubyです（よくわかっていないので適当な表現）。通常のRubyのように利用することもできますが、C言語等で作られた実行形式のファイルにmrubyを組み込んで使います。組み込まれたツールは、Rubyのコードを評価することができるようになります。組み込みというと、家電の中のマイコン等で動いているようなイメージがありますが、mrubyはマイコンで動かすには大きすぎると思われ、一般的なコンピュータで利用するプログラムに組み込んで使う用途が多いと思われます。

マイコンや小型のデバイスでRubyを実行したい場合は、[mruby/c](https://github.com/mrubyc/mrubyc)や、[picoruby](https://github.com/picoruby/picoruby)など、mrubyよりもさらに小さなRuby実装があり、そういったものが好まれるようです。

## コマンドラインツールにmrubyを組み込みたい

私は次世代シーケンサーのファイル形式をRubyから操作したいと思い、[HTSlib](https://github.com/samtools/htslib)の[ラッパーライブラリ](https://github.com/kojix2/ruby-htslib)を作成しています。これは主にRubyからC言語の関数を呼び出そうという作戦です。しかし、実際にはRubyで記述したい部分はそれほど多くありません。大半がCのコードで、一部分だけRubyを使います。それならば、逆のアプローチがあってもいいと思います。C言語で書かれた既存のコマンドラインツールのうち、本当に必要な部分だけ、mrubyを組み込んでしまうという方法も考えられます。

ここでは、ごく簡単にSamtoolsにmrubyを組み込んで、mrubyのputsメソッドでタヌキを表示してみようと思います。

## mrubyを準備する

samtoolsが置いてあるディレクトリと同じ階層に、Gitからmrubyをクローンします。

```sh
git clone https://github.com/mruby/mruby
git checkout 3.1.0 # 本文作成時の最新版
git switch -c v3.1.0
```

mrubyをビルドして、静的ライブラリを作成します。

```
make
```

静的ライブラリ `build/host/lib/libmruby.a` が作成されたことを確認します。

Samtools をビルドするときに、この `libmruby.a` をリンクすることで、samtoolsからmrubyを使えるようにします。

通常のRuby（つまりCRubyまたはMRI）とは違い、mrubyはあとから動的に外部のライブラリGemを読み込むことができないそうで、あらかじめmrubyをコンパイルするときにmgemを指定しておくそうです。その際には `build_config.rb` で利用するmgemを指定するそうです。今回はmgemを使わないのでデフォルトのままにします。

## SamtoolsのMakefileをいじる

https://github.com/kojix2/samtools/commit/945083e73c17be9173bc85bc2a09102e948ee532

C言語がよくわかっていないのですが、samtoolsがlibmruby.aをリンクするようにMakefileを編集します。
tanuki.o を作る際に、`-L ../mruby/build/host/lib/ -I ../mruby/include -lmruby -lm` を指定します。
（tanuki.c については[以前の記事](https://qiita.com/kojix2/items/8f3c67acc1ae56fbb9cb)を参照ください）

```diff
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ AOBJS=      bam_aux.o bam_index.o bam_plcmd.o sam_view.o bam_fastq.o \
             bam_tview.o bam_tview_curses.o bam_tview_html.o bam_lpileup.o \
             bam_quickcheck.o bam_addrprg.o bam_markdup.o tmp_file.o \
             bam_ampliconclip.o amplicon_stats.o bam_import.o bam_samples.o \
-            bam_consensus.o consensus_pileup.o reference.o
+            bam_consensus.o consensus_pileup.o reference.o tanuki.o
 LZ4OBJS  =  $(LZ4DIR)/lz4.o
 
 prefix      = /usr/local
@@ -142,7 +142,7 @@ LIBST_OBJS = sam_opts.o sam_utils.o bedidx.o bam.o
 
 
 samtools: $(AOBJS) $(LZ4OBJS) libst.a $(HTSLIB)
-	$(CC) $(ALL_LDFLAGS) -o $@ $(AOBJS) $(LZ4OBJS) libst.a $(HTSLIB_LIB) $(CURSES_LIB) -lm $(ALL_LIBS) -lpthread
+	$(CC) $(ALL_LDFLAGS) -o $@ $(AOBJS) $(LZ4OBJS) ../mruby/build/host/lib/libmruby.a libst.a $(HTSLIB_LIB) $(CURSES_LIB) -lm $(ALL_LIBS) -lpthread
 
 # For building samtools and its test suite only: NOT to be installed.
 libst.a: $(LIBST_OBJS)
@@ -202,6 +202,10 @@ faidx.o: faidx.c config.h $(htslib_faidx_h) $(htslib_hts_h) $(htslib_hfile_h) $(
 padding.o: padding.c config.h $(htslib_kstring_h) $(htslib_sam_h) $(htslib_faidx_h) $(sam_opts_h) $(samtools_h)
 phase.o: phase.c config.h $(htslib_hts_h) $(htslib_sam_h) $(htslib_kstring_h) $(sam_opts_h) $(samtools_h) $(htslib_hts_os_h) $(htslib_kseq_h) $(htslib_khash_h) $(htslib_ksort_h)
 reference.o: reference.c config.h $(htslib_sam_h) $(htslib_cram_h) $(samtools_h) $(sam_opts_h)
+
+tanuki.o:
+	$(CC) tanuki.c -c -o $@ -L ../mruby/build/host/lib/ -I ../mruby/include -lmruby -lm
+
 sam_opts.o: sam_opts.c config.h $(sam_opts_h)
 sam_utils.o: sam_utils.c config.h $(samtools_h)
 sam_view.o: sam_view.c config.h $(htslib_sam_h) $(htslib_faidx_h) $(htslib_khash_h) $(htslib_kstring_h) $(htslib_thread_pool_h) $(htslib_hts_expr_h) $(samtools_h) $(sam_opts_h) $(bam_h) $(bedidx_h)
```

## mrubyでタヌキを表示する部分のコード

初心者なので、難しいことは考えずに、`mrb_load_string` でタヌキをputsするようにしました。

```c:tanuki.c
#include <mruby.h>
#include <mruby/compile.h>

#include <stdlib.h>
#include <stdio.h>

int main_tanuki(void)
{
    mrb_state *mrb;
    mrb = mrb_open();
    mrb_load_string(mrb, "puts \""
"　　　　__,-─-､__\n"
"　　　（〆-─-ヽ)\n"
"　　　 （ ´・ω・｀ ）\n"
"　　　/ 　,r‐‐‐､ヽ\n"
"　 　 し　ｌ　 x　）J\n"
"　　　_.'､ ヽ　 ノ.人\n"
"　(_((__,ノＵ´U. (酒)\n\"");
    mrb_load_string(mrb, "puts 'Tanuki in mruby'");
    mrb_close(mrb);

    return 0;
}
```

## ビルド ＆ 実行

```
autoheader
autoconf -Wno-syntax
./configure
make
```

```
samtools tanuki
```

タヌキが表示されました。

```
　　　　__,-─-､__
　　　（〆-─-ヽ)
　　　 （ ´・ω・｀ ）
　　　/ 　,r‐‐‐､ヽ
　 　 し　ｌ　 x　）J
　　　_.'､ ヽ　 ノ.人
　(_((__,ノＵ´U. (酒)
Tanuki in mruby
```

## mruby が klib/kexpr よりも輝ける場所は存在するか？

BamやBcfファイルに対して、細かい条件を付加してカウントしたりフィルタリングしたりといった用途を実行するためのツールを作成するときに、C言語のライブラリである [klib/kexpr](https://github.com/attractivechaos/klib) が用いられます。これは、C言語のライブラリで、実行時に与えた式を評価してくれます。しかし、mrubyを用いれば、遥かに柔軟な式の評価が可能になるでしょう。高機能なkexprとしてmrubyが活躍できる場所がないか探してみたいと考えています。

この記事は以上です。