Aug 8, 2008

Ruby/SDL をビルド


素の Ruby/SDL を Visual C++ 2008 Express Edition でビルドするまでのメモです。 TTF や draw_line などを使用しているサンプルは動きませんのでご注意を。

用意するもの

  • Visual C++ 2008 (Express Edition SP1)
  • OpenGL
    • include : c:/Program Files/Microsoft SDKs/Windows/v6.0A/Include
    • lib : c:/Program Files/Microsoft SDKs/Windows/v6.0A/Lib
  • DirectX SDK
SDL のビルドに必要です。March 2009 を利用しました。
D:\tmp>ruby -v
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mswin32_90]
d:/ruby19 にインストール & d:/ruby19/bin にパス設定済みとします。

ビルドとインストール

SDL
  • VisualC.zip を展開、SDL.sln を開いて Release ビルド。
  • dsound.h が無いとビルドエラー。⇒ 要 DirectX SDK
できあがった VisualC/SDL/Release/SDL.dll は d:/ruby19/bin にコピーしておきます。
Ruby/SDL
Makefile を書くのも面倒なので、sdl.so を作成するための新規プロジェクトを作成して対応します。
rubysdl-2.1.0 を展開したフォルダに 新規プロジェクトを作成します。ここでは `VCBuild' という名前にします。

DLL を作りたいのでそう選択します。空のプロジェクトに設定して、ソースコードの追加を後で行います。


既存の要素として Ruby/SDL のソースコードをプロジェクトに追加します。


プロジェクトのプロパティを設定する前に下準備をします。VCBuild フォルダの中に `sdl.so.def' という名前のテキストファイルを新規作成します。


内容はこちらの2行だけです:


ここから VCBuild プロジェクトのプロパティ設定を行います。ソリューション構成は Release にした 上で、まずは "C/C++ > 全般 > 追加のインクルード ディレクトリ" に以下のように記入します(各自の環境で適宜読み換えてください):


記入するのは SDL と Ruby のインクルードファイルが配置してある場所です。次に "C/C++ > プリプロセッサ > プリプロセッサの定義" に追記します:


ENABLE_OPENGL と追記しました。次は "リンカ > 全般 > 追加のライブラリ ディレクトリ" にパスを記入します:


SDL.lib, SDLmain.lib そして Ruby のエクスポートライブラリの場所を指定します。 次は "リンカ > 全般 > 出力ファイル" で名前を 'sdl.so' と明記します。


次は "リンカ > 入力 > 追加の依存ファイル" :


最後は "リンカ > コマンドライン > 追加のオプション" に sdl.so.def を使用するよう記入しておきます。


ビルドすると 'VCBuild/Release' に sdl.so が作成されます。


