From: Takashi Nishimoto Date: 1999-08-15T09:00:22+09:00 Subject: [ruby-list:16151] extinit & mkmain 拡張ライブラリ作成自動化ツールを作ってみました。 拡張ライブラリの Init_xxx 関数で書く内容や、ヘッダファイル(の一部)はど の拡張ライブラリでも共通しているようです。 それに目をつけました。 まだ未完成ですが、ここに公開します。 要望、パッチなどは歓迎します。 * Usage/extinit --------------- prev: next: 書式 extinit 説明 拡張ライブラリ作成をちょっとだけ自動化する。 extconf.rb と mkmain スクリプトとヘッダファイルを生成する。すでに存在 するときは上書きされないので、生成後は自由に変更してよい。 ライブラリ名はカレントディレクトリの basename になる。 * Usage/mkmain -------------- prev: next: 書式 mkmain 説明 extinit スクリプトによって生成される。このスクリプトの定義より、 main.c (Init_xxx が定義されているファイル) と funcs.c (Init_xxx の定義 から生成される関数定義の雛型) と variables.h (Init_xxx から生成される 大域変数定義) を生成する。すでに存在するときは上書きされる。だからこれ らのファイルを手で修正することは勧められない。 変数 $id:シンボルの ID (rb_intern 関数によって求められる) を格納する変数の リスト。命名規則(以下参照)に従う必要がある。 $class:クラス定義のための hash の hash $module:モジュール定義のための hash の hash。 $class と同じ形式をとる。 ID の命名規則 ID を格納する変数名には簡単な命名規則がある。(スクリプトで自動生成する んだから、当然だ) -->変数名は 「接頭辞」+「シンボル名」 の形をとる -->接頭辞は英字大文字 -->I から始まるものはインスタンス変数の ID -->M から始まるものはメソッドの ID -->その他は自由に使って良い $class と $module の形式 $class と $module は hash の hash である。 -->最初のキーはクラス(モジュール)名 -->2番目のキー -->prefix -->C関数名の接頭辞。 -->定義されていないときは 「クラス(モジュール)名」.downcase となる。 -->singleton_method -->特異メソッド定義の配列。(クラスのときはクラスメソッド) -->定義法 -->0番目の要素は C 関数名 -->実際の C 変数名は接頭辞が付加される -->1番目の要素は引数の個数 -->-1 としたとき、C の配列で渡る -->-2 としたとき、Ruby の配列で渡る -->2番目の要素 (省略可能) は Ruby のメソッド名 -->省略したときは C 関数名と同じになる -->method -->メソッド定義の配列 -->定義法は上に同じ -->module_function -->モジュール関数定義の配列 -->定義法は上に同じ -->include -->インクルードするモジュール(の配列) -->文字列か文字列の配列で指定する extinit スクリプト -------------------------------------------------- #!/usr/bin/ruby Indent = ' ' Transtbl = { '+' => 'plus', '-' => 'minus', '*' => 'mul', '/' => 'div', '+@' => 'uplus', '-@' => 'uminus', '[]' => 'aref', '[]=' => 'aset' } VariablesFile = "variables.h" MainFile = "main.c" FunctionsFile = "funcs.c" ExtconfFile = "extconf.rb" TodoFile = "mkmain" def write_id $main.puts ("#{Indent}/* ID */") $id.each{|id| name = if id[0,1] == 'I' then '@' else '' end + id[1 .. -1] $main.printf '%s%s = rb_intern("%s");' + "\n", Indent, id, name } end def method2cfunc(what, info, method) info['prefix'] + if what == 'singleton_method' then '_s_' else '_' end + Transtbl.fetch(method, method) end def write_methods(cname, info) if (i=info['include']) i.to_a.each{|incs| $main.puts(Indent + 'rb_include_module(' + info['object'] + ', rb_m' + incs + ');') } end $main.puts "" info['prefix'] = info['prefix'] || cname.downcase ['singleton_method', 'method', 'module_function'].each {|what| if info[what] then $main.puts "#{Indent}/* #{what} */" info[what].each{ |method, args, *opt| cfunc = method2cfunc(what, info, method) method = opt[0] || method $main.printf '%srb_define_%s(%s, "%s", %s, %d);' + "\n", Indent, what, info['object'], method, cfunc, args sel = if what == 'singleton_method' then 'klass' else 'self' end case args when 0 $funcs.printf "VALUE %s(VALUE %s)\n", cfunc, sel when -1 $funcs.printf "VALUE %s(int argc, VALUE *argv, VALUE %s)\n", cfunc, sel when -2 $funcs.printf "VALUE %s(VALUE %s, VALUE ary)\n", cfunc, sel else $funcs.printf "VALUE %s(VALUE %s", cfunc, sel 0.upto(args - 1){ |i| $funcs.printf ", VALUE %s", ("a"[0]+i).chr } $funcs.printf ")\n" end $funcs.printf "{\n\n}\n\n" } $main.puts "" end } end def create_main libname = Dir.pwd.gsub(/^.*\/([^/]+)$/, '\1') $class = $class || {} $module = $module || {} $main = File.open(MainFile, "w") $funcs = File.open(FunctionsFile, "w") $funcs.puts "#include \"#{$basename}.h\"" $main.print <<"SRC" #define MAIN #include "#{libname}.h" void Init_#{libname}() { SRC $class.each_pair {|cname, info| $main.printf "%s/* class %s */\n", Indent, cname info['super'] = info['super'] || 'Object' $main.printf '%srb_c%s = rb_define_class("%s", rb_c%s);' + "\n", Indent, cname, cname, info['super'] info['object'] = 'rb_c' + cname write_methods(cname, info) } $module.each_pair {|mname, info| $main.printf "%s/* %s */\n", Indent, mname $main.printf '%srb_m%s = rb_define_module("%s");' + "\n", Indent, mname, mname info['object'] = 'rb_m' + mname $main.puts "" write_methods(mname, info) } write_id $main.puts "}" puts "Create " + MainFile $main.close puts "Create " + FunctionsFile $funcs.close create_variableh end def create_variableh ids = $id.join(', ') objects = ($class.keys.collect{|k| $class[k]['object']} + $module.keys.collect{|k| $module[k]['object']}).join(", ") $Variables = File.open(VariablesFile, "w") if ids != "" then $Variables.print <<"SRC" #ifndef MAIN extern #endif ID #{ids}; SRC end if objects != "" then $Variables.print <<"SRC" #ifndef MAIN extern #endif VALUE #{objects}; SRC end puts "Create " + VariablesFile $Variables.close end def create_file(file, contents, permission=0755) unless File.exist?(file) then fp = File.open(file, "w") fp.print contents fp.chmod permission fp.close puts "Create " + file end end #-------------------------------------------------------------------------------- $id = [] $class = {} $module = {} $basename = File.basename(Dir.pwd) $klass = $basename.dup $klass[0] = $klass[0,1].upcase create_file(ExtconfFile, <<"SRC") #!/usr/bin/ruby require "mkmf" create_makefile("#{$basename}") SRC create_file(TodoFile, <<"SRC") #!/usr/bin/ruby load "#{File.basename($0)}" $id = %w( ) $class = { '#{$klass}' => { 'prefix' => nil, 'singleton_method' => [ ['new', 1, "[]"] ], 'method' => [ ], 'module_function' => nil, 'include' => nil }, } $module = { } create_main SRC create_file($basename + ".h", <<"SRC", 0644) #include "ruby.h" #include "#{VariablesFile}" /* for debug */ #define DEBUG puts("DEBUG") #include "proto.h" SRC #-------------------------------------------------------------------------------- -- Takashi Nishimoto: g96p0935@mse.waseda.ac.jp I love Emacs, zsh, and Linux!! See ftp://ftp.misao.gr.jp/pub/tak/README Key fingerprint = EE D7 1D 18 A9 42 C0 8A 63 E7 F2 AC 4D C7 83 6B