CPAN経由のText::MeCabインストールで失敗(解決編)
id:soybeens:20080530の続き。CPAN経由でText:MeCabがインストールにこける件。
今朝方日記にコメントを頂いたので、せっかくだから調べてみた。
なんだかだらだらとした書き方になってしまったので、お急ぎの方は結論だけ見てください。
なぜ失敗するのか
まず、失敗時のログを見ると、
t/node/03_clone................1/28 # Failed test 'Deep clone node B OK' # at t/node/03_clone.t line 33.
t/node/03_clone.tの33行目で失敗している。
CPANがダウンロードしてきたパッケージは、CPANディレクトリ下のsource/authors/id/D/DM/DMAKI/Text-MeCab-0.20007.tar.gzにある。(tarボールの名前はバージョンによって違うかも)
これを手動で解凍して、該当ファイルを見ると、以下。
#!perl use strict; use utf8; use Test::More (tests => 28); use Encode; BEGIN { use_ok("Text::MeCab"); } my $data = { taro => encode(Text::MeCab::ENCODING, "太郎は次郎が持っている本を花子に渡した。"), sumomo => encode(Text::MeCab::ENCODING, "すもももももももものうち。"), }; my $mecab = Text::MeCab->new; my $node_A_orig = $mecab->parse($data->{taro}); ok($node_A_orig, "Original node A OK"); my $node_A = $node_A_orig->dclone; my $node_B_orig = $mecab->parse($data->{sumomo}); ok($node_B_orig, "Original node B OK"); my $node_B = $node_B_orig->dclone; # XXX - better be at least 5 nodes after parsing (this may actually depend # on the dictionary that you are using, but heck, if you are crazy enough # to muck with the dictionary, then you know how to diagnose this test) for(1..5) { ok($node_A, "Deep clone node A OK"); ok($node_B, "Deep clone node B OK"); isa_ok($node_A, "Text::MeCab::Node::Cloned", "Deep clone node A isa OK"); isa_ok($node_B, "Text::MeCab::Node::Cloned", "Deep clone node B isa OK"); if ($node_A->length != 0 || $node_B->length != 0) { isnt($node_A->surface, $node_B->surface, sprintf( "Contents of cloned nodes must differ (A = %s, B = %s)", $node_A->surface, $node_B->surface, ) ); } $node_A = $node_A->next; $node_B = $node_B->next; }
こんなテストやってたんだ(^^;
失敗しているのは、以下の行。
ok($node_B, "Deep clone node B OK");
このok()って関数がなにやってるのかはわからんが、どうせ単体テスト用の判定ルーチンだろうと推測。
つまり、第一引数が非ゼロなら成功と見なすんでないか?
CppUnitとかでもそんなのあった気がするし。
て、ことは・・・エラーの原因は$node_Bがゼロ、つまりNULL(とゆー概念がperlにあるのかわからんが)ってことか。
要するに、インスタンス生成されていないってことだよね。
$node_Bの出所を探ると、すぐ上で生成している行が見つかる。
my $node_B = $node_B_orig->dclone;
つまり、$node_B_orig->dcloneの戻り値がNULLだ、と。
じゃあ、このdcloneという関数の実体はどこにあるのかというと、grepすると出てくる。
text-mecab-node.cの中。
TextMeCab_Node_Cloned * TextMeCab_Node_dclone(node) TextMeCab_Node *node; { TextMeCab_Node_Cloned *prev_node = NULL; TextMeCab_Node_Cloned *cloned_node = NULL; TextMeCab_Node *head = NULL; TextMeCab_Node *current = NULL; TextMeCab_Node_Cloned *tmp = NULL; TextMeCab_Node_Cloned_Meta *meta; /* XXX - We clone the entire node list, to make management easier */ head = node; while (head->prev != NULL) { head = head->prev; } Newz(1234, meta, 1, TextMeCab_Node_Cloned_Meta); current = head; while(current != NULL) { tmp = TextMeCab_Node_clone_single_node(current); if (current == node) { cloned_node = tmp; } tmp->meta = meta; tmp->prev = prev_node; if (prev_node != NULL) { prev_node->next = tmp; } else { meta->first = tmp; } prev_node = tmp; current = current->next; } meta->refcnt++; return cloned_node; }
C言語の関数だけど、同名の関数だから、多分これが本体だろう、と。
perlからどうにかしてこのCの関数を呼び出してるわけだ。
で、この関数がNULLを返のは、head=NULLの場合しか考えられない。
headってのは形態素解析した要素のチェインの先頭なわけで、そいつがNULLってことは・・・。
解析に失敗してんでないの?(^^;
もう一度、テストのコードに戻る。
my $data = { taro => encode(Text::MeCab::ENCODING, "太郎は次郎が持っている本を花子に渡した。"), sumomo => encode(Text::MeCab::ENCODING, "すもももももももものうち。"), }; my $mecab = Text::MeCab->new; my $node_A_orig = $mecab->parse($data->{taro}); ok($node_A_orig, "Original node A OK"); my $node_A = $node_A_orig->dclone; my $node_B_orig = $mecab->parse($data->{sumomo}); ok($node_B_orig, "Original node B OK"); my $node_B = $node_B_orig->dclone;
上記のうち、$node_Aはちゃんとインスタンス生成されているのに、$node_Bはインスタンス生成されない。
その違いは?・・・$node_Bの元になった文字列って、ひらがなだけだよねぇ。
つまり、漢字が混じるとうまく解析できるけど、ひらがなだけだと解析できない。・・・原因、文字コードくさくない?
上記コードで文字コード指定してるのはText::MeCab::ENCODINGっていう変数。これ、何だろう・・・。
はたと気付く。
そういや、テスト始める前に辞書の文字コードを聞かれた。アレでないのか?
Encoding of your mecab dictionary? (shift_jis, euc-jp, utf-8) [utf-8]
何も考えずにそのままEnter押したんだけど、辞書がUTF-8じゃないんじゃないの?
Text::MeCabはCPAN経由でインストールしているが、MeCab本体はaptitudeを使ってインストールした。
デフォルトの文字コードが何になってるのか、よくわからん。
それなら、当たってくだけてみよう、と、とりあえずEUCを指定してみた。
Encoding of your mecab dictionary? (shift_jis, euc-jp, utf-8) [utf-8] euc-jp Using euc-jp as your dictionary encoding Detected the following mecab information: version: 0.93 cflags: -DMECAB_MAJOR_VERSION=0 -DMECAB_MINOR_VERSION=93 -I src libs: -L/usr/lib -lmecab -lstdc++ include: /usr/include reading /usr/include/mecab.h to find all constants Checking if your kit is complete... Looks good Writing Makefile for Text::MeCab cp lib/Text/MeCab/Dict.pm blib/lib/Text/MeCab/Dict.pm cp lib/Text/MeCab/Node.pod blib/lib/Text/MeCab/Node.pod cp lib/Text/MeCab.pm blib/lib/Text/MeCab.pm cp lib/Text/MeCab.xs blib/lib/Text/MeCab.xs /usr/bin/perl /usr/share/perl/5.8/ExtUtils/xsubpp -typemap /usr/share/perl/5.8/ExtUtils/typemap -typemap typemap MeCab.xs > MeCab.xsc && mv MeCab.xsc MeCab.c cc -c -DMECAB_MAJOR_VERSION=0 -DMECAB_MINOR_VERSION=93 -I src -O2 -DVERSION=\"0.20007\" -DXS_VERSION=\"0.20007\" -fPIC "-I/usr/lib/perl/5.8/CORE" -DTEXT_MECAB_ENCODING='"euc-jp"' -DTEXT_MECAB_CONFIG='"/usr/bin/mecab-config"' MeCab.c In file included from MeCab.xs:7: text-mecab.h:124:30: warning: no newline at end of file cc -c -DMECAB_MAJOR_VERSION=0 -DMECAB_MINOR_VERSION=93 -I src -O2 -DVERSION=\"0.20007\" -DXS_VERSION=\"0.20007\" -fPIC "-I/usr/lib/perl/5.8/CORE" -DTEXT_MECAB_ENCODING='"euc-jp"' -DTEXT_MECAB_CONFIG='"/usr/bin/mecab-config"' text-mecab-clone.c In file included from text-mecab-clone.c:7: text-mecab.h:124:30: warning: no newline at end of file cc -c -DMECAB_MAJOR_VERSION=0 -DMECAB_MINOR_VERSION=93 -I src -O2 -DVERSION=\"0.20007\" -DXS_VERSION=\"0.20007\" -fPIC "-I/usr/lib/perl/5.8/CORE" -DTEXT_MECAB_ENCODING='"euc-jp"' -DTEXT_MECAB_CONFIG='"/usr/bin/mecab-config"' text-mecab-node.c In file included from text-mecab-node.c:7: text-mecab.h:124:30: warning: no newline at end of file cc -c -DMECAB_MAJOR_VERSION=0 -DMECAB_MINOR_VERSION=93 -I src -O2 -DVERSION=\"0.20007\" -DXS_VERSION=\"0.20007\" -fPIC "-I/usr/lib/perl/5.8/CORE" -DTEXT_MECAB_ENCODING='"euc-jp"' -DTEXT_MECAB_CONFIG='"/usr/bin/mecab-config"' text-mecab.c In file included from text-mecab.c:7: text-mecab.h:124:30: warning: no newline at end of file text-mecab.c:116:30: warning: no newline at end of file Running Mkbootstrap for Text::MeCab () chmod 644 MeCab.bs rm -f blib/arch/auto/Text/MeCab/MeCab.so LD_RUN_PATH="/usr/lib" cc -shared -L/usr/local/lib MeCab.o text-mecab-clone.o text-mecab-node.o text-mecab.o -o blib/arch/auto/Text/MeCab/MeCab.so \ -lmecab \ chmod 755 blib/arch/auto/Text/MeCab/MeCab.so cp MeCab.bs blib/arch/auto/Text/MeCab/MeCab.bs chmod 644 blib/arch/auto/Text/MeCab/MeCab.bs Manifying blib/man3/Text::MeCab::Dict.3pm lib/Text/MeCab/Dict.pm:166: Unknown command paragraph "=encoding UTF-8" Manifying blib/man3/Text::MeCab::Node.3pm Manifying blib/man3/Text::MeCab.3pm DMAKI/Text-MeCab-0.20007.tar.gz make -- OK Running make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/*/*.t t/01-sanity....................ok t/99-pod-coverage..............skipped: Enable TEST_POD environment variable to test POD t/99-pod.......................skipped: Enable TEST_POD environment variable to test POD t/node/01_load.................ok t/node/02_api..................ok t/node/03_clone................ok t/node/04_clone_free...........ok t/node/05_format...............ok t/regression/01_tomi_args......skipped: SWIG MeCab not available t/tagger/01_load...............ok t/tagger/02_api................ok t/tagger/03_basic..............ok All tests successful. Files=12, Tests=2280, 20 wallclock secs ( 6.10 usr 0.41 sys + 11.53 cusr 0.89 csys = 18.93 CPU) Result: PASS DMAKI/Text-MeCab-0.20007.tar.gz make test -- OK
あっさり通ったorz
ぐぐってもよくわからなかったのに、腹くくってコード見てみたら、15分で解決かよ・・・。