OS2/emx/ruby/porting/1.8.4

/OS2/emx/ruby/porting/1.8.4

てきとうにメモ。 (現状では 1.8.4 あたりの話)

Index

作業状況

バイナリはこっちに捨てる予定。 未定。

ruby-1.8.4

パッチ : ruby-1.8.4-os2emx-diff-20060122.zip

  • defines.h : OS/2 のヘッダを include
  • eval.c :
    • select → rb_os2_select (missing/os2.c)
    • USE_OS2_THREAD が定義されているときは、スレッドのスケジューリングに OS/2 ネイティブスレッドで実装したタイマを用いる(ruby.h, rubysig.h, signal.c も参照)
      この場合、標準の EMX だと SIGVTALRM シグナルが使えないので、SIGUSR2 でためしに代用してみた(signal.c も参照)。 innotek libc だと定義されてるくさいけどどうなんでしょ。
  • io.c :
    • popen 時は自前の双方向 popen() を使う。(missing/os2.c も参照) libc の popen() は使わない。
    • NO_LONG_FNAME 有効(util.c, util.h も参照)。どうだろ。
  • signal.c : USE_OS2_THREAD で SIGVTALRM が使えない場合は SIGUSR2 で代用(eval.c も参照)
  • util.c, util.h : NO_LONG_FNAME 関連(io.c も参照)
  • missing/os2.c :
    • *popen(), do_os2_system() : 利用シェルが cmd.exe 互換のときは argv[0] のディレクトリセパレータを \ にする("..." で括ってある場合は空白文字も考慮する)
    • emx_fduplex_popen/pclose() : 双方向パイプサポート
    • DosSleep(0) がちょろちょろ入ってるが、どの程度に利いているのかは入れた本人にも確信がない。make test-all でのデッドロック (openssl のテスト時) が多少改善されるはず。
  • ext/curses/curses.c : 一部シンボルの二重定義回避(defines.h で OS/2 のヘッダを取り込んだことによる)
  • ext/digest/sha2/sha2.c : 一部の内部関数名を変更。openssl ライブラリをスタティックリンクする場合のリンクエラーを回避するため。
  • ext/socket/socket.c : とりあえず UNIXSocket を使用不可にしてある。

そのほかは 1.8.4-preview2 のパッチから継承。

ruby-1.8.4-preview2

configure 部分のパッチ : rb184p2-c-20051220.zip

  • LIBEXT が ruby 本体のライブラリにも適用されるようにしてみた(今のところ意味なし)
  • emx 標準の gcc-2.8.1 が引用符でくくられたライブラリパス名(-L"hoge")を正しく扱えないようなので、os2-emx の場合だけ " をとっ払うことにした

ruby 本体と拡張ライブラリへのパッチ : ruby-1.8.4-preview2-os2-emx-20051220.diff

  • dir.c : s/TRUE/!0/
  • io.c : libc の popen を使うようにする(missing/os2.c 中の do_os2_popen も参照)
  • instruby.rb : os2 の場合はバッチファイルの拡張子を .cmd に(.bat だと別セッションで DOS ウィンドウが起動してしまう)
  • main.c : os2 関係の微妙な初期化をとりあえずここに入れてみた(missing/os2.c 中の _os2ruby_init 参照)
  • missing/os2.c :
    • 環境変数 RUBYSHELL が設定されている場合、system() だけでなく popen() でもそれがデフォルトシェルとして用いられる。SHELL で指定されたシェルは EMXSHELL があらかじめ設定されていない場合のみ用いられる。
    • Chdir と Pwd でドライブ文字考慮 [ruby-dev:21945]
    • popen 時にデフォルトシェルを調べ、cmd.exe なら先頭引数の / を \ に置換する。 つまり popen("./foo ./bar.baz") は popen(".\\foo ./bar.baz") になる。(置換処理が大ざっぱなので、パス名が空白文字を含む場合、現時点では誤動作する)
  • ext/curses/curses.c: typedef int chtype;
  • ext/openssl/ossl.h : OSSL_NO_CONF_API
  • ext/socket/extconf.rb : s/os2_emx/os2-emx/
  • test/fileutils/test_fileutils.rb : [ruby-dev:22238]
  • test/fileutils/test_nowrite.rb : 同上

旧版

configure 部分のパッチ : rb184p2-c-20051207.zip

ruby 本体と拡張ライブラリへのパッチ : ruby-1.8.4-preview2-os2-emx-20051207.diff

  • 最低限コンパイルが通るレベル

チラシの裏 - 基本部分に関するアレ

OS/2 環境自体はその出自からして、ファイルとかプロセスまわりとかは(Unix というより) DOS/Windows に近い。 Win9x 以上 NT 以下って感じ?

プロセス : emxlibc - fork (io.c, process.c)

emx の libc をスタティックリンクしてないときは fork がまともに動かないかも。 というか -Zcrtdll で dll 版のライブラリを使ってるときは make test が失敗する。
io.c 中にある pipe_open 関数を適当にいじって popen を使うようにし、unix 系のシェルを指定すると動くようになる。 (逆に言うなら、スタティックリンクしているときは fork が使えるってことでいいんでしょうか。process.c 中の rb_f_fork を有効にしてもいいの?)

そういえば OS/2 版の perl にはふつうのバイナリと "forkable" ってやつの2種類があったような…

