Hatena::ブログ(Diary)

Kazzzの日記 このページをアンテナに追加 RSSフィード

2009-05-14

[][][][] All Green! (DBMError: dbm_store failed in ruby1.8.7_p160 その5)


link関数を呼んでいる以上、コピーではなくやはりリンクを使うべきだろうということで、CopyFileの代わりにWindows 2000以降実装されたCreateHardLinkで試してみることにした。

CreateHardLinkのマクロはCopyFile同様にVC++付属しているWinBase.h(%VSINSTALLDIR%\VC\PlatformSDK\Include\WinBase.h)に定義されている。

  • WinBase.h (Line:7698〜7725)
#if (_WIN32_WINNT >= 0x0500)
//
// API call to create hard links.
//
WINBASEAPI BOOL WINAPI CreateHardLinkA( __in LPCSTR lpFileName, __in LPCSTR lpExistingFileName, __reserved LPSECURITY_ATTRIBUTES lpSecurityAttributes ); WINBASEAPI BOOL WINAPI CreateHardLinkW( __in LPCWSTR lpFileName, __in LPCWSTR lpExistingFileName, __reserved LPSECURITY_ATTRIBUTES lpSecurityAttributes ); #ifdef UNICODE #define CreateHardLink CreateHardLinkW #else #define CreateHardLink CreateHardLinkA #endif // !UNICODE
#endif // (_WIN32_WINNT >= 0x0500)

これをそのまま使えば良い訳だが、注意しなければならないのがマクロ_WIN32_WINNTである。VC++ 2005に添付されているWinBase.hのデフォルトでは0x0400(Windows 95/NT)が定義されているため、そのままでは上記のマクロが展開されない。

従ってプリプロセッサ等で"_WIN32_WINNT"の値を適切に設定する必要がある。

