Mecab-python、natto-py に失敗した話、の続きというか。
- natto-py など、すぐに手に入るヤツが Windows で全滅だった
- mecab 公式のレポジトリにある python バインディングが保守されてない
- 幸いmecab 公式のpython バインディングは(素の) swig なので
「生成し直しちゃいなよ」、まる。
mecab 公式レポジトリにある「swig による生成物」は、古い swig と古い python (2.6 以下)によるものなのだが、インターフェイス(*.i)の仕様が変わってないんであれば、「新しい swig で」生成し直せば、望みの python 用になるであろう、と。
これも幸いというかなんというか、mecab 公式レポジトリにある「swig による生成物、をインストールするための setup.py」はほんっと最小限のことしかしてないんで、自力で全部書き下してしまったほうが早いと思われる。
つーわけで。
さて、swig は確か前に持ってきてたよなぁ…、と探すも、見つからない。うーん、かなり前だからなぁ、もしかして、今の PC の前のヤツだった頃か? とすりゃ、もう6年以上前だわ。なので http://www.swig.org/download.html より swigwin-4.0.1 をダウンロード。「インストール」の公式な方法ってのはなさげね。バイナリ一個をぺろっと好きな場所に置くだけ、か。
んで、必要なのは「mecab のソースパッケージ」ね。GitHub からか 公式ドキュメント のどっちからでもいいけど、それの mecab/swig が目的のもの。
1 [me@host: mecab-0.996]$ mkdir python
2 [me@host: mecab-0.996]$ cd swig
3 [me@host: swig]$ make SWIG=../../swigwin-4.0.1/swig.exe python
4 c:/Users/hhsprings/swigwin-4.0.1/swig.exe -python -shadow -c++ MeCab.i
5 ..\src\mecab.h(136) : Warning 302: Identifier 'surface' redefined by %extend (ignored),
6 MeCab.i(74) : Warning 302: %extend definition of 'surface'.
7 ..\src\mecab.h(848) : Warning 302: Identifier 'set_sentence' redefined by %extend (ignored),
8 MeCab.i(95) : Warning 302: %extend definition of 'set_sentence'.
9 mv -f MeCab_wrap.cxx ../python
10 mv -f MeCab.py ../python
11 [me@host: swig]$ ls -lh ../python
12 total 277K
13 -rw-r--r-- 1 hhsprings Administrators 13K Nov 20 21:05 MeCab.py
14 -rw-r--r-- 1 hhsprings Administrators 264K Nov 20 21:05 MeCab_wrap.cxx
と、ここまでは良かったのだが。
結論としては、「MeCab 自身の 64bit 対応が出来てないので、CPython amd64 版は(MeCab 自身の改変なしでは)不可能」。一時間ほど弄んでてようやく理解した。試してないが、間違いなく 32bit 版 CPython なら問題なく作れるハズ。
一応こんな setup.py を書いてみていたのよ:
1 # -*- coding: utf-8 -*-
2 import re
3 from distutils.core import setup, Extension
4 #import string
5
6 #def cmd1(str):
7 # return os.popen(str).readlines()[0][:-1]
8
9 #def cmd2(str):
10 # return string.split(cmd1(str))
11
12
13 _OBJS = """feature_index.obj param.obj learner.obj string_buffer.obj \
14 char_property.obj learner_tagger.obj tagger.obj \
15 connector.obj tokenizer.obj \
16 context_id.obj dictionary.obj utils.obj \
17 dictionary_compiler.obj viterbi.obj \
18 dictionary_generator.obj writer.obj iconv_utils.obj \
19 dictionary_rewriter.obj lbfgs.obj eval.obj nbest_generator.obj"""
20 _SRCS = ["../src/{}".format(obj.replace(".obj", ".cpp"))
21 for obj in re.split(r"\s+", _OBJS)]
22 setup(
23 name="mecab-python",
24 version="0.996", # cmd1("mecab-config --version"),
25 py_modules=["MeCab"],
26 ext_modules=[
27 Extension(
28 "_MeCab",
29 #_SRCS + ["MeCab_wrap.cxx",],
30 ["MeCab_wrap.cxx",],
31 #c:/Program Files (x86)/MeCab/sdk
32 #define_macros=[("_WIN32",)],
33 #extra_compile_args=[
34 # "-O2",
35 # #"-GL",
36 # "-GA",
37 # "-Zi",
38 # ],
39 #define_macros=[
40 # ("HAVE_STDINT_H", ""),
41 # #("WIN64", ""),
42 # #("_WIN64", ""),
43 # #("_WIN32", ""),
44 # ("_CRT_SECURE_NO_WARNINGS", "1"),
45 # ("_CRT_SECURE_NO_DEPRECATE", "1"),
46 # ("MECAB_USE_THREAD", ""),
47 # ("HAVE_GETENV", ""),
48 # ("HAVE_WINDOWS_H", ""),
49 # ("DIC_VERSION", "102",),
50 # ("VERSION", "0.996"),
51 # ("PACKAGE", '"\\"mecab\\""'),
52 # ("UNICODE", ""),
53 # ("_UNICODE", ""),
54 # #("MECAB_DEFAULT_RC", '"\\"c:/Program Files (x86)/MeCab/etc/mecabrc\\""'),
55 # ("MECAB_DEFAULT_RC", 'mecabrc'),
56 # ],
57 undef_macros=[
58 "DLL_EXPORT",
59 #"NDEBUG"
60 ],
61 include_dirs=["../src"], # include_dirs=cmd2("mecab-config --inc-dir"),
62 library_dirs=["../src"], # library_dirs=cmd2("mecab-config --libs-only-L"),
63 #libraries=["ADVAPI32"],
64 libraries=["libmecab"], # libraries=cmd2("mecab-config --libs-only-l"))
65 )
66 ])
コメントアウトの嵐が試行錯誤の痕跡。あれこれやってて、やっと MeCab 自身の問題だと理解した。
ちゃんと続ければ、対応は可能だと思う。その場合、システムにインストールされた「公式の MeCab バイナリ」にリンクするんではなくて、コメントアウト内で試みている「抱え込み」にすべき。_MeCab.pyd 内に取り込まない場合であっても、いずれにしても「64bit 版 libmecab.dll/libmecab.lib」のビルドは必要である。(その場合は、_MeCab.pyd と libmecab.dll を同じ場所に配備することになる。)
ソースの改変がとてつもなく大変だとは思わない。そもそも型のオーバロードが特定できない、という理由で「コンパイル出来ない」ことが原因なのだが、typedef をごにょごにょするか、特殊化を追加するか、てとこだと思う。ただ、そうやって改変したものの「正しさ」の検証の方は多分大変だろうね。
うーん、どうすっかなー…。悔しいという気持ちはあるのだけれど、別に subprocess 経由で使う使い方がとてつもなくイヤだ、てほどでもないんだよね。(ワタシの使い方だと結局一つのドキュメント相手に一回しか呼び出さないので。)誰かやってくんないかなー。