rake for mruby

シンプルな英語

何をとち狂ったか、図書館から借てきた(講談社現代新書)。面白くなかったら素直に返却するだけですから。 が、めっぽう面白かった。と言うより、ためになった。

50年前にこの本に出会っていたら、オイラーの人生変っていたかも。どう変っていたか想像するに、シリコンバレーの近くで、カリフォーニアワインの製造。ガレージで趣味のパソコンを売出しとかね。それからジョブ・チェンジですよ。。。

英語の文型は、SV、SVO、SVCがマスター出来れば、取り敢えずOK。 Sは主語、Vは述語の動詞、Oは目的語、Cは主語を説明する補語になる形容詞または名詞。

著者は、特許翻訳の方。そのせいか例題が工業よりで、すらすらよめた。まあ、manを見てる雰囲気。

文を組み立てる時は、まず主語を決める。主語はIとかYOUとかの人物に限らない。色々な製品でも概念でも多様なものが主語になれる。そして一旦主語を決めたら、主語になりきって、主語の視点で動詞を決める。後は、OとかCは極端に言えば補足事項だ。

英文を読むこつは、動詞が何であるか見極める亊だと言う。ツィッターは、文字制限がある中での表現になる為、複雑な表現は少く、読解にはよいらしい。

著者が行う講習会で、英語はプログラミングに似ていると言った人がいたとか。 そりゃ逆だろ。最初に英語があった。それに語順を合わせたプログラミング言語が作られた。rubyが正にそうである。

irb(main):001:0> mystr = 'hello'
=> "hello"
irb(main):002:0> mystr.size()
=> 5

これ、前回やった。文字列に名前を付ける。名前を付ける目的は、後で使いたいから。代理と言うか代名詞って思えばいいのか。その文字列を主語にして、動詞はsize()だ。SVな文型だね。

irb(main):003:0> '12,34,56'.split( ',' )
=> ["12", "34", "56"]

もう一つ例題。動詞のsplitに目的語がついてる。コンピュータは馬鹿だから、分割しろと指示されても、どこで分割していいか解らない。そこで目的語を付け足してやるのさ。SVOって文型になるな。

rubyのプログラミングって、英語の文型そのものじゃん。ああ、rubyの特徴は、オブジェクト指向だな。主語のオブジェクトを主体にする。主語の視点で考えれば、色々な動詞が浮んでくるよ。大文字にしろとか、シンボルとして登録(Lispからの受売りでインターン、収監とかいう)とか、色々だ。

irb(main):008:0> 'abcaxswfrtwa'.count('a')
=> 3

文字列の中に指定した文字が幾つあったか数えろ、なんてものあったな。そういえば、日本人が不得手な、数詞の扱いも説明されてたぞ。水は数えられませんから、give me a waterと言うな。 言うなら、give me a cup of water て具合に測ってね。ちゃんとした辞書には、数えられる/数えられないの区別が載ってるそうだ。英語脳は論理的。

巻末にお勧めのサイトが掲載されてた。息抜きにどうぞ。

ルミナス英和・和英辞典 - 研究社 真面目な辞書 Cが付くと数えられる。Uだと数えられない。

ああ、この前にやったsqlite3の辞書にも、この区別がしてあった。

pen
    〈C〉(インクを用いる筆記具全体をさして)『ペン』;万年筆,ボールペン,フェルトペン;ペン先 / 
    〈U〉《the~》文筆,著述 / 〈C〉《単数形で》《文》文筆家 / 《気どって》…‘を'書く,著す(write)

ボールペンとかは、1本、2本と数えられるけど、ペン・クラブみたいに文筆業は数えられないとな。少しは利口になったな。

そしてAIの成果

「I'm sorry」の日本語訳は?→「アイム・ソーリー・ヒゲソーリー」

楽しい英語だな。OK -> よっしゃ は、許せる。これぞ漢な言葉だ。

small app

Source code (.c) を参考に、小さなアプリを作ってみる。書いたコードってかコピペしたC言語のそれは、下記。

~/mruby/z.c

#include <mruby.h>
#include <mruby/compile.h>

