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 2008 | 0x0600 |
Windows Vista | 0x0600 |
Windows Server 2003 with SP1, Windows XP with SP2 | 0x0502 |
Windows Server 2003, Windows XP | 0x0501 |
Windows 2000 | 0x0500 |
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
- nmakeを使ってビルド、インストールする (dbm.so)
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="さて、いよいよだ。test_dbm.rbを実行してみよう。 ヤッターーーーーーーーーーーーーーーーーーーーーーーーーーー ちなみにtest_gdbm.rbも元々失敗していた二つのテスト(test_reorganizeとtest_s_open_create_new)以外は全てグリーンであることを確認できた。 やっと全てのテストが通った。これが何かの役に立つとは思えないけど(今更DBM、それもWindows上での需要があるとは思えない)、諦めずにいろいろとやってきて良かった。とはいえ、知っている方であれば一日もたたずに完了した作業を数日間かけている当たり、やはりこの辺は知らないことが多いことを露呈した恰好だ。 追記: CopyFileの代わりにCreateHardLinkを使うことでテストが通った訳だが、問題が無い訳ではない。というのもCreateHardLinkを使う際には制限が存在しているからだ。" -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
- CreateHardLinkの制限
- Windows 2000以降のO/Sを使用している
- 対象ファイルが属するボリュームはNTFSフォーマットされている
- テストコードは偉大だ
- 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