_WIN32_WINNTの一覧(http://msdn.microsoft.com/ja-jp/library/aa383745.aspx)

OS_WIN32_WINNTの値
Windows Server 20080x0600
Windows Vista0x0600
Windows Server 2003 with SP1, Windows XP with SP20x0502
Windows Server 2003, Windows XP0x0501
Windows 20000x0500

今回はMakeFileに直接マクロの設定を挿入してしまうことにした。

  • MakeFile
LOCAL_DEFS = -DBUILDING_DLL -D_WIN32_WINNT=0x0600 

ではwin32.cを書き換えてビルドしてみよう。変更に関しては後でパッチを当てられるようにdiff形式で書いておく。

  • win32c.diff
@@ -14,7 +14,7 @@
 
 int link(const char *src, const char *dst)
 {
-    return !CopyFile(src, dst, TRUE);
+    return !CreateHardLink(dst, src, NULL);
 }
 

CopyFileと比べると引数の型が文字列へのポインタであることは変わらないが、順が逆になることに注意が必要だ。

ビルドはすんなり通った。

E:\ruby_build_image\gdbm-1.8.3-1\win32>nmake clean all
Microsoft(R) Program Maintenance Utility Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved. : : cl -nologo -c -DBUILDING_DLL -D_WIN32_WINNT=0x0600 -I.. -I..\.. -I.\win32\include -MD gdbm.c gdbm.c : : cl -nologo -LD gdbm.obj bucket.obj close.obj dbmclose.obj dbmdelete.obj dbmdirfno.obj dbmfetch.obj dbminit.obj dbmopen.obj dbmpagfno.obj dbmrdonly.obj dbmseq.obj dbmstore.obj delete.obj falloc.obj fetch.obj findkey.obj gdbmclose.obj gdbmdelete.obj gdbmerrno.obj gdbmexists.obj gdbmfdesc.obj gdbmfetch.obj gdbmopen.obj gdbmreorg.obj gdbmseq.obj gdbmsetopt.obj gdbmstore.obj gdbmsync.obj global.obj hash.obj seq.obj store.obj update.obj version.obj win32.obj -Fegdbm.dll ライブラリ gdbm.libオブジェクト gdbm.exp を作成中 gdbm.lib gdbm.dll cd ..
E:\ruby_build_image\gdbm-1.8.3-1\win32>nmake install :

なお、このMakeFileもマニフェストの埋込は行われないので、都度mt.exeを使うかMakeFileにターゲットを追加してSxS用のマニフェストをgdbm.dllに埋め込む必要がある。
>mt -manifest gdbm.dll.manifest -outputresource:gdbm.dll;2

あとはビルドで出来たgdbm.libとヘッダファイルを使って、以前書いたのと同様にdbm.soとgdbm.soを作り直せば作業は完了だ。(予めdbm/win32、gdbm/win32というディレクトリに生成物をコピーしておく)

  • extconf.rbを実行してMakeFileを生成する (dbm.so)
E:\ruby-1.8.7-p160\ext\dbm>ruby extconf.rb --with-winsock2 --with-dbm-lib=win32\lib --with-dbm-include=win32\include
checking for __db_ndbm_open() in db.lib... no
checking for __db_ndbm_open()... no
checking for __db_ndbm_open() in db2.lib... no
checking for __db_ndbm_open()... no
checking for dbm_open() in db1.lib... no
checking for dbm_open()... no
checking for dbm_open() in dbm.lib... no
checking for dbm_open()... no
checking for dbm_open() in gdbm.lib... yes
checking for DBM in gdbm-ndbm.h... no
checking for DBM in ndbm.h... yes
checking for cdefs.h... no
checking for sys/cdefs.h... no
creating Makefile

E:\ruby-1.8.7-p160\ext\dbm>nmake
Microsoft(R) Program Maintenance Utility Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved.
e:\ruby\bin\ruby -e "puts 'EXPORTS', 'Init_dbm'" > dbm-i386-mswin32_80.def Setting environment for using Microsoft Visual Studio 2005 x86 tools. cl -nologo -I. -I. -Ie:/ruby/lib/ruby/1.8/i386-mswin32_80 -I. -MD -O2b2xty- -DHAVE_TYPE_DBM -DDBM_HDR="<ndbm.h>" -Iwin32\include -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -c -Tcdbm.c Setting environment for using Microsoft Visual Studio 2005 x86 tools. dbm.c cl -nologo -LD -Fedbm.so dbm.obj msvcr80-ruby18.lib gdbm.lib oldnames.lib user32.lib advapi32.lib shell32.lib ws2_32.lib -link -incremental:no -debug -opt:ref -opt:icf -dll -libpath:"." -libpath:"e:/ruby/lib" -libpath:"win32\lib" -implib:dbm-i386-mswin32_80.lib -pdb:dbm-i386-mswin32_80.pdb -def:dbm-i386-mswin32_80.def ライブラリ dbm-i386-mswin32_80.lib とオブジェクト dbm-i386-mswin32_80.exp を作成中 mt -nologo -manifest dbm.so.manifest -outputresource:dbm.so;2
E:\ruby-1.8.7-p160\ext\dbm>nmake install
Microsoft(R) Program Maintenance Utility Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved.
install -c -p -m 0755 dbm.so e:\ruby\lib\ruby\site_ruby\1.8\i386-msvcr80

さて、いよいよだ。test_dbm.rbを実行してみよう。
f:id:Kazzz:20090512190625g:image

ヤッターーーーーーーーーーーーーーーーーーーーーーーーーーー

ちなみにtest_gdbm.rbも元々失敗していた二つのテスト(test_reorganizeとtest_s_open_create_new)以外は全てグリーンであることを確認できた。

やっと全てのテストが通った。これが何かの役に立つとは思えないけど(今更DBM、それもWindows上での需要があるとは思えない)、諦めずにいろいろとやってきて良かった。とはいえ、知っている方であれば一日もたたずに完了した作業を数日間かけている当たり、やはりこの辺は知らないことが多いことを露呈した恰好だ。

追記:
CopyFileの代わりにCreateHardLinkを使うことでテストが通った訳だが、問題が無い訳ではない。というのもCreateHardLinkを使う際には制限が存在しているからだ。

  • CreateHardLinkの制限


Windows2000以降は良いとして、NTFS縛りは最悪問題が出ることも考えられるので、同じような対応を行う場合は注意が必要だ。今は思いつかないが、回避策を含めたコードで書き換えたいところ。


  • テストコードは偉大だ

今回のDBMのように「どうなっていれば正しい」のかが私には分からないような場合、テストコードがあるというのは本当に助かる。テストが引っかかっていなければdbm.soを作った所で満足していたことだろう。

なお、test_dbm.rbだがテストに途中で失敗した場合にセットアップで作ったデータベースファイルが消えないため、次回のテストが失敗することが分かったので、自分環境では以下の修正を施している。

  • test_dbm.rb.diff
@@ -40,13 +40,16 @@
       assert_instance_of(DBM, @dbm_rdonly = DBM.new("tmptest_dbm_rdonly", nil))
     end
     def teardown
-      assert_nil(@dbm.close)
-      assert_nil(@dbm_rdonly.close)
-      ObjectSpace.each_object(DBM) do |obj|
-        obj.close unless obj.closed?
+      begin
+        assert_nil(@dbm.close)
+        assert_nil(@dbm_rdonly.close)
+        ObjectSpace.each_object(DBM) do |obj|
+          obj.close unless obj.closed?
+        end
+      ensure
+        File.delete *Dir.glob("tmptest_dbm*").to_a
+        p Dir.glob("tmptest_dbm*") if $DEBUG
       end
-      File.delete *Dir.glob("tmptest_dbm*").to_a
-      p Dir.glob("tmptest_dbm*") if $DEBUG
     end

2009-05-12

[][][][]Ruby拡張ライブラリィとDBM(GDBM)をデバッグする


Ruby-DBMのテストが通らない原因をいろいろと調べていたのだが、DBMインタフェースの実装として使用しているGDBM(GNU DBM)に問題がある可能性が非常に高くなったので、こいつのデバッグをしてみることにした。

Windows用のGDBMの実装は通常にビルドした場合、動的リンク可能なライブラリィつまりはDLLとして配置されるので、今回の場合

ruby.exe -> dbm.so -> gdbm.dll

と呼ばれてくるはずのgdbm.dllをデバッグしてやる訳だ。

デバッグするためにはそのための情報(デバッグデータベース)が必要だが、デバッグ用の情報を出力するためにはコンパイラとリンカに予めそのことを教えてやる必要がある。最初はMakeFileを書き換えて対応しようと思ったのだが、私にそこまでの(MakeFile書き換えの)知識はまだ無いので、今回はVisual Studio上でプロジェクトを作ってデバッグビルドを行うことでデバッグ情報の生成を行うことにした。

プロジェクトといってもWin32 DLL用の空のプロジェクトを作り、そこに必要なソースインクルードファイルを読み込ませるだけの簡単なものだ(一部のヘッダファイルは外部ディレクトリから読み込む必要があるので、都度プロジェクトに追加していく)
f:id:Kazzz:20090512171205g:image
コンパイルリンク時のパラメタとして幾つか気をつけることがあるのだが失念してしまった。


ビルドが成功したならば、デバッグ用のバイナリ(gdbm.dll)とデバッグデータベース(gdbm.pdb)が出来るので、これをrubyインタプリタとdbm.soから見える場所にコピーしておけば準備完了。


デバッグを開始するには、Rubyからスクリプト実行をして適当な場所(データベースをアクセスする直前など)で実行を止める。
f:id:Kazzz:20090512170933g:image

この例では日記で何度も取上げているRubyMineによるデバッグセッションブレークポイントを利用して実行を止めているが、通常のrubyデバッガを使っても問題無いはずだ。

あとはVisual Studioでgdbm.dllをビルドしたプロジェクトから、「デバッグ > プロセスにアタッチ」でRubyインタプリタ(ruby.exe)をアタッチするだけ。
f:id:Kazzz:20090512171203g:image


アタッチが完了した所でRubyインタプリタの実行を再開することにより、拡張ライブラリィ(dbm.so)からgdbm.dllのファンクションが呼ばれるとVisual Studioに制御が移り、デバッグが可能となる。
f:id:Kazzz:20090512171204g:image


試していないが、その気になれば同様の方法でRuby自体のデバッグも可能だ。

2008-09-15

[][]Ruby In Steel Now


件のRuby In Steelだが、自宅の環境にはすんなりインストールできた。
f:id:Kazzz:20080915014528p:image
スクリーンショットはインストールした後にRailsのプロジェクトを作成し、その後generateスクリプト(これもIDEから指示できる)でコントローラを生成した後の様子だ。

スクリーンショットではGuestクラスのnewが表示されないが、インテリセンス補完も動いているぽい。
嬉しい誤算だったのはテキストエディタの設定でRubyがちゃんと個別設定になっている所。
f:id:Kazzz:20080915010944p:image

ちょっと重いがEclipseのRDTとは違い、かなり使えそうな雰囲気。

Rubyを元々使っている人からすれば、このようなIDEで使うのはまどろっこしくてやってらんないだろうが、私はプロジェクトという単位で開発を進めるスタイルや、IDEから言語記述デバッグはおろか、データベース、スクリプトまでを操作する、"環境の一体感/統一感"ってのが好きなので、使えるIDEがあれば使う。むろん、UNIX環境ではそんな馬鹿な事は考えないが。

2008-09-12

[][]Proxy環境下でのRubyGems


Visual Studio(2005, 2008)をRubyIDEのエンジンとして利用する画期的な開発環境である、Ruby In Steelを試してみようと思い、Zipファイルを展開してインストールを始めたのだが、gemによるRailsのgetで止まってしまい、次に進まない。

Updating RubyGems...
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。ま
たは接続済みのホストが応答しなかったため、確立された接続は失敗しました。 - connect(2)(Errno::ETI
MEDOUT)

なんだこりゃ、と思いつつもよく考えたら会社のPCはネットワーク統合後、インターネットにはプロキシ経由でしか通らないんだった。こりゃgemのネットワーク設定をいじる必要がありそうだ。

といいつつもさっぱり解らないので本家で調べてみた。
RubyGems - gem Command Reference
  3. Configuration File
GEM_HOMEに設定ファイル(.gemrc)を置くか、環境変数を設定してコマンドを発行するかの選択らしいが、まずはgemがプロキシ認証を超えられるかを試してみるためにコマンドを使ってみることにした。

プロキシを介してgemを実行するには-pオプションを使用してプロキシに接続するためのURIを記述する。必要であれば認証情報をURIに埋め込むことができる。(パスワードが丸見えなので、一時的な方法に済ませるべきだろう)
gem list rails -r -p http://<ユーザ>:<パスワード>@<プロキシのホスト名>:<ポート>
ERROR: While executing gem ... (NoMethodError) undefined method `[]=' for #<Gem::ConfigFile:0x445e490>

早速失敗だし。
んん? '='メソッドが無いってどういうことよ、と調べて見たのだが、これはRubyGems(1.0以前)のバグらしく、最新にアップデートするか又は以下のように=メソッドを追加することで回避できる。
    def []=(key, value)
      @hash[key] = value
    end

ちょっと怖いけど、スクリプトビルドとかいらないので、パッチ当ても楽だな。

気を取り直してもう一度実行すると、今度は

gem list rails -r -p http://<ユーザ>:<パスワード>@<プロキシのホスト名>:<ポート>
*** REMOTE GEMS ***
ERROR:  While executing gem ... (Gem::RemoteSourceException)
    HTTP Response 407

407ということは認証情報が渡っていないのだろうかと思って調べると、今度はRuby1.8のOpenURIモジュールのバグらしい。

open-uri.rb 407 Proxy Authentication Required (OpenURI::HTTPError)
こちらも、open-uri.rb中の一行(私の使っているバージョンでは216行目)を書き直すことでFIXすることができる。

215  if proxy
216    klass = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password)
217  end

今度こそーーーーと思ったのだが、

gem list rails -r -p http://<ユーザ>:<パスワード>@<プロキシのホスト名>:<ポート>
*** REMOTE GEMS ***
ERROR:  While executing gem ... (URI::InvalidURIError)
    bad URI(is not URI?): http://<ユーザ>:<パスワード>@<プロキシのホスト名>:<ポート>

一筋縄ではいかんな。今度はURIがダメらしい。
ここまで書いていなかったのだが、実はプロキシの認証に使用している「ユーザ」とはWindowsドメインユーザである。
Windowsネットワーク環境でRubyGemsからプロキシサーバの認証を通す場合はちょっと工夫が必要なようで、こんな情報があったので、ruby用のsspiライブラリィ(rubysspi-1.2.4.gem)をインストール、リンクにあるようにコマンドを書き換えて再実行してみた。

gem list rails -r -p http://<ユーザ>:<パスワード>@<プロキシのホスト名>:<ポート>
*** REMOTE GEMS ***
ERROR:  While executing gem ... (RuntimeError)
    Error occurred during proxy negotiation. req: "NTLM TlRMTVNTUAAXXXXXXXXXXXAB
BBBBBBBbbAAAAAAAAAAGAHEXAAAADw=="; res: #<Net::HTTPProxyAuthenticationRequired 4
07 Proxy authentication required readbody=true>; Original message: Error: SEC_E_INVALID_TOKEN

.............
どんどんと深みに嵌っていっている気がするが、どうやらアカウント情報はNTLM認証を介してサーバに渡されているぽい。

Windowsのプロキシサーバのある環境でリモートのgemをインストールしたいだけなのだが、そもそもWindowsのドメインユーザアカウントをrubyから使う、なんてこと自体やっちゃ駄目なことなのだろうか。


....あれ、俺ってば元々何をしたかったんだっけか。


追記:
似た事例がrubyforgeに上がっていたぞ。
cannot use rubysspi with NTLM proxy server
BASIC認証とNTLM認証を取り違えているのか?

2008-05-23

[][]ReSharper 4.0 beta for Visual Studio


ReSharper 4.0 Beta available

Visual Studio 2005用は華麗にスルーかと思ったが、なんと。
f:id:Kazzz:20080523064645p:image
.NET2.0がターゲットであれば、より軽い2005(2008はちょっと重いと思う)をまだ常用しているデベロッパも多いが、費用対効果を考えると両方サポートするのは大変だろうと思っていた。さすがJetBrainsだな。

ということでインストールしてみたのだが、
f:id:Kazzz:20080523073614p:image
2007/12/21以前にVersion3系を購入したユーザは無償アップグレードの対象にはならないらしい。
orz....

2007-11-04

[][]クエリビルダ


データ層をリモートに持つことを前提としている3層のシステムを担当していたこともあり、今まで一度も起動したことが無かった。
f:id:Kazzz:20071103112954p:image
DAOのクエリの確認のために、と思い始めて起動してみたのだが、Accessのクエリビルダに非常に似ているもののAccessとは違い各ペインを縦に同時に表示できたり(Accessの一番嫌いなのが、これが出来ない所だったりする)とかなり良い出来じゃないか。
スクリーンショットはサーバエクスプローラにデータベース接続を作成して(データセットは使わないので作らない)そこからクエリビルダを起動した様子。自動的に生成されたSQLは勿論コピーできるし、空の状態でSQLをコピペすることでSQLが解釈され、その後全てのペインが自動的に構成される。
クエリ全体をオブジェクトとして保存できれば満点なので惜しいなと思ったのだが、ならばAccess使えってことだろう。

2007-10-24

[]冷遇


Releasing the Source Code for the .NET Framework Libraries - ScottGu's Blog

やっとデバッグ時にソースコードを参照できる、ということで素晴らしいことなのだが、残念なのは対象が".NET Framework 3.5 + Visual Studio 2008"ということ。
新しい開発環境の"売り"を増やしたいのだろうが、現在の主力は.NET Framework 2.0 + Visual Studio 2005なのだから、まずはこちらからソースコードを公開すべきではないのか。
今に始まったことではないが、旧バージョンそれも一つ前でしかないバージョンを冷遇するのは良い方針とは思えない。