int main(void) {
  mrb_state *mrb = mrb_open();
  if (!mrb) { /* handle error */ }
  mrb_load_string(mrb, "puts 'hello MRUBY'");
  mrb_close(mrb);
  return 0;
}

そして、大事なコンパイル手順。インターネットを閲覧してると、ソースコードは載ってるけど、コンパイル手順が説明されていない、不完全なものが散見される。片手落もいい所。オイラーは、豪華に2種類も提示するからね。

cc -g -std=c99 z.c -Iinclude build/host/lib/libmruby.a -lm
 or
cc z.c `bin/mruby-config --cflags --libmruby-path` -lm

ここまで書いたら、実行例も載せないと、後ろ指さされそう。一応つまらないものですけど。 ああ、今Debianで実行してるけど、gccが普通だろう。debianの親心で、ccでも実行出来るようにセットアップされてるから、大丈夫。オイラーがラウンドロビンで、BSDを使う時も、コピペで済むようにしてるのさ。これぐらいなら、わざわざMakefileを起す必要ないからね。

sakae@deb:~/mruby$ ./a.out
hello MRUBY

libruby.a周辺を見ていたら、面白いものを発見したよ。 build/host/lib/libmruby.flags.mak

sakae@deb:~/mruby$ cat build/host/lib/libmruby.flags.mak
MRUBY_CFLAGS = -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -DMRB_USE_RATIONAL -DMRB_USE_COMPLEX -I"/home/sakae/mruby/include" -I"/home/sakae/mruby/build/host/include" -I"/home/sakae/mruby/mrbgems/mruby-time/include" -I"/home/sakae/mruby/mrbgems/mruby-io/include"
MRUBY_CC = gcc
MRUBY_LD = gcc
MRUBY_LDFLAGS =  -L/home/sakae/mruby/build/host/lib
MRUBY_LDFLAGS_BEFORE_LIBS =
MRUBY_LIBS = -lmruby -lm
MRUBY_LIBMRUBY_PATH = /home/sakae/mruby/build/host/lib/libmruby.a

しっかりgccって正しいコンパイラーが埋め込まれているぞ。BSDの場合は、ccが埋め込まれているんだろうね。その辺どうなってるの? 全部rake言語のDSLになってるから。まずは、そちらを攻略だな。

ええ、mrubyをコンパイルする時にログを取っていたんで、それをみたんだけど、例によって大事な所が隠されてますから。プンプン。