(2006-01-04) ためしに libiconv-1.10-ja1 と openssl-0.9.8a をスタティックリンクしたバイナリを作ったら fork に失敗した。 スタックサイズを削ったら通った。 えー、使用メモリサイズにけっこう敏感?

プロセス : do_spawn (missing/os2.c)

process.c 中の rb_f_system から呼び出し。

コマンドラインがシェルのメタ文字を含む場合は libc の system() 相当、含まない場合はコマンドラインを分解して spawnvp() に渡す。 (unix 系と cmd.exe 系で認識するメタ文字が違うので、メタ文字判定の前に利用シェルを調べている)

emx libc 版 system() との違いは、環境変数 RUBYSHELL および SHELL の内容を参照すること(つまり EMXSHELL は参照されない)。

emx 版の do_spawn を利用しているのは rb_f_system のみ。 つまり(EMX 版の ruby において)環境変数 RUBYSHELL/SHELL の設定を利用するのは組み込み関数の system のみということになる。
(Win32 の場合、pipe_open でも RUBYSHELL の内容が参照される。win32/win32.c 中の do_spawn, do_aspawn, pipe_exec 関数 → CreateChild 関数参照)

コマンドラインの quoting ("..." の括り) って対応した方がいいのかなあ。

拡張ライブラリの make 時、extmk.rb が system() 経由で make を呼び出す。 1.8.4 では引数に $(topdir) とかの文字列が渡されるので、RUBYSHELL, SHELL, COMSPEC に unix 系のシェルを指定すると make にそのまま渡してくれず、結果としてライブラリ中のファイルがとんでもないところにコピーされたりする。 (ruby の system() 関数のバックエンドとして使われる)do_spawn を作り直す必要に迫られるのかもorz

プロセス : emxlibc - system, popen (process.c)

これらの関数は、環境変数 EMXSHELL もしくは COMSPEC で指定されたシェルを暗黙で呼び出す。
COMSPEC には通常 cmd.exe(OS/2 のデフォルトシェル)のフルパスが設定されている。 unix 系のシェル(sh とか)の利用を想定したプログラムは誤動作する可能性がある。 というか ruby のテストスクリプトが動作しない。(ext/iconv あたりの make もやばかったんじゃなかったっけかなあ…いや、これは↓の問題だったかも)

libc の popen は読み込み/書き込みどちらか一方向のパイプしか指定できない。 IO.popen の双方向パイプ("w+","r+")を使うとはまる。 test/openssl/ssl_server.rb のあたりとか。

参考:Win32 の場合

プロセス : デフォルトシェル (process.c)

rb_proc_exec - sh があったら sh を execl、なかったら system。
proc_spawn - sh があったら sh を spawnl、なかったら system。

sh はソース中でリテラルに決め打ち。 Moztools だとデフォルトシェルが ash なので、何らかの方法で選べるとうれしいな、的な。 (とはいっても Moztools がまともにインストールされている環境なら sh は必ず存在するはず。perl 5.8 の bin 中に pdksh が入っているからだ。しかし ruby の構築/テストに perl が必要とかいう事態になりかねないので微妙に納得できない感じ)

OS/2 のデフォルトシェルは cmd.exe である。 "/" のディレクトリセパレータや unix 系シェルを想定した構文を cmd.exe は受け付けない。

あらかじめ COMSPEC や EMXSHELL に unix 系シェルの指定を強要することはできれば避けたい。これらの環境変数に cmd.exe 非互換のシェルを設定すると gcc が(というか -Zomf 指定時に link386 への内部ラッパーが)正しく動作しなくなる。

その他:lib/drb/extservm.rb # invoke_service_command(name, command)

      if RUBY_PLATFORM =~ /mswin32/
	system("cmd /c start /b #{command} #{uri} #{name}")
      else
	system("#{command} #{uri} #{name} &")

OS/2 の場合、cmd.exe がデフォルトシェルだとたぶんはまる(というか、test-all 時にはまった)

ファイル : MBCS(つーか日本語の)ファイル名

マルチバイトパス名の列挙/分解/照合。
ちゃんと調べるのが欝陶しいところ(べつに OS/2 に限らず、Win32 でも)。

MBCS 文字列に関して、emx ライブラリの支援はほとんど期待できない。 emx はシングルバイト文字圏の locale しか知らない

MB_CUR_MAX に2以上の値を叩き込んでしまえば、カレントコードページの DBCS ベクタに対応した値を mblen で取得することは可能。この点以外は djgpp と同程度。

ファイル : ドライブ名

いわゆる _chdir2 と _getcwd2([ruby-dev:21945]
それから [ruby-dev:22238]

たしかに Win32 API だとカレントドライブという概念は実体として存在しない(とアドバンスト Windows にも書いてあった)ですが…DOS の CDS がしぶとく生き残っちゃっている 9x 系の Windows だとカレントドライブ以外のカレントディレクトリは消せない可能性もあるような(あとで調べよう)。

チラシの裏 - ext/* に関するアレ

ext/socket

ext/digest/*

  • sha2/sha2.c - openssl-0.9.8 をスタティックリンクしようとしたら SHA256_Transform, SHA512_Transform が競合するらしくリンク失敗。 関数名を変えて対処(単に static 宣言すればいいだけかもしれない。というか emx 標準の ld がショボいのか?)