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を使う (山本隆の開発日誌)

Jul 20, 2008

RMath

今年1月に書いておきながら、 そのまま忘れて半年放置していた 3D CG 向けの数学関数の拡張ライブラリを公開しておこうと思います。
RMath といいます。
RMath は行列(3x3,4x4)、ベクトル(3要素,4要素)、クォータニオンの各クラスを含んだ拡張ライブラリです。 デバッグ用に同じインタフェースを持った Ruby 実装版も同梱しています(RMath.rb)。
ライセンスは zlib/libpng としました。
リファレンスやサンプルが少ないので、それらの充実が当面の課題ですね。
(2009-12-26 URLを新しいものに修正しました)

Jul 6, 2008

takeown, icacls コマンド

Windows Vista での chown / chmod コマンドに相当するものです。
訳あって C ドライブの Vista (Home Premium) をインストールしなおしたところ、 以前から D ドライブ側に置いていたファイルへのアクセスに支障が出てしまいました。
具体的には:
  • 消せないファイルが多発。
  • Cygwin 系コマンドでの操作が軒並み ``Permission denied'' に。
OS からは別な(しかももう存在しない!)ユーザーの所有物とみなされているのが原因です。
この問題を次のようにして解決してみました。
1. コマンドプロンプトを「管理者として実行」する
``C:\Windows\System32\cmd.exe'' のアイコンを右クリックし、「管理者として実行(A)」で起動。

2. 作業するディレクトリに移動
ここでは ``D:\cygwin'' とします。
C:\Windows\system32>d:

D:\>cd cygwin

D:\cygwin>
3. ファイルの所有権を取得
takeown コマンドを実行します。 ``/f '' がファイル名の指定。``/r'' は再帰的に実行することを指定するものです。
D:\cygwin>takeown /f * /r

成功: ファイル (またはフォルダ): "D:\cygwin\bin" は現在ユーザー "lt140-PC\lt140" によって所有されています。

成功: ファイル (またはフォルダ): "D:\cygwin\cygdrive" は現在ユーザー "lt140-PC\lt140" によって所有されています。

成功: ファイル (またはフォルダ): "D:\cygwin\Cygwin.bat" は現在ユーザー "lt140-PC\lt140" によって所有されています。

成功: ファイル (またはフォルダ): "D:\cygwin\Cygwin.ico" は現在ユーザー "lt140-PC\lt140" によって所有されています。

成功: ファイル (またはフォルダ): "D:\cygwin\dev" は現在ユーザー "lt140-PC\lt140" によって所有されています。

...(以下略)
4. アクセス権を操作
icacls コマンドを実行します。 複雑なオプションを多数受け付ける高機能なコマンドですが、「ユーザーに指定したパス以下のフルコントロールを与える」のであれば ``icacls <パス名> /grant <ユーザー名>:F /t'' だけで終了です。 フルコントロール可能な状態にしたくない場所での実行には注意してください。その場合は ``:F'' とした部分を変更することになります。``icacls /?'' で詳細を確認してください。
D:\cygwin>icacls D:\cygwin /grant lt140:F /t
処理ファイル: D:\cygwin\bin
処理ファイル: D:\cygwin\cygdrive
処理ファイル: D:\cygwin\Cygwin.bat
処理ファイル: D:\cygwin\Cygwin.ico
処理ファイル: D:\cygwin\dev
処理ファイル: D:\cygwin\etc
処理ファイル: D:\cygwin\lib
処理ファイル: D:\cygwin\setup.exe
処理ファイル: D:\cygwin\tmp

...(以下略)
これで無事に読み書きできるようになりました。
…OS は忠実に自分の仕事をこなしているだけなのですが、自分がPCの管理者かつ唯一の利用者であるという状況では正直切ないですね。

Jun 8, 2008

マニフェスト関連


Visual C++ ファイルの再配布

Visual C++ ライブラリ DLL (MSVCR80.DLL など) に到達できた場合 (アプ
リケーションのローカル フォルダまたはシステム フォルダにインストール
されている場合など)、次のメッセージが表示されます。

R6034 An application has made an attempt to load the C runtime library incorrectly.

