銀月の符号

Python 使い見習いの日記・雑記

形態素解析エンジン MeCab 0.97 とその Python バインディングを MinGW でビルドする

環境は Windows XP, Python 2.5.2(公式サイトのインストーラーより)。この環境に MinGW, msys をセットアップし、これを用いて MeCab 0.97 をビルド。その後 MeCab Python バインディングをビルドした際の手順についてのメモ。

のまえにバイナリが欲しい方へ(2010/8/10 追記)

MeCab 0.98 もあります。また Python 2.6, 2.7 用も作りました。

MinGW インストール

今回は Previous, Candidate ではなく Current 版を入れた。2008/11/8 現在、入った gcc のバージョンは 3.4.5 。

msys インストール

インストール中に以下のように聞かれるので y で答え、 MinGW を入れたディレクトリを入力する(デフォルトの場合 c:/MinGW)。

C:\msys\1.0\postinstall>PATH ...
...
 
C:\msys\1.0\postinstall>..\bin\sh.exe pi.sh
 
This is a post install process that will try to normalize between
your MinGW install if any as well as your previous MSYS installs
if any.  I don't have any traps as aborts will not hurt anything.
Do you wish to continue with the post install? [yn ]

環境変数設定

gcc, g++ で C, C++ コードのコンパイルができるところまで持っていく。http://www.knatech.info/Dev-mingw-install.html の自動設定スクリプト env_set_mingw.js を使用するとすばやく終了できる(ただし PATH 以外のユーザー環境変数を上書きするので下記変数になんらかの値を設定済みの人は注意)。このスクリプトが設定する内容は以下のとおり。

MINGW_HOME (MinGW インストール先, 例 C:\MinGW)
MSYS_HOME (MSYS インストール先, 例 C:\MSYS)
GCC_VER (GCC バージョン, 例 3.4.5)
GCC_EXEC_PREFIX %MINGW_HOME%
PATH MSYS_HOME%\bin;
%MINGW_HOME\bin;
%MINGW_HOME%\libexec\gcc\mingw32\%GCC_VER%;
(既存の文字列)
C_INCLUDE_PATH %MINGW_HOME%\include;
%MINGW_HOME%\lib\gcc\mingw32\%GCC_VER%\include
CPLUS_INCLUDE_PATH %C_INCLUDE_PATH%;
%MINGW_HOME%\include\c++\%GCC_VER%;
%MINGW_HOME%\include\c++\%GCC_VER%\mingw32
LIBRARY_PATH %MINGW_HOME%\lib;
%MINGW_HOME%\lib\gcc\mingw32\%GCC_VER%

さらに、Python バインディングのビルドに備えるため、以下を加える。

C_INCLUDE_PATH (Python 2.5.2 インストール先\include, 例 C:\Python25\include)
LIBRARY_PATH (Python 2.5.2 インストール先\libs, 例 C:\Python25\libs)

MeCab のビルド

MeCab のサイト より mecab-0.97.tar.gz, mecab-ipadic-2.7.0-20070801.tar.gz, mecab-python-0.97.tar.gz を用意する。これを展開する。場所はどこでもよいがスペースの入ったパスは避けるのが無難か。msys 上におけるホームディレクトリ、 (msys インストールディレクトリ\1.0\home\ユーザー名)に置くと楽。

tar を展開する。

$ tar -zxvf mecab-0.97.tar.gz
$ cd mecab-0.97

MeCab 0.97 を MinGW でビルドするにあたって mecab.h, libmecab.cpp に問題があるので次のパッチ mecab-0.97_mingw.patch を当てる。

$ cd src
$ patch < ../../mecab-0.97_mingw.patch
$ cd ..

mecab-0.97_mingw.patch 内容

*** libmecab.cpp	Mon Nov 26 02:27:12 2007
--- libmecab.cpp	Fri Nov 07 11:10:13 2008
***************
*** 50,56 ****
  #if defined(_WIN32) && !defined(__CYGWIN__)
  HINSTANCE DllInstance = 0;
  
! #ifdef __cplusplus
  extern "C" {
  #endif
    BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) {
--- 50,56 ----
  #if defined(_WIN32) && !defined(__CYGWIN__)
  HINSTANCE DllInstance = 0;
  
! #if defined(__cplusplus) && !defined(__MINGW32__)
  extern "C" {
  #endif
    BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) {
***************
*** 62,68 ****
      std::locale::global(loc);
      return TRUE;
    }
! #ifdef __cplusplus
  }
  #endif
  #endif
--- 62,68 ----
      std::locale::global(loc);
      return TRUE;
    }
! #if defined(__cplusplus) && !defined(__MINGW32__)
  }
  #endif
  #endif