… Makefile 書くのとどっちが面倒だったろう?(^^;
それはそれとして、この DLL を含め、 Ruby/SDL のライブラリファイルを既定の場所へコピーしてインストール完了です。
  • VCBuild/Release/sdl.so
    • -> d:/ruby19/lib/ruby/site_ruby/1.9.1/i386-msvcr90 にコピー
  • lib/sdl.rb, lib/rubysdl_aliases.rb, lib/rubysdl_compatible.ver1.rb
    • -> d:/ruby19/lib/ruby/site_ruby/1.9.1/ にコピー
コマンドラインから実行:
D:\tmp\RubySDL\rubysdl-2.1.0\sample>ruby randrect.rb



今回ビルドしたのは素の SDL のみなので、フォントや高レベルの描画機能(draw_line, etc.)は 動作しませんのでご注意を。

Aug 3, 2008

Ruby 1.9.0-3 : インタプリタ DLL を C から呼ぶ例

(2010-08-29 追記 : 以下の記事は古くなっています。こちらが参考になるかもしれません。 http://sites.google.com/site/ltsevenscore/ruby/tips/interpreter_dll )


  • ruby_options が値を返すようになっている
  • ruby_run という関数がない
など、Rubyの初期化・実行に関する README.EXT の内容は現状に追いついていないようです。
そのようなわけで、Ruby 1.9 を外部から利用する方法を少し調べてみました。
  • アプリケーションに Ruby を組み込みたい
  • rubyw.exe に代わる前処理プログラムを作りたい
といったときに役立つかもしれません。
利用したのは Ruby 1.9.0-3です。 これを VC++ 2008 でビルドした msvcr90-ruby190.dll を C プログラムから利用してみます。
こちらがアプリケーションのソースコード。必要最小限のエントリーポイント を取得した後、インタプリタの初期化と実行に移っています。
UseRubyDLL.c:
#define WIN32_LEAN_AND_MEAN
#include 

#define APP_NAME                "UseRubyDLL.exe"
#define RUBY_DLL_NAME           "d:\\ruby19\\bin\\msvcr90-ruby190.dll"
#define RUBY_SCRIPT_NAME        "script.rb"

HINSTANCE ruby_dll;

/* from include/ruby/ruby.h */
void  (*ruby_init)( void );
void  (*ruby_sysinit)( int*, char*** );
void* (*ruby_options)( int, char** );
/* from include/ruby/intern.h */
void  (*ruby_init_loadpath)( void );
int   (*ruby_run_node)( void* );

void
WinMainCRTStartup()
{
    /* Dummy arguments for ruby_sysinit(). */
    int         sysinit_argc = 0;
    char**      sysinit_argv = NULL;

    /* Manually construct interpreter arguments. */
    int         app_argc = 3;
    char*       app_args[3];
    char**      app_argv = app_args;
    app_args[0] = APP_NAME;
    app_args[1] = RUBY_SCRIPT_NAME;
    app_args[2] = "See ya.";

    ruby_dll = LoadLibrary( RUBY_DLL_NAME );
    if ( ruby_dll )
    {
        ruby_sysinit       = (void (*)( int*, char*** ))GetProcAddress( ruby_dll, "ruby_sysinit" );
        ruby_init          = (void (*)( void ))GetProcAddress( ruby_dll, "ruby_init" );
        ruby_init_loadpath = (void (*)( void ))GetProcAddress( ruby_dll, "ruby_init_loadpath" );
        ruby_options       = (void* (*)( int, char** ))GetProcAddress( ruby_dll, "ruby_options" );
        ruby_run_node      = (int (*)( void* ))GetProcAddress( ruby_dll, "ruby_run_node" );

        ruby_sysinit( &sysinit_argc, &sysinit_argv );
        ruby_init();
        ruby_init_loadpath();

        ruby_run_node( ruby_options( app_argc, app_argv ) );

        /* NOTE: After the execution, +ruby_run_node+ automatically
           calls +ruby_cleanup+. So there's no need to explicitly call
           Ruby's finalizer here(otherwise cause disaster).
         */

        FreeLibrary( ruby_dll );
    }
}
上記の ruby_*() 系関数の利用方法は Ruby のソースコード(winmain.c, eval.c, etc.)から把握したものです。 当然ですがインタプリタ DLL の場所など、ハードコードした部分は適宜変更する必要があります。
アプリケーションから呼び出すスクリプトはこちら。インタプリタ DLL の場所 次第では、標準ライブラリ(ここでは matrix.rb)jの require に失敗しますの でご注意を。
script.rb:
require 'matrix'
File.open( 'result.txt', 'w' ) do |f|
  f.puts Time.now
  f.puts Vector[rand,100*rand,10000*rand]
  f << ARGV
end
ビルドによって生成された UseRubyDLL.exe を以下のように実行します。 script.rb は同じ場所にあるものとします。
$ ./UseRubyDLL
実行すると同じ場所に result.txt ができているはず。以下が結果の一例です。
result.txt(例)
2008-08-03 18:52:16 +0900
Vector[0.434646515113465, 27.44515575993, 9534.51425972162]
["See ya."]
ちなみにビルド用の Makefile はこちら。実行ファイルを小さくするため、あえて Cygwin gcc (gcc -mno-cygwin -mwindows) を利用してみました。
Makefile:
TARGET = UseRubyDLL.exe
SRC = $(TARGET:exe=c)
OBJ = $(TARGET:exe=o)

CC = gcc -mno-cygwin -mwindows
CFLAGS = -Wall -Os
LDFLAGS = -lkernel32 -nostdlib

all: $(TARGET)

$(TARGET): $(OBJ)
 $(CC) -o $(TARGET) $< $(LDFLAGS)
 strip $(TARGET)

clean:
 rm $(OBJ) $(TARGET)

.c.o:
 $(CC) $(CFLAGS) -c $<
手元での結果はこちら:
$ gcc --version
gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


kazuwe@kazuwe-PC ~/UseRubyDLL
$ ls -l UseRubyDLL.exe
-rwxr-xr-x 1 kazuwe None 2560 Aug  3 18:58 UseRubyDLL.exe
ちょっと心配になるくらい小さくなるものですね :-)

参考

  • embedding Ruby 1.9.0 inside pthread
  • BDS2006(C++Builder)からRubyを使う (山本隆の開発日誌)