mrruby-sqlite
FreeBSD upgrade
root@fb:~ # freebsd-update upgrade -r 13.1-RELEASE : The following components of FreeBSD seem to be installed: kernel/generic kernel/generic-dbg src/src world/base The following components of FreeBSD do not seem to be installed: world/base-dbg Does this look reasonable (y/n)? y Fetching metadata signature for 13.1-RELEASE from update2.freebsd.org... done. : marge some file by hand root@fb:~ # freebsd-update install Kernel updates have been installed. Please reboot and run "/usr/sbin/freebsd-update install" again to finish installing updates. shutdown -r now
root@fb:~ # freebsd-update install Installing updates...Scanning //usr/share/certs/blacklisted for certificates... Scanning //usr/share/certs/trusted for certificates... Scanning //usr/local/share/certs for certificates... done. root@fb:~ # freebsd-update install No updates are available to install. Run '/usr/sbin/freebsd-update fetch' first. shutdown -r now
コンパイルの準備
前回Rakefileをみた時、いきなりモジュールMRubyが使われていてびっくりした。どんなタイミングでbuild.rb等のMRuby構成要素を読込みしてるのだろう?
こういう時は、トレースするに限るな。
sakae@deb:/tmp/mruby$ strace -o SLOG rakae
まずはRakefileがオープンされて、それからbuild.rbがオープンされてる。
sakae@deb:/tmp/mruby$ egrep '(Rakefile|build.rb)' SLOG | grep -v 'No such' | grep open openat(AT_FDCWD, "/tmp/mruby/Rakefile", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/Rakefile", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5
他の構成要素はどうだ? なるべくユニークなファイル名を指定してあげる。
sakae@deb:/tmp/mruby$ egrep '(presym.rb|build.rb)' SLOG | grep -v 'No such' | grep open openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/build.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/presym.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5 openat(AT_FDCWD, "/tmp/mruby/lib/mruby/presym.rb", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 5
やっぱり、大本のRakefileを見るのが基本。
MRUBY_ROOT = File.dirname(File.expand_path(__FILE__)) $LOAD_PATH << File.join(MRUBY_ROOT, "lib") # load build systems require "mruby/core_ext" require "mruby/build"
冒頭はこんな風に始まっている。そしてオイラーは冒頭で躓いた。
sakae@deb:/tmp/mruby$ irb irb(main):001:0> File.dirname(File.expand_path(__FILE__)) => "/tmp/mruby" sakae@deb:/tmp/mruby$ cd src/ sakae@deb:/tmp/mruby/src$ irb irb(main):001:0> File.dirname(File.expand_path(__FILE__)) => "/tmp/mruby/src"
なる程、pwdのruby表現なのね。で、得られたdirにlibを結合して、それををロードパスに追加する。後は、requireしてるのね。なる程。読込みがまだまだ、浅いな。
racc
mrubyのコンパイルログをみていると、rubyのコードをコンパイルする装置を用意してる。どうやら、これがそうみたい。
vbox$ ls /tmp/mruby/mrbgems/mruby-compiler core/ mrbgem.rake p/mruby/mrbgems/mruby-compiler/core/ < codegen.c keywords lex.def node.h parse.y y.tab.c
binsonとかで、ごにょごにょするやつ。難しいみたいなので、rubyの同等品を当ってみる。
Racc で遊ぼう なにか、昔流行した256倍シリーズの本を彷彿させるな。 ちょいと遊んでみたよ。こういう道草も楽しいな。
mruby-sqlite
mrubyのHPにmruby用のgemの集積が案内されてる。つらつら見ていたら、面白そうなものを発見した。 mruby-sqlite
これはもう試してみるしか。buildの案内はこうなってた。
Download or clone MRuby
Add the mruby-sqlite gem declaration to MRuby's build_config.rb file.
Use conf.gem :github => 'jbreeden/mruby-sqlite' to have MRuby checkout
the lasest version automatically before building.
Use conf.gem 'PATH/TO/mruby-sqlite' if you've already downloaded this
repo.
Run rake in MRuby's root directory.
build_config.rb に設定をしろって亊だけど、これは古い。現代のmrubyでは、こちらに設定を行う。あらかじめ、塊をDLしておいた。
[sakae@fb /tmp/mruby]$ cat build_config/default.rb : # include the GEM box conf.gembox 'default' conf.gem '/tmp/mruby/mruby-sqlite'
そして、mrubyを最初からコンパイルする。後で設定を付け足しでもダメ。一気に組込むのがmrubyの流儀。rake clean; rake てやるのが正解。
[sakae@fb /tmp/mruby]$ rake : LD build/host/bin/mirb ld: error: undefined symbol: pthread_create >>> referenced by sqlite3.c:22701 (mruby-sqlite/src/sqlite3.c:22701) >>> sqlite3.o:(sqlite3ThreadCreate) in archive /tmp/mruby/build/host/lib/libmruby.a clang: error: linker command failed with exit code 1 (use -v to see invocation) rake aborted! Command failed with status (1): [clang -o "/tmp/mruby/build/host/bin/mirb"...] /tmp/mruby/lib/mruby/build/command.rb:37:in `_run' /tmp/mruby/lib/mruby/build/command.rb:217:in `run' /tmp/mruby/tasks/bin.rake:17:in `block (4 levels) in <top (required)>' /tmp/mruby/Rakefile:42:in `block in <top (required)>' Tasks: TOP => build => /tmp/mruby/bin/mirb => /tmp/mruby/build/host/bin/mirb (See full trace by running task with --trace)
んが、エラーだよ。追加したsqliteで名前解決出来無いとな。でも、よくみるとスレッドが作る方法が不明だよのエラーだ。
野生の勘を働かせて、 mruby-thread も一緒にコンパイル。これで、上手くいった。本当に使えるか実験。
sql.rb 2022-04-17(App by webrick) からコピペ。但し、require 'sqlite3' は不要。mrubyでは、既に組込まれているから。
[sakae@fb /tmp/mruby]$ cat sql.rb
def search(sw)
ew = sw + "%"
db = SQLite3::Database.new("/usr/share/dict/ejdict.sqlite3")
stmt = db.prepare("SELECT word,mean FROM items WHERE word like ? limit 3")
stmt.bind_param(1, ew)
stmt.execute().each do |res|
puts("<dt>#{res[0]}</dt>\n")
puts("<dd>#{res[1]}</dd>\n")
end
stmt.close(); db.close()
end
puts search("ruby")
[sakae@fb /tmp/mruby]$ bin/mruby sql.rb <dt>ruby</dt> <dd>〈C〉紅玉,ルビー / 〈U〉ルビー色,深紅色(しんこうしょく) / ルビー色の,深紅の, 本発のプログラミング言語</dd> true
やったね!!
mirbでも当然requireは使えないし、頼みの綱のloadも無い。上の場合だと、関数をコピペで入力してあげよう。
尚、多数のgemが公開されてるけど、みんな5年以上前のものばかりだ。ひょっとしたら現代のmrubyで動かないものもあるかも知れない。
OpenBSDでも、同じ亊をやってみた。ええ、ちょっと確認するのを忘れていたので。
vbox$ ldd bin/mruby
bin/mruby:
Start End Type Open Ref GrpRef Name
163ca000 363da000 exe 2 0 0 bin/mruby
05ef5000 25ef7000 rlib 0 1 0 /usr/lib/libm.so.10.1
0dfe1000 2dfe3000 rlib 0 1 0 /usr/lib/libpthread.so.26.1
05bc4000 25bd5000 rlib 0 1 0 /usr/lib/libc.so.96.1
0a6d2000 0a6d2000 ld.so 0 1 0 /usr/libexec/ld.so
sqlite3は内部に格納してるけど、pthreadはシステムのそれを借用してるのね。まあ、sqlite3は組み込みを目指しているから、組み込んでしまえって亊なんでしょうね。
それから、sql.rbは、関数なんだけど、括弧が省けるのか? これも疑問なんで確認。
vbox$ bin/mirb
mirb - Embeddable Interactive Ruby Shell
> def search(sw)
* ew = sw + "%"
* db = SQLite3::Database.new("/usr/share/dict/ejdict.sqlite3")
:
* stmt.close(); db.close()
* end
=> :search
> search 'ruby'
<dt>ruby</dt>
<dd>〈C〉紅玉,ルビー / 〈U〉ルビー色,深紅色(しんこうしょく) / ルビー色の,深紅の,
本発のプログラミング言語</dd>
=> true
on debian
ついでなので、毛色の違う、リナ系でもやってみる。
sakae@deb:/tmp/mruby$ rake : LD build/host/bin/mirb /usr/bin/ld: /tmp/mruby/build/host/lib/libmruby.a(sqlite3.o): in function `unixDlOpen': /tmp/mruby/mruby-sqlite/src/sqlite3.c:31507: undefined reference to `dlopen' :
こいつも、問題児だな。ダイナミックロードは、ほぼ必須だと言うのに、デフォの設定に入っていない。いや、それは違うだろう。組み込み系なんで、リナだけ特別扱いはしませんと言う、大人の配慮がなされている。
tasks/toolchains/gcc.rake に、デフォを増やす設定を追加。
linker.libraries = %w(m dl)
これで、ちゃんと、辞書アプリも動くようになった。
sakae@deb:/tmp/mruby$ ldd bin/mirb
linux-gate.so.1 (0xb7f8a000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb7c93000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7c8d000)
libreadline.so.8 => /lib/i386-linux-gnu/libreadline.so.8 (0xb7c3a000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7c18000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7a2f000)
/lib/ld-linux.so.2 (0xb7f8c000)
libtinfo.so.6 => /lib/i386-linux-gnu/libtinfo.so.6 (0xb7a06000)
libdlが組込まれている。mirbの場合は、追加でlibreadlineも加わるのか。
mruby-threadを組み込みしたけど、linker.libraries の所に、pthreadを追加した方がよさげだな。
(m)ruby with gnuplot
上でraccを調べていた時、プログラムで遊ぼうというサイトに行き着いたんだ。面白いやつがあったので、ちょっとやってみる。
題材は、奥村晴彦著「C 言語による最新アルゴリズム事典」 から、風が吹けば桶屋が儲かるじゃなくて、アマゾンで蝶が羽搏けば、日本が大嵐になる(かもしれない)という有名なやつ。
なんてのは、表の説明。気象予報士の試験に出る、何故、天気予報は当らないを、素人に説明するためのやつです。
大気循環モデル。どんな風に大気が動くか、時間を追って計算。桜の花弁なり枯葉が、風に舞ってどう動くかを説明する式。花弁の位置は、3次元の座標 x,y,z で表わす、結果になる。
A,B,Cは、気温の傾斜だとか、気圧の傾きだとか、要する風の発生原因を表すパラメータだ。 花弁は、初期状態の位置、x=y=z=1の原点にある。そこから、細かい時間区切Dによって、少しづつ、系を更新してくって亊で、計算を進行させる。
やってみると分るけど、パラメータがちょっと違っただけで、とんでもなく異った結果になる。 いかにパラメータをちゃんと計測しても、何らかの誤差が内在してる。そのため、結果がバラケル亊になるよって亊だ。こればかりは、どんなスパコンをもってしても、解決は出来無い。
/***********************************************************
lorenz.c -- Lorenz (ローレンツ) アトラクタ
***********************************************************/
#include "plotter.c" /* ラージモデルでコンパイル */
#define A 10.0
#define B 28.0
#define C (8.0 / 3.0)
#define D 0.01
int main()
{
int k;
double x, y, z, dx, dy, dz;
gr_on(); gr_window(-30, 0, 30, 60, 1, GREEN);
x = y = z = 1;
for (k = 0; k < 3000; k++) {
dx = A * (y - x);
dy = x * (B - z) - y;
dz = x * y - C * z;
x += D * dx; y += D * dy; z += D * dz;
if (k > 100) draw(x, z); else move(x, z);
}
hitanykey();
return EXIT_SUCCESS;
}
これをruby語に翻訳する。翻訳なんておこがましい。ちょっと変換しただけだ。知らない間に、微分方程式の数値解を求めてる。pythonでAIは、うんざりだからね。
require "plotter"
A = 10.0
B = 28.0
C = (8.0 / 3.0)
D = 0.01
def main()
gr_on()
x = y = z = 1
for k in 0..3000 do
dx = A * (y - x)
dy = x * (B - z) - y
dz = x * y - C * z
x += D * dx; y += D * dy; z += D * dz
if k > 100 then draw(x, z) else move(x, z) end
end
gr_off()
end
main()
ドライバーは、gnuplotの駆動ルーチンだ。 /usr/local/lib/ruby/site_ruby/ とかに入れておくと、寂る亊なく未来永劫で使えるぞ。
それから、翻訳の過程で、 gr_window( ... ) を省略しちゃった。これは、X、Y軸の範囲を固定するもの。gnuplotは、範囲指定がないと、自動で決定してくれる。
# coding: utf-8
#/***********************************************************
# plotter.rb -- グラフィックス
#***********************************************************/
# プロッタのシミュレーション
$xpen = 0; $ypen = 0; # ペンの現在位置
def gr_on
$gplot = IO.popen("gnuplot -persist", "w")
$gplot.puts 'plot "-" w l'
end
def gr_off
$gplot.puts "end"
$gplot.close
end
def gr_wline(x1, y1, x2, y2)
$gplot.puts "#{x1} #{y1}\n#{x2} #{y2}\n\n"
end
def move(x, y) # ペンアップで移動
$xpen = x; $ypen = y
end
def move_rel( dx, dy) # 同上 (相対座標)
$xpen += dx; $ypen += dy
end
def draw(x, y) # ペンダウンで移動
gr_wline($xpen, $ypen, x, y)
$xpen = x; $ypen = y
end
def draw_rel( dx, dy) # 同上 (相対座標)
gr_wline($xpen, $ypen, $xpen + dx, $ypen + dy)
$xpen += dx; $ypen += dy
end
# sample usage
=begin
gr_on
gr_wline(0, 0, 10, 10)
move(5, 0)
draw_rel(0, 5)
gr_off
=end
mirbに最初、plotter.rbを貼り付けてから、次に本体を貼り付けたら、ちゃんとグラフが出てきた。素晴しい。
vbox$ bin/mirb : > def draw_rel( dx, dy) # 同上 (相対座標) * gr_wline($xpen, $ypen, $xpen + dx, $ypen + dy) * $xpen += dx; $ypen += dy * end => :draw_rel > gr_on => nil > for x in 0 .. 100 do * draw x, Math.sqrt(x) * end => 0..100 > gr_off => nil
こんな風に、貼り付けてから、手動でグラフを描画しても、勿論OK。matplotだかの使いかたを思い出すより、よっぽど楽です。
plotter 改造
上で使ったのは、プロッターをシュミレーションする技術。勿論デバイスは、gnuplotだ。
でも、今や2次元平面を描画するだけじゃなくて、3Dが大流行。そう3Dプリンターね。 宇宙へ持っていって、そこで工作しましょって時代。 宇宙空間において3Dプリンターで人工衛星アンテナを製造する技術を開発
地上にいる我々も、3Dプロッターしたい。 そんな目論見もあって、あえて基礎になるコードを展開させてもらってる。作者様には感謝の念がたえません。
デバイス本体が対応してるか? 答はYES。protってなってる所を、splotに変更するのがまずは必要。後は、x, y とかしてる所を、x, y, z に変更するだけ。おっと、$zpen = 0 も忘れないでね。機械的な変更だから、容易に出来るだろう。
2Dと3Dを同居させるなら、draw(x,y) draw3(x,y,z) ぐらいで片づけるのいいかな。
gr_on の所は、 gr3_on ね。'splot "-" w l' 3Dでプロットしてね。"-" は、データは、パイプからの奴を使ってね。w l は省略形。正式に書くと、with line 線グラフって亊。
パイプからのデータ流し込みが終ったら、end で、教えてあげる。これは、2Dも3Dも一緒だ。 共通に使える。