*** mecab.h	Sun Jan 20 22:47:06 2008
--- mecab.h	Fri Nov 07 10:35:45 2008
***************
*** 127,133 ****
  extern "C" {
  #endif
  
! #ifdef _WIN32
  #include <windows.h>
  #  ifdef DLL_EXPORT
  #    define MECAB_DLL_EXTERN  __declspec(dllexport)
--- 127,133 ----
  extern "C" {
  #endif
  
! #if defined(_WIN32) && !defined(__MINGW32__)
  #include <windows.h>
  #  ifdef DLL_EXPORT
  #    define MECAB_DLL_EXTERN  __declspec(dllexport)
***************
*** 250,256 ****
  
      virtual ~Tagger() {}
  
! #ifndef SIWG
      static Tagger* create(int argc, char **argv);
      static Tagger* create(const char *arg);
  #endif
--- 250,256 ----
  
      virtual ~Tagger() {}
  
! #ifndef SWIG
      static Tagger* create(int argc, char **argv);
      static Tagger* create(const char *arg);
  #endif

いざビルド。 configure に --enable-utf8-only オプションをつけると扱える文字コードUTF-8 のみになる代わりにバイナリサイズが小さくなる。つけるかどうかは好みで。

$ ./configure --enable-utf8-only
$ make
$ make install
$ cd ..

MeCab 辞書のビルド

続けて辞書のビルド。 --with-charset=utf8 オプションをつけると辞書の文字コードUTF-8 になる。つけなければデフォルトの EUC-JP になる。ここも好みで。 mecab.exe をコマンドプロンプトから多用するのならば --with-charset=sjis で Shift-JIS にしておくと便利。

$ tar -zxvf mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --with-charset=utf8
$ make
$ make install

MeCab Python バインディングのビルド

$ tar -zxvf mecab-python-0.97.tar.gz
$ cd mecab-python-0.97

setup.py の中で呼び出されている mecab-config がうまく動作しないので書き換える。各パスを直接指すように。

setup(
    name = "mecab-python",
    version = "0.97",
    py_modules=["MeCab"],
    ext_modules = [
    Extension("_MeCab",
        ["MeCab_wrap.cxx",],
        include_dirs=[r"C:\msys\1.0\local\include"],
        library_dirs=[r"C:\msys\1.0\local\lib"],
        libraries=["libmecab"])
])

ビルド

$ setup.py build -c mingw32

Python バインディングを使ってみる

をパスの通ったディレクトリへコピー。

の4つを同一ディレクトリにコピー(空白文字を含むパス禁止)。このディレクトリに次の test.py を作成。6行目を辞書の文字コーディングとそろえる。実行。

test.py

# coding: utf-8

import os
import MeCab

encoding = 'utf-8'


def main():
    sentence = u"太郎はこの本を二郎を見た女性に渡した。".encode(encoding)

    try:
        print MeCab.VERSION

        root_dir = os.path.dirname(__file__)
        mecabrc = os.path.join(root_dir, 'mecabrc')
        dicdir = os.path.join(root_dir, 'dic','ipadic')
        t = MeCab.Tagger('-r %s -d %s' % (mecabrc, dicdir))

        print t.parse(sentence).decode(encoding)

        m = t.parseToNode(sentence)
        while m:
            print m.surface.decode(encoding),
            print u"\t".decode(encoding), m.feature.decode(encoding)
            m = m.next
        print u"EOS"

        n = t.parseToNode(sentence)
        len = n.sentence_length;
        for i in range(len + 1):
            b = n.begin_node_list(i)
            e = n.end_node_list(i)
            while b:
                print u"B[%d] %s\t%s" % (i, b.surface.decode(encoding),
                        b.feature.decode(encoding))
                b = b.bnext 
            while e:
                print u"E[%d] %s\t%s" % (i, e.surface.decode(encoding),
                        e.feature.decode(encoding))
                e = e.bnext 
        print u"EOS";

        d = t.dictionary_info()
        while d:
            print u"filename: %s" % d.filename
            print u"charset: %s" %  d.charset
            print u"size: %d" %  d.size
            print u"type: %d" %  d.type
            print u"lsize: %d" %  d.lsize
            print u"rsize: %d" %  d.rsize
            print u"version: %d" %  d.version
            d = d.next

    except RuntimeError, e:
        print "RuntimeError:", e;

if __name__ == '__main__':
    main()

参考サイト

これらのサイトからの情報なくしてはビルド完了までこぎつけることは出来ませんでした。ありがとうございます。

積み残し課題

  • 設定ファイル mecabrc のよりよい置き場所(Tagger の引数で指定? レジストリで指定? モジュールと同じ場所?)
  • 辞書の使いやすい置き場所
  • Python 2.6 の場合