CC    src/version.c -> build/host/mrbc/src/version.o
CC    src/vm.c -> build/host/mrbc/src/vm.o
CC    mrbgems/mruby-compiler/core/codegen.c -> build/host/mrbc/mrbgems/mruby-compiler/core/codegen.o
CC    mrbgems/mruby-compiler/core/y.tab.c -> build/host/mrbc/mrbgems/mruby-compiler/core/y.tab.o
AR    build/host/mrbc/lib/libmruby_core.a
LD    build/host/mrbc/bin/mrbc
GEN   mrblib/*.rb -> build/host/mrblib/mrblib.c
      MRBC mrblib/00class.rb
           mrblib/00kernel.rb
           mrblib/10error.rb
           mrblib/array.rb
 :
CC    build/host/mrbgems/mruby-eval/gem_init.c -> build/host/mrbgems/mruby-eval/gem_init.o
CC    build/host/mrbgems/gem_init.c -> build/host/mrbgems/gem_init.o
AR    build/host/lib/libmruby.a
CC    mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c -> build/host/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.o
CC    mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c -> build/host/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.o
LD    build/host/bin/mrbc
INSTALL build/host/bin/mrbc -> bin/mrbc
INSTALL build/host/bin/mruby-config -> bin/mruby-config
CC    mrbgems/mruby-bin-mirb/tools/mirb/mirb.c -> build/host/mrbgems/mruby-bin-mirb/tools/mirb/mirb.o
LD    build/host/bin/mirb
 :

rake

少し勉強って亊で、 library rake をみている。そして見付たのさ。

rake -P
  :
rake /home/sakae/mruby/build/host/lib/libmruby.a
    /home/sakae/mruby/build/host/src/array.o
    /home/sakae/mruby/build/host/src/backtrace.o
     :
    /home/sakae/mruby/build/host/mrbgems/mruby-compiler/core/codegen.o
    /home/sakae/mruby/build/host/mrbgems/mruby-compiler/core/y.tab.o
    /home/sakae/mruby/build/host/mrblib/mrblib.o
    /home/sakae/mruby/build/host/mrbgems/mruby-compar-ext/gem_init.o
    /home/sakae/mruby/build/host/mrbgems/mruby-enum-ext/gem_init.o
    /home/sakae/mruby/build/host/mrbgems/mruby-string-ext/src/string.o
      :
    /home/sakae/mruby/build/host/mrbgems/mruby-eval/gem_init.o
    /home/sakae/mruby/build/host/mrbgems/gem_init.o
  :
rake /home/sakae/mruby/build/host/lib/libmruby.flags.mak
    /home/sakae/mruby/tasks/libmruby.rake
    /home/sakae/mruby/build/host/lib/libmruby.a
  :
rake benchmark
    /home/sakae/mruby/benchmark/default.png
rake benchmark/default
rake benchmark/default/host
rake benchmark/default/host/bm_ao_render.dat
    /home/sakae/mruby/benchmark/bm_ao_render.rb
    benchmark/default/host
    /home/sakae/mruby/build/host/bin/mruby
  :

rake … ってなってるのがターゲット、兼タスクだ。libruby.aは、array.oとかbacktrace.oとかから出来上がっていますって宣言。

benchmarkのタスクはdefault.pngを作る亊ですってのもあるな。

早速、ベンチマークしてみる。

sakae@deb:~/mruby$ rake benchmark
bm_ao_render...
bm_app_lc_fizzbuzz...
bm_fib...
bm_so_lists...
Benchmark results output to /home/sakae/mruby/benchmark/default.png

結果は棒グラフだった。それぞれの実行時間だろうね。

こちらは、一般の人が使う、タスクのリストだ。細かい中身はどうでもよいって向きにお勧め。

VBOX$ rake -T
rake all             # build all targets, install (locally) in-repo
rake clean           # clean all built and in-repo installed artifacts
rake deep_clean      # clean everything
rake doc             # generate document
 :

10分くらいでできるRakefile入門 こちらに、おおまかな説明があった。他にも出て来るけど、みんなレールがらみの、楽しようという魂胆がすけてみえるものばかりだ。まあ、それでいいんだけど。楽するためにあるんだから。

Rakefileの書き方

上記の入門を利用して、あーだこーだします。

[sakae@fb /tmp/t]$ cat Rakefile
desc 'Do task1'
task :task1 do
  puts 'task1'
end

desc 'Do task2'
task :task2 do
  puts 'task2'
end

desc 'Do task1 then task2'
task :task3 => [:task1, :task2] do
  puts 'task3'
  sh %{echo Hello Rakefile sh!}
end

rake特有な構文として、descによる説明文がある。それとtaskの宣言。実体はhashだな。keyがタスクの名前で、valueがタスクの連鎖(もし必要なら)を表しているっぽい。後は、普通のrubyだな。

ちょいと実行。

[sakae@fb /tmp/t]$ rake -T
rake task1  # Do task1
rake task2  # Do task2
rake task3  # Do task1 then task2
[sakae@fb /tmp/t]$ rake -P
rake task1
rake task2
rake task3
    task1
    task2

上記だと、タスクはシリアルに実行される。じゃ、パラレル実行をするには? 反対語を同時に調べておく亊が、幅を拡げる亊になる。正解はtaskの宣言をmultitaskにする亊だ。以上を踏まえて、辞書をあげておく。Rakefile Format

rakeの引数にタスク名を指定すれば、そのタスクが実行される。書き散らかしたやつを、Rakefileにまとめて整理するのは良い考えだな。

じゃ、

[sakae@fb /tmp/t]$ rake
rake aborted!
Don't know how to build task 'default' (See the list of available tasks with `rake --tasks`)

(See full trace by running task with --trace)

注意が出て来た。これをヒントすればいいんだな。:task3 ってしてた所を :default に変更。

[sakae@fb /tmp/t]$ rake
task1
task2
task3
echo Hello Rakefile sh!
Hello Rakefile sh!

じゃ、スクリプトを寄せ集めた時、引数を渡したくたくなる。そんな時には、こうする。

desc 'Do task1'
task :task1, [:first_name, :last_name] do |t, args|
  args.with_defaults(:first_name => "Bill", :last_name => "Joy")
  puts "First name is #{args.first_name}"
  puts "Last  name is #{args.last_name}"
end
[sakae@fb /tmp/t]$ rake task1
First name is Bill
Last  name is Joy
[sakae@fb /tmp/t]$ rake task1[Ken, Tompson]
rake aborted!
Don't know how to build task 'task1[Ken,' (See the list of available tasks with `rake --tasks`)

(See full trace by running task with --trace)
[sakae@fb /tmp/t]$ rake task1[Ken,Tompson]
First name is Ken
Last  name is Tompson

rakeの規約により、引数は空白無しで記述しなければならない。ちょっと窮屈だ。 そしてもう一つ窮屈なのは、合成したタスクの個々に引数を配分出来無い亊。まあ、諦めろ。

rakeの特徴はmake相当をrubyで実現する亊にある。makeのいの一番の機能は、ファイルをどうやって作成(コンパイル)するか? 既に出来上がっているファイルを再度作ってしまう無駄を省く亊だ。勿論、ソースファイルが変更されてたら、再度のコンパイルは必要。これを自動化するのが、makeの最大の特徴。

rakeもしっかり受け継いでいる。taskって所をfileに変更し、ターゲットにファイル名を指定すれば、そのファイルの作成、変更の自動化が出来る。

mrubyのRakefileを読む

実際に、実物がどうなってるか?

vbox$ grep task Rakefile
load "#{MRUBY_ROOT}/tasks/core.rake"
load "#{MRUBY_ROOT}/tasks/mrblib.rake"
load "#{MRUBY_ROOT}/tasks/mrbgems.rake"
load "#{MRUBY_ROOT}/tasks/libmruby.rake"
load "#{MRUBY_ROOT}/tasks/bin.rake"
load "#{MRUBY_ROOT}/tasks/presym.rake"
load "#{MRUBY_ROOT}/tasks/test.rake"
load "#{MRUBY_ROOT}/tasks/benchmark.rake"
load "#{MRUBY_ROOT}/tasks/doc.rake"
task :default => :all
task :all => :gensym do
task :build => MRuby.targets.flat_map{|_, build| build.products}
task :clean do
task :deep_clean => %w[clean doc:clean] do

mrubyのTopにあるやつ。ファイルが肥大にならないように、要素毎に分て、メンテナンス性をあげているんだな。

core.rake

as_cxx_srcs = %w[vm error gc].map{|name| "#{MRUBY_ROOT}/src/#{name}.c"}

MRuby.each_target do
  objs = Dir.glob("#{MRUBY_ROOT}/src/*.c").map do |src|
    if cxx_exception_enabled? && as_cxx_srcs.include?(src)
      compile_as_cxx(src)
    else
      objfile(src.pathmap("#{build_dir}/src/%n"))
    end
  end
  self.libmruby_core_objs << objs
end

着本的な亊を忘れてしまっていた。リテラルの類なんだな。

irb(main):001:0> %w[vm error gc]
=> ["vm", "error", "gc"]

それから、%n て何だ? 拡張子なしのファイル名だってさ。instance method String#pathmap time系のそれと言い、perlとどっこい、どっこいだな。

それから、ほとんどのrakeファイルで、MRubyってのを参照してるんだけど、何処で定義されてる?

lib/mruby/build.rb

require "mruby/core_ext"
require "mruby/build/load_gems"
require "mruby/build/command"

module MRuby
  autoload :Gem, "mruby/gem"
  autoload :Lockfile, "mruby/lockfile"
  autoload :Presym, "mruby/presym"
   :

長い文章なので、オンデマンドでよい亊にして、概要だけを捉えておく。

vbox$ wc ./lib/mruby/build.rb
     565    1390   15379 ./lib/mruby/build.rb
vbox$ egrep class ./lib/mruby/build.rb
  class << self
  class Toolchain
    class << self
  class Build
    class << self
      dst = "#{self.class.install_dir}/#{File.basename(src)}"
      build = self.class.new(name, internal: true){}
  class CrossBuild < Build

これが、舞台裏って訳か。大変だ能。たまたま、build.rbを採り上げたけど、Mrubyのモジュールはこれだけではない。他の多数のファイルもMRubyモジュールの一員になってる。lib/mruby/ の下が、全部MRubyのモジュールという、壮大さだ。コードを読む時は注意されたし。

それから、tasksの中にtoolchianが有り、その中に、コンパイラーの設定(gcc,clang,…)が置かれていた。gccが基本っぽい。ちょいと覗く。

MRuby::Toolchain.new(:gcc) do |conf, params|
  default_command = params[:default_command] || 'gcc'
  compiler_flags = %w(-g -O3 -Wall -Wundef)
  c_mandatory_flags = %w(-std=gnu99)
  cxx_invalid_flags = %w(-Werror-implicit-function-declaration)
  compile_opt = '%{flags} -o "%{outfile}" "%{infile}"'
   :

オイラーの楽しみ設定はここにあった。使うには -O3 がいいけど、観光するには、-O0 だな。

究極の追跡は、こうだ。

vbox$ rake -t
 ** Invoke default (first_time)
 ** Invoke all (first_time)
 ** Invoke gensym (first_time)
 ** Invoke /tmp/mruby-3.1.0/build/host/presym (first_time)
 ** Invoke /tmp/mruby-3.1.0/build/host/src/array.pi (first_time)
 ** Invoke /tmp/mruby-3.1.0/src/array.c (first_time, not_needed)
 ** Invoke /tmp/mruby-3.1.0/build_config/default.rb (first_time, not_needed)
 ** Execute /tmp/mruby-3.1.0/build/host/src/array.pi
clang -E -P -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -DMRB_USE_RATIONAL -DMRB_USE_COMPLEX -I"/tmp/mruby-3.1.0/include" -I"/tmp/mruby-3.1.0/build/host/include" -DMRB_PRESYM_SCANNING -o "/tmp/mruby-3.1.0/build/host/src/array.pi" "/tmp/mruby-3.1.0/src/array.c"
mkdir -p /tmp/mruby-3.1.0/build/host/src
CPP   src/array.c -> build/host/src/array.pi
 ** Invoke /tmp/mruby-3.1.0/build/host/src/backtrace.pi (first_time)
  :

bin/mruby-config

おまけで、mruby-configの中身。

vbox$ cat bin/mruby-config
 :
while [ $# -gt 0 ]; do
  case $1 in
    --cc) echo clang;;
    --cflags) echo -std=gnu99 -g -O0 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -DMRB_USE_RATIONAL -DMRB_USE_COMPLEX -I"/tmp/mruby-3.1.0/include" -I"/tmp/mruby-3.1.0/build/host/include" -I"/tmp/mruby-3.1.0/mrbgems/mruby-time/include" -I"/tmp/mruby-3.1.0/mrbgems/mruby-io/include";;
    --ld) echo clang;;
    --ldflags) echo  -L/tmp/mruby-3.1.0/build/host/lib;;
    --ldflags-before-libs) ;;
    --libs) echo -lmruby -lm;;
    --libmruby-path) echo /tmp/mruby-3.1.0/build/host/lib/libmruby.a;;
    *) print_help;;
  esac
  shift
done

このコマンドを冒頭で使ってみた。コンパイルした場所やトップdirの名前が埋め込まれているので注意。ようするに、 置かれた場所で咲きなさい、って亊です。ちなみに、オイラーが得意の/tmpでzipファイルを展開、ソースの観光用に、-O0 したもの。-O0 だと、コンパイラーが努力しないので、最速でmrubyシステムが出来上がるぞ。


This year's Index

Home