C/C++ 分離アプリケーションおよび side-by-side アセンブリのトラブルシューティング

すべての DLL がバイナリに埋め込まれたマニフェストを持つようにすることを
お勧めします。DLL が LoadLibrary 呼び出しで読み込まれる場合、外部マニフェストは
無視されます。

方法 : マニフェストを C/C++ アプリケーションに埋め込む

C/C++ アプリケーション (またはライブラリ) では、そのマニフェストを
最終的なバイナリに埋め込んでおくことをお勧めします。これにより、ほとんどの
シナリオで実行時の適切な動作が保証されます。

wxRuby2


wxWidgets の Ruby バインディング・ wxRuby というものを試そうとしてみました。 利用しているのは One-Click Ruby Installer 1.8.6-26。以下の作業は主に Cygwin Bash シェルで行っています。
インストールは RubyGems で楽勝でした。
$ gem.bat install wxruby
Bulk updating Gem source index for: http://gems.rubyforge.org
Building native extensions.  This could take a while...
Successfully installed wxruby-1.9.7-x86-mswin32-60
1 gem installed
wxRuby 1.9.7 の Windows 版バイナリがインストールされます。
それではさっそくサンプル起動!
$ ruby -rubygems d\:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/samples/minimal/minimal.rb
しかしその瞬間、よくありがちなダイアログボックスが登場しました。
---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Runtime Error!

Program: d:\ruby\bin\ruby.exe

R6034
An application has made an attempt to load the C runtime library incorrectly.
Please contact the application's support team for more information.

---------------------------
OK   
---------------------------
そしてターミナルにはエラーメッセージが:
d:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/lib/wxruby2.so: 1114: ダイナミック リンク ライブラリ (DLL) 初期化ルーチンの実行に失敗しました。
- d:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/lib/wxruby2.so (LoadError)
from d:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from d:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/lib/wx.rb:12
from d:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:32:in `gem_original_require'
from d:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:32:in `require'
from d:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/samples/minimal/minimal.rb:8
要するに wxruby2.so の LoadError だそうです。見に行きましょう。
$ cd d\:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/lib/
$ ls
wx  wx.rb  wxruby2.exp  wxruby2.lib  wxruby2.so  wxruby2.so.manifest
む、wxruby2.so.manifest が。まさか。
$ strings wxruby2.so | grep manifest

$
埋 め 込 ま れ て な い
LoadLibrary される DLL の場合は外部マニフェストが無視されるそうです。最終的なバイナリファイルにはマニフェストの埋め込みが必要となります。
そのようなわけで Visual Studio のコマンドプロンプトから以下の作業をしました:
Setting environment for using Microsoft Visual Studio 2008 x86 tools.

D:\Programs\Microsoft Visual Studio 9.0\VC>cd D:\ruby\lib\ruby\gems\1.8\gems\wxruby-1.9.7-x86-mswin32-60\lib

D:\ruby\lib\ruby\gems\1.8\gems\wxruby-1.9.7-x86-mswin32-60\lib>mt -manifest wxruby2.so.manifest -outputresource:wxruby2.so;2
確かめてみましょう。元の Cygwin ターミナルから:
$ strings wxruby2.so | grep manifest
無事に埋め込まれた模様。ではあらためて!
$ ruby -rubygems d\:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.7-x86-mswin32-60/samples/minimal/minimal.rb


うまくいきました。

May 18, 2008

メニューなどのテスト




Qtの各種機能を試してみました。前回のものではGLUTとの違いがわかりませんからね。
OpenGLによる描画とQtによる2Dオブジェクトの描画機能が併用できる、というのは強力だと思いました(画面右下のRubyロゴがそれ)。
中にはGLUTのソースコード(glut_teapot.c)から移植した 'teapot.rb' というものも入ってます。 GLUTから離れてティーポットが拝めなくなってしまったのが寂しかったものですから(^^;
アーカイブの展開後に
$ ruby -rubygems menutest.rb
…として起動してください。
動作に必要なものは以下の通り:

※menutest.rb: 利用したQtオブジェクトのリファレンス