青木日記

<前の日 | この月 | 次の日>

2006-05-18

■ ruby-dev summary

あっ! やっべ、ruby-dev summary 忘れてた。 隔週になっていらい、さらに忘れやすくなったな。

(05:26)

■ Ruby スクリプトを実行ファイル化する on Linux (1)

Linux で、Ruby スクリプトを単一実行ファイル化したい。 それもできるだけ汎用的な方法がいい。

[方針]

  • libc その他はすべてスタティックリンクする。
  • 拡張ライブラリもスタティックリンクする。
  • Ruby スクリプトは全部 objcopy でオブジェクトファイルにしてバイナリにリンクする。
  • require を Ruby レベルで差し替えてそっちを読ませる。

■ Ruby スクリプトを実行ファイル化する on Linux (2) ダイナミックロードの問題

ruby をスタティックリンクで作ると、 PAM でダイナミックロードされてるライブラリが使えない。ううーん。

……ん? 実は WEBrick では etc.so はほとんど使ってないのか。 UserDir オプションさえ使わなければ、 WEBrick に関してはとりあえず問題なさそうだな。 問題棚上げ。

他にダイナミックロードが必要と言えばネームサービスか。 サーバ側なら IP アドレスだけ使ってれば getaddrinfo は動かない……かな? なんかいけそうな気がしてきた。 これも問題棚上げ。

■ Ruby スクリプトを実行ファイル化する on Linux (3) どうやってプログラムを実行させるか

Ruby スクリプトは予定通り objcopy でオブジェクトファイルにして、 これを ruby とリンクさせる……まではよいのだが、 ruby にどうやってこのプログラムを実行させるかが問題だ。

ruby の main.c を差し替えてリコンパイルする方法だと、 ruby の objdir をずっと残しておかないといけない。 できれば ruby とか libruby-static.a だけを使ってどうにかしたい。

普通に ruby 組み込みインターフェイスを使ってしまうと、 拡張ライブラリがリンクされない (libruby-static.a しかリンクされないから)。 拡張ライブラリをスタティックリンクした libruby があるといいんだけどな。

libruby-static.a と拡張ライブラリの *.a を 全部まとめた、専用の libruby.a を自分で作ることにした。 これと独自の main.o をリンクしてバイナリを作る。 独自の main.c は ruby の main.c を基本とするが、 argv をすりかえてメインプログラムを -e でつっこむことにする。 なんかイマイチな方法だけど、まあとりあえず動きゃいい。

■ Ruby スクリプトを実行ファイル化する on Linux (4) require のすりかえ

さらに、main.c で次のように require をすりかえる。

extern struct script_source rb2exe_file_list[];
 
static VALUE
lookup_script(VALUE name)
{
    struct script_source *ent;
 
    for (ent = rb2exe_file_list; ent->name; ent++) {
        return rb_str_new(ent->start, ent->size);
    }
    return Qnil;
}
 
static void
replace_require(void)
{
    rb_define_global_function("_rb2exe_lookup_script", lookup_script, 1);
    rb_eval_string("\
        module Kernel                             \n\
          alias _rb2exe_original_require require  \n\
          remove_method :require                  \n\
          def require(feat)                       \n\
            rb = feat.sub(/\\.rb\\z/, '') + '.rb' \n\
            if src = _rb2exe_lookup_script(rb)    \n\
              eval src, ::TOPLEVEL_BINDING, rb    \n\
              $\".push rb                         \n\
              return true                         \n\
            end                                   \n\
            _rb2exe_original_require feat         \n\
          end                                     \n\
        end                                       \n\
    ");
}

rb2exe_file_list はリンク時にソースコードを自動生成してコンパイルする。

■ Ruby スクリプトを実行ファイル化する on Linux (5) 必要な *.rb の検出

残るは、require される *.rb を検出することだ。

……ちょっと考えたんだけど、必要なファイルだけじゃなくて、 ロードパスに入ってる *.rb は全部入れちゃうというのも一案かもしれない。 当然ながらバイナリはでかくなるけど、どーせオンデマンドロードなんだし、 いまどきバイナリが 10MB デカくなったところでたいした問題ではないのではなかろうか。

