mruby 2.0.0 を Mac で動かしてみる

 以前少し触ったことはあったのですが、まともに触ったことがなかったのと、先日まつもとゆきひろさんとお話しさせていただいたときに mruby の話題もあったので、改めて mruby に触ってみました。とりあえず Mac 上で動かすところまでやってみたのでまとめてみます。

mruby とは

 mruby とは組み込み等でハードウェアリソースが限られる環境でも動かせるように、メモリ消費量を小さくした Ruby 実装です。また、様々なアプリケーションにも組み込みやすくなっています。メモリ消費量を抑えるために実装されている機能は絞られていますので、普通の Ruby 実装では使えても mruby では使えないものも多くなっています。

 mruby の開発は GitHub の下記リポジトリで行われています。

github.com

 また、公式サイトも公開されています。

http://mruby.org/

mruby 2.0.0 リリース

 この記事を書いている数日前の 2018/12/11 に mruby 2.0.0 がリリースされていました。その前が 1.4.1 ですので、メジャーバージョンアップになります。

mruby 2.0.0 released

とりあえず動かしてみる

 mruby でのコードの実行方法については下記サイトでも紹介されていまして、今回参考にさせていただきました。

Executing Ruby code with mruby

 とりあえずデフォルトの構成で触ってみるということであれば、 mruby は rbenv でもインストールが可能です。下記のように rbenv のインストール可能なバージョンのリストに mruby 2.0.0 も既に含まれています。

$ rbenv install --list | grep mruby
  mruby-dev
  mruby-1.0.0
  mruby-1.1.0
  mruby-1.2.0
  mruby-1.3.0
  mruby-1.4.0
  mruby-1.4.1
  mruby-2.0.0

 これをインストールします。

$ rbenv install mruby-2.0.0
Downloading 2.0.0.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/fa495898d51130c69480a13e90df5dc18cb1a9d9a31836268a895989d902048f
Installing mruby-2.0.0...
Installed mruby-2.0.0 to /Users/akanuma/.rbenv/versions/mruby-2.0.0

 インストールされたら使用するバージョンを mruby 2.0.0 に設定します。

$ rbenv versions
* system (set by /Users/akanuma/.rbenv/version)
  2.0.0-dev
  2.3.1
  2.5.3
  mruby-2.0.0
$ rbenv local mruby-2.0.0
$ rbenv version
mruby-2.0.0 (set by /Users/akanuma/workspace/mruby_test/.ruby-version)
$ ruby -v
mruby 2.0.0 (2018-12-11) 

 mruby での REPL は mirb で、 irb を実行すると mirb が起動します。

$ irb
mirb - Embeddable Interactive Ruby Shell

> 

 irb と同じようにインタラクティブに mruby のコードを実行することができます。

> puts 'Hello World!'
Hello World!
 => nil

 ちなみに mirb での入力内容はコードが正しいかチェックされた後に、バイトコードにコンパイルされてから実行されているようで、二回のパースが必要ということのようです。

 REPL 以外にも、もちろんコードを書いたファイルを実行することもできます。下記は hello.rb というファイル内のコードを実行する例です。

$ cat hello.rb 
puts 'Hello World!'
$ ruby hello.rb 
Hello World!

 この方法も、実行時に rb ファイルの内容がバイトコードにコンパイルされます。実行時のコンパイルを避けるため、あらかじめコンパイルしておくことも可能です。 mrbc というコンパイラを使ってコンパイルします。生成されたバイナリファイルを実行する時には、それがバイナリファイルであると示すために -b オプションをつけて実行します。

$ mrbc hello.rb 
$ ls -l
total 16
-rw-r--r--  1 akanuma  staff  98 Dec 15 12:21 hello.mrb
-rw-r--r--  1 akanuma  staff  20 Dec 15 12:09 hello.rb
$ ruby -b hello.mrb 
Hello World!

mrbgems

 Ruby ではパッケージ管理システムとして RubyGems を使いますが、 mruby では gem コマンドはなく、 RubyGems の代わりに mrbgems を使用します。

$ gem
rbenv: gem: command not found

 mrbgems では RubyGems のように gem コマンドでインストールしたり require で読み込むのではなく、ビルド時の設定ファイルに使用するライブラリを設定して一緒にコンパイルし、起動時に全てロードします。そのためにはソースコードからビルドする必要がありますので、 GitHub からソースコードを clone してきます。

$ git clone https://github.com/mruby/mruby.git
$ cd mruby

 まずはそのままデフォルトの構成でビルドしてみます。ディレクトリの root で minirake を実行します。

$ ./minirake

 ビルドが成功すると、設定ファイルの内容に従って各環境用にビルドされたもののサマリが下記のように表示されます。 Included Gems の項目が、実際にコンパイルされた mrbgems の gem です。 mruby では環境に応じて必要なもののみを使えるように、 Ruby ではコア機能として組み込まれていたものも、 mruby では mrbgems として切り出して、必要に応じて使用するかどうかを切り替えられるようになっています。

Build summary:

================================================
      Config Name: host
 Output Directory: build/host
         Binaries: mrbc
    Included Gems:
             mruby-metaprog - Meta-programming features for mruby
             mruby-io - IO and File class
             mruby-pack - Array#pack and String#unpack method
             mruby-sprintf - standard Kernel#sprintf method
             mruby-print - standard print/puts/p
             mruby-math - standard Math module
             mruby-time - standard Time class
             mruby-struct - standard Struct class
             mruby-compar-ext - Enumerable module extension
             mruby-enum-ext - Enumerable module extension
             mruby-string-ext - String class extension
             mruby-numeric-ext - Numeric class extension
             mruby-array-ext - Array class extension
             mruby-hash-ext - Hash class extension
             mruby-range-ext - Range class extension
             mruby-proc-ext - Proc class extension
             mruby-symbol-ext - Symbol class extension
             mruby-random - Random class
             mruby-object-ext - Object class extension
             mruby-objectspace - ObjectSpace class
             mruby-fiber - Fiber class
             mruby-enumerator - Enumerator class
             mruby-enum-lazy - Enumerator::Lazy class
             mruby-toplevel-ext - toplevel object (main) methods extension
             mruby-compiler - mruby compiler library
             mruby-bin-mirb - mirb command
               - Binaries: mirb
             mruby-error - extensional error handling
             mruby-bin-mruby - mruby command
               - Binaries: mruby
             mruby-bin-strip - irep dump debug section remover command
               - Binaries: mruby-strip
             mruby-kernel-ext - Kernel module extension
             mruby-class-ext - class/module extension
             mruby-bin-mrbc - mruby compiler executable
================================================

 bin ディレクトリに mruby や mirb 等の実行ファイルが生成されますので、これを使ってみます。

$ bin/mirb
mirb - Embeddable Interactive Ruby Shell

> Time.now
 => Sat Dec 15 17:12:35 2018

 Time クラスもコア機能としては含まれていませんが、 mruby-time gem が含まれているので、 Time クラスを使用することができています。試しに ENV オブジェクトを使ってみると、 mruby では ENV はコアには含まれていないのでエラーになります。

> ENV
(mirb):2: uninitialized constant ENV (NameError)

 ENV は mrbgems の mruby-env を組み込むことで使用できるようになりますので、 build_config.rb に下記のように設定を追加します。

conf.gem :mgem => 'mruby-env'

 config.gem での指定は、 gem のタイプによって少々異なります。今回は mgem-list に含まれているので、上記のように gem 名のみで使用可能です。

 ちなみに複数の gem をひとまとめにしたものを GemBox と言い、デフォルトでは build_config.rb 内で default という GemBox が指定されていて、デフォルトで使用する gem が指定されています。

conf.gembox 'default'

 では再度 minirake でビルドします。

$ ./minirake

 Build Summary をみると mruby-env が含まれているのがわかります。

Build summary:

================================================
      Config Name: host
 Output Directory: build/host
         Binaries: mrbc
    Included Gems:
             mruby-env
〜〜〜以下略〜〜〜

 下記のように実行してみると、 ENV モジュールが使用できるようになっています。

$ export DEBUG="TRUE"
$ bin/mirb
mirb - Embeddable Interactive Ruby Shell

> ENV['DEBUG']
 => "TRUE"

 ちなみに mruby 2.0.0 での変更点の一つとして、メタプログラミング関連の機能は組込みシステム開発ではあまり使われないという前提のもと、 mruby-metaprog という gem に切り出されました。

1.4.1 と 2.0.0 の違い

 リリースノートの内容を参考に、 1.4.1 と 2.0.2 の違いを確認してみます。

 2.0.0 では基本的な言語の機能として、キーワード引数に対応しています。サンプルとして下記のようなコードを書いて hello.rb として保存します。

def hello(name: 'World')
  puts "Hello #{name}!"
end

hello(name: 'Hiro')

 これを mruby 2.0.0 で実行すると下記のように正しく実行されます。

mruby_test  $ ruby hello.rb
Hello Hiro!

 mruby 1.4.1 で試してみると下記のように Syntax Error になります。

$ ruby hello.rb 
hello.rb:1:15: syntax error, unexpected tLABEL_TAG, expecting ')'
SyntaxError: syntax error

 また、詳細は割愛しますが、下記の Core Libraries にもメソッドの追加等の変更が入っています。

  • mruby-kernel-ext

  • mruby-array-ext

  • mruby-string-ext

  • mruby-pack

  • mruby-sleep

 mrbgems のところでも少し触れましたが、メタプログラミング関連の機能はコア機能から切り出されて、 mruby-metaprog という gem が追加されています。

 その他メモリ使用量削減の対策が行われていたり、インタプリタにデバッグモード実行用のオプションや、ライブラリ読み込み用のオプションが追加されたりしています。詳細はリリースノートをご覧いただければと思います。

http://mruby.org/releases/2018/12/11/mruby-2.0.0-released.html

まとめ

 組込み開発についてはまだまだ素人なのですが、Ruby が組込みでも使えるようになってくるとハードルが低くなってきますね。 mruby 2.0.0 になって Ruby にさらに近づいてきましたし、より開発しやすくなってそうです。実際に組込み開発で使うには、これをマイコン上で動かせるようにビルドしていったり、深く知っていく必要があるかと思いますが、少しずつでも掘り下げていければと思っています。