まあ、いちおう無駄を最小にする方向で考えてみよう。

exerb だと、実際に実行してみて require されるファイルを検出したりする。 しかし、これはいろいろな理由からできれば避けたい感じだ。 あと、レシピファイルを作るのはなんか面倒なので、 少なくともデフォルトにはしたくない。

なお、俺が前に作った簡単スクリプトでは、 単純にソースコードを require で grep して適当に検出していた。 動的な require さえなければこれでも結構うまくいく。

今回は、適当に grep する方針をデフォルトにしつつ、 残りの方法はオプションで選べるようにしようと思う。 つまり以下の四つを用意する。

  • ソースコードを再帰 grep して検出 (デフォルト)
  • 実際に実行して検出
  • 必要なファイルを明示的にリストする
  • ロードパスにある *.rb を全部つっこむ

(06:16)

■ ruby-dev summary (2)

ruby-dev summary を書くとき、具体的に何をやっているのか、 この日記に書き出してみようと思う。

まず、要約する範囲を決める。現在は隔週でやってるので、 前回の人が担当した部分のあとから二週間分くらいが目安となる。 前回は立石さんの [ruby-talk:190859] ruby-dev summary 28495-28605 なので、[ruby-dev:28606] から二週間くらい。 スレッドの切れめを考えると、[ruby-dev:28636] あたりまでかなあ。 ……って、30 通しかないのかよ。普通はもうちょっとあるんだけど。

対象範囲が決まったら、 新しいメールを作って Subject に範囲を書いてしまう。

次に、メールを眺めつつ、要約する必要のあるスレッドを抽出する。 「要約する必要のあるスレッド」とは、

  • Ruby の仕様や実装に関係する
  • 単に「バグ報告 → 修正」ではない
  • 議論がされている

という感じのスレッドを指す。今回の範囲ならば、次のようなあたり。 リストアップすると同時に、日本語の Subject はてきとうに英訳してしまう。

ちょっと減らしすぎた? というか、元となるメールが少なすぎるんだよ今回は。

ここまで来たら、あとは地道に一個ずつ要約して訳していく。

(次回に続く)

(07:57)

■ Mac Book 見てきた

店員が Mac Book を示してゐる。

大層大事さうに陳列棚に載せてゐる。
眠い目を擦り、いつたい何が入つてゐるのか見極めようとするが、如何にも眠かった。
Xeon か Opteron でも入つてゐるのか。
何とも手頃な善い匣である。
店員は時折笑つたりもする。

「ぽん」
匣の中から聲がした。
鈴でも轉がすようなシンセサイザーの聲だった。

……

……えー、まあ、京極ネタは置いといて。 実物見ると意外にのっぺりしてるのな。

(22:15)

■ もうだめ

ぎがねむす。今日はもう寝よう。

ああ、しかし、日記に適当なネタだけ書いて寝たりしてると、 ようやく本調子に戻ってきたかなと実感するね、印象があるね、そんな情勢だね (三段活用)。

(22:24)

本日のツッコミ(全4件)
■ なかだ (2006-05-18 12:27)

const char *ptr = StringValuePtr(name);
    long len = RSTRING(name)->len;
    if (strlen(ent->name) == len && strncmp(ptr, ent->name, len) == 0)

あたりが抜けてると予想。

■ MoonWolf (2006-05-18 16:37)

RubyScript2Exeとかはどうでしょうか?
Windows,Linux,MacOSXに対応しているようです。
http://www.erikveen.dds.nl/rubyscript2exe/index.html

■ 青木 (2006-05-18 22:22)

やっぱり眠いときにコードを書いちゃいけませんねえ。

■ 青木 (2006-05-18 22:24)

あ、何かあったなーと思ってたけど、RubyScript2Exe でしたか。
rb2exe はぐぐったんだけどなー。*.so までまとめてくれるんだなあ。
libc までは、なんとなく入れてくれないっぽい?
まあ、PAM とネームサービスの問題は解決方法が思いつかないしなあ。


<前の日 | この月 | 次の日>
2002|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|04|05|06|09|10|
2009|07|
2010|09|