64-bit Windows で MeCab の Java バインディングを使いたい

# あるいは Docker 上の Mingw-w64 環境で 64-bit DLL をクロスコンパイルした作業ログ

ちょっと MeCab を Java で使ってみたいのだが、Windows で MeCab の Java バインディングがうまくいかない。

現時点で入手できる公式の Windows 版 MeCab-0.996 は 32bit バイナリなのに、JVM は 64bit なので JNI-DLL 間のリンクができないということらしい。

よろしい、ならばソースからビルドするまでだ。

しかし、これだけのために Visual Studio だの Cygwin だのフルスタックの開発環境をインストールするのはどうも気が重い。どうせシステムの肥やしになることはわかっている。

イマドキなら開発環境でも、使い捨ての利く仮想環境に用意できてしかるべきだ。

そこで、ここはあえて Docker 上に GCC クロスコンパイル環境を構築し、Windows 版の MeCab64 をビルドしてみようと思う。

用意するもの

* Docker Toolbox
* JDK 8 Window x64
* mecab-0.996.exe (オプション)  
https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7WElGUGt6ejlpVXc

次世代の Docker for Window でも通用するかはまだ未検証。

## Mingw-w64 on Ubuntu on Docker on VirtualBox on Windows

Mingw-w64 は Windows バイナリをターゲットとした GCC コンパイラツールチェーンであり、Linux をホストにクロスコンパイラとして使用することもできる。

* [Mingw\-w64 \- GCC for Windows 64 & 32 bits \[mingw\-w64\]](http://mingw-w64.org/doku.php)

Ubuntu にもそのパッケージが用意されているので、Docker イメージに入れるのには苦労しない。

```bash:Dockerfile
$ cat Dockerfile
FROM ubuntu:trusty

RUN apt-get update && apt-get -f install
RUN apt-get install -y mingw-w64 --no-install-recommends

CMD /bin/bash

$ docker build -t local/mingw64base .
...
...
Successfully built 4c1dd4dc9f83

$ docker run -it --rm local/mingw64base
root@8f8c39682f56:/#
root@8f8c39682f56:/# ls -l /usr/bin | grep gcc
-rwxr-xr-x 1 root root     767736 Jan  6  2014 i686-w64-mingw32-gcc
-rwxr-xr-x 1 root root     767744 Jan  6  2014 i686-w64-mingw32-gcc-4.8
-rwxr-xr-x 1 root root      27088 Jan  6  2014 i686-w64-mingw32-gcc-ar
-rwxr-xr-x 1 root root      27088 Jan  6  2014 i686-w64-mingw32-gcc-nm
-rwxr-xr-x 1 root root      27088 Jan  6  2014 i686-w64-mingw32-gcc-ranlib
-rwxr-xr-x 1 root root     767736 Jan  6  2014 x86_64-w64-mingw32-gcc
-rwxr-xr-x 1 root root     767744 Jan  6  2014 x86_64-w64-mingw32-gcc-4.8
-rwxr-xr-x 1 root root      27088 Jan  6  2014 x86_64-w64-mingw32-gcc-ar
-rwxr-xr-x 1 root root      27088 Jan  6  2014 x86_64-w64-mingw32-gcc-nm
-rwxr-xr-x 1 root root      27096 Jan  6  2014 x86_64-w64-mingw32-gcc-ranlib

root@8f8c39682f56:/# x86_64-w64-mingw32-gcc --version
x86_64-w64-mingw32-gcc (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

root@8f8c39682f56:/# exit
exit

$ 
```
ubuntu:trusty をベースにした場合、デフォルトでインストールされる Mingw-w64 のバージョンは 3.1.0、対応する gcc は 4.8.2。ubuntu のバージョンを上げれば、より最近のバージョンになるはずだが、今回は MeCab のソースが古いので余計な冒険はしない。

mingw-w64 パッケージの依存関係により、 binutils など一通りのツールがインストールされるので、基本的な開発ではこれだけで間にあってしまう。ただ make は別途追加しなければならない。mingw-w64 で `--no-install-recommends` をつけなければ、素の gcc も芋づるでインストールされる。

各ツールコマンドには x86_64-w64-mingw32-* というクロスコンパイル識別用のプレフィックスが付いている。i686-w64-mingw32-* という一式も見えるが、そちらは 32bit ターゲット用なので使わない。

さて、これら GCC ツールを **Linux 付きコマンド** として Windows 側から実行したい。
そのためには、コンテナのコマンドからも Windows のファイルが見えるようにしてやらなければならない。

VirtualBox の設定を見ると `C:¥Users` が Docker マシン の `/c/Users` というパス名でマウントされていて、Windows 側のホームディレクトリ配下を参照できる。

つまり、Docker ボリュームを `/c/Users` 配下のディレクトリに指定すると、コンテナから Windows までが「吹き抜け」になる。

一方、Docker Toolbox ターミナル (= Git Bash) で $HOME をみると　`/c/Users/<user account>` となっていて、こちらも `C:¥` を `/c/` でマウントしている。

それらを踏まえ、以下のようなエイリアスを用意してみる。

```bash
$ alias gw='docker run -it --rm -v "/$HOME":"/$HOME" -w "/$PWD" local/mingw64base '
```

このエイリアス越しにコンテナ内のコマンドを実行すれば、Windows ホームディレクトリ配下で作業している限り、Git Bash とコンテナとでカレントディレクトリのパスを一致させる事ができる。

試しに、C++ ソースをクロスコンパイルしてみよう。

```cpp:hello.cpp
#include <iostream>

int main()
{
    using namespace std;
    cout << "こんにちは、MinGW64" << endl;
    return 0;
}
```
```bash
$ ls
hello.cpp

$ gw ls
hello.cpp*

$ gw x86_64-w64-mingw32-g++ -o hello.exe hello.cpp

$ ls
hello.cpp  hello.exe*

$ ./hello.exe
縺薙ｓ縺ｫ縺｡縺ｯ縲｀inGW64

$ ./hello.exe | cat
こんにちは、MinGW64
```

いけそうやん。

ターミナルで文字化けするのは、なにか Git Bash に対する仁義が足りないのだろうが、UTF-8 で出力できているようなので、とりあえず動作確認できたということで後回し。

問題は、この hello.exe をコマンドプロンプト（DOSプロンプト）側から実行したときに、システムエラーになることだ。

```dos
C:\Users\KUMAZO\hello> hello.exe
```
![libstdc.png](https://qiita-image-store.s3.amazonaws.com/0/21239/4091175b-2816-bd06-598f-70fdb345debb.png)

EXE が依存する DLL が見つからないという。そんなこと言われても DLL が置いてあったコンテナはもう消滅している。

いや逆に、どうして Docker ターミナルからは普通に実行できたのか。

Docker Toolbox ターミナルの実体は Git Bash（Git for Windows）あり、それは Mingw-w64 をベースにしている。プロンプトにもご丁寧に MINGW64 と出ている。Git Bash 上で EXE を実行する分には、コンテナにあったものと同じ DLL ファイルがどこかにあるわけで、パスも通っていてリンクの辻褄が合うのだろう。

Git Bash 上のコマンドとして使うだけのなら、これで問題なさそうだが、今回の MeCab のように、ライブラリ(DLL)として Windows 側から利用したい場合、それではポータビリティに欠ける。

そのためにはできれば依存ライブラリは静的リンクさせておきたい。リンカオプションの `-static` など追加してみる。

```bash
$ gw x86_64-w64-mingw32-g++ -static -o hello.exe hello.cpp
```
```dos
C:\Users\KUMAZO\hello> hello.exe
縺薙ｓ縺ｫ縺｡縺ｯ縲｀inGW64
```

お、エラーにならない、文字化けするけど。

しかし、実はこれでも十分はない。
（MeCab ビルド時にはどうしても、依存 DLL が残った）

どうも、依存ライブラリにさらに依存するライブラリがある場合、`-static` はその孫ライブラリまでたどって面倒はみてくれないようだ。
ほかにもいろいろリンカオプションを調べて試したが、MeCab の依存ライブラリをすべて静的リンクする方法は見つからなかかった。（知っている方がいたらぜひご教示頂きたい）

もう面倒なので、以下のような RUN 命令を Dockerfile に追加する。

```bash
RUN find /usr -name '*.dll' -delete
RUN find /usr -name '*.dll.a' -delete
RUN find /usr -name 'libgcc_s.a' -execdir rm {} \; -execdir ln -s libgcc_eh.a {} \;
```

要は mingw-w64 の DLL ファイルを全て削除してしまう。

リンカは動的ライブラリ(.dll)と静的ライブラリ(.a)の両方のファイルを見つけた場合、動的ライブラリの使用を優先する。
Mingw-w64 のイメージにある DLL を予め全て削除してしまえば、リンカは静的ライブラリのほうを拾わざるを得ない。
こういう乱暴なことを心置きなくできるのも、Docker ならではだ。


```bash
# -static などをつけない
$ gw x86_64-w64-mingw32-g++ -o hello.exe hello.cpp
```
```dos
C:\Users\KUMAZO\hello> hello.exe
縺薙ｓ縺ｫ縺｡縺ｯ縲｀inGW64
```

ぬはは、これでクロスコンパイル環境の見通しが立った。

ついでに Windows アプリのコンパイルでも試しておこうか。

```cpp:hellowin.cpp
//#define UNICODE
//#define _UNICODE
#include <windows.h> 
#include <tchar.h>

int WINAPI
_tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, int nShowCmd)
{
    MessageBox(NULL, _T("こんにちは、MinGW64！"), _T("テスト"),
               MB_OK | MB_ICONINFORMATION);
    return 0;
}
```

```bash
$ gw x86_64-w64-mingw32-gcc --exec-charset=cp932 -o hellowin.exe hellowin.cpp
$ ls
hellowin.cpp  hellowin.exe*
$ ./hellowin.exe
```

![hellowin.png](https://qiita-image-store.s3.amazonaws.com/0/21239/7275f92c-8102-01f1-cbdc-1cf2c94a1c3e.png)


これも UNICODE モードでコンパイルすると文字化けするが、今回の本題ではないので深追いせず先に進める。

## Mecab ビルド環境 の Dockerfile

MeCab のビルド用に必要最低限となるクロスコンパイル環境イメージの Dockerfile は以下の通り。

```bash:Dockerfile
FROM ubuntu:trusty

RUN apt-get update && apt-get -f install
RUN apt-get install -y mingw-w64 --no-install-recommends
RUN apt-get install -y make patch
RUN apt-get install -y wget vim
RUN apt-get install -y swig

RUN find /usr -name '*.dll' -delete
RUN find /usr -name '*.dll.a' -delete
RUN find /usr -name 'libgcc_s.a' -execdir rm {} \; -execdir ln -s libgcc_eh.a {} \;

CMD /bin/bash
```

あとはお好みで使いそうなツールや Git Bash に欲しかったコマンドなどを apt-get しておく。試行錯誤の間は、RUN を小分けしたほうが効率がいい。

イメージをビルドし、エイリアスもきり直す。

```
$ ls
Dockerfile
$ docker build -t local/mingw64mecab .
$ alias gw='docker run -it --rm -v "/$HOME":"/$HOME" -w "/$PWD" local/mingw64mecab '
$ gw ls
Dockerfile
```

以降、gw エイリアスの使いどころに注意されたし。

## Mecab のビルド

MaCab の公式ソースは以下に公開されているものがそうらしい。
（google アカウントが必要かもしれない）

* [MeCab downloads](https://drive.google.com/folderview?id=0B4y35FiV1wh7fjQ5SkJETEJEYzlqcUY4WUlpZmR4dDlJMWI5ZUlXN2xZN2s2b0pqT3hMbTQ&usp=drive_web)

0.996 というのが最新版らしいので、それをダウンロードする。

```
$ gw wget --no-check-certificate 'https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE' -O mecab-0.996.tar.gz
$ tar xzf mecab-0.996.tgz
$ cd mecab-0.996  
$ git init
$ git add .
$ git commit -m 'mecab64'
```

早速ビルドしてみよう

```bash
$ gw ./configure --host x86_64-w64-mingw32 --enable-utf8-only

$ gw make
...
...
...
...
...
```

すごいエラーになるね。
うん、知ってた。

しばし、Google の海へ旅に出る。

Google 賢者の助言を総合すれば、コンパイル・リンクが通る最低限必要なパッチは以下のようなところか。

```diff
$ git diff --no-prefix
diff --git config.h.in config.h.in
index db6d8f1..4bbe7ec 100644
--- config.h.in
+++ config.h.in
@@ -16,7 +16,7 @@
 #undef HAVE_FCNTL_H
 
 /* */
-#undef HAVE_GCC_ATOMIC_OPS
+//#undef HAVE_GCC_ATOMIC_OPS
 
 /* Define to 1 if you have the `getenv' function. */
 #undef HAVE_GETENV
@@ -34,7 +34,7 @@
 #undef HAVE_IO_H
 
 /* Define to 1 if you have the `pthread' library (-lpthread). */
-#undef HAVE_LIBPTHREAD
+//#undef HAVE_LIBPTHREAD
 
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
@@ -49,7 +49,7 @@
 #undef HAVE_OSX_ATOMIC_OPS
 
 /* Define to 1 if you have the <pthread.h> header file. */
-#undef HAVE_PTHREAD_H
+// #undef HAVE_PTHREAD_H
 
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
diff --git src/common.h src/common.h
index 4ed43c9..9329133 100644
--- src/common.h
+++ src/common.h
@@ -81,11 +81,7 @@
 #define EXIT_SUCCESS 0
 #endif
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define WPATH(path) (MeCab::Utf8ToWide(path).c_str())
-#else
 #define WPATH(path) (path)
-#endif
 
 namespace MeCab {
 class die {
diff --git src/mecab.h src/mecab.h
index 6c1b637..ce7f265 100644
--- src/mecab.h
+++ src/mecab.h
@@ -1122,7 +1122,7 @@ public:
 
   virtual ~Model() {}
 
-#ifndef SIWG
+#ifndef SWIG
   /**
    * Factory method to create a new Model with a specified main's argc/argv-style parameters.
    * Return NULL if new model cannot be initialized. Use MeCab::getLastError() to obtain the
@@ -1411,7 +1411,7 @@ public:
 
   virtual ~Tagger() {}
 
-#ifndef SIWG
+#ifndef SWIG
   /**
    * Factory method to create a new Tagger with a specified main's argc/argv-style parameters.
    * Return NULL if new model cannot be initialized. Use MeCab::getLastError() to obtain the
diff --git src/mmap.h src/mmap.h
index 4ef8003..9e96096 100644
--- src/mmap.h
+++ src/mmap.h
@@ -104,7 +104,7 @@ template <class T> class Mmap {
       CHECK_FALSE(false) << "unknown open mode:" << filename;
     }
 
-    hFile = ::CreateFileW(WPATH(filename), mode1, FILE_SHARE_READ, 0,
+    hFile = ::CreateFileA(WPATH(filename), mode1, FILE_SHARE_READ, 0,
                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
     CHECK_FALSE(hFile != INVALID_HANDLE_VALUE)
         << "CreateFile() failed: " << filename;
```

WPATHという Windows 用マクロを潰している。
ちょっとした修正で何とかなりそうだが、ビルド優先で後回し。
たぶん、日本語を含むパスやファイル名が読み込めなくなるだけだ。

pthread(winpthread) が有効になっているとなぜか動かないので、pthread 関連も潰す。
パフォーマンスに影響が出るかもしれない。

あとは SWIG の typo など。

いくつか warning は残るがそれは後回しにするとして、これでコンパイルはどうにかなる。
しかしそれでも make は通らず、wWinMain がどうのこうのというリンクエラーが連発する。

```
...
/usr/lib/gcc/x86_64-w64-mingw32/4.8/../../../../x86_64-w64-mingw32/lib/libmingw32.a(lib64_libmingw32_a-crt0_w.o): In function `wmain':
/build/buildd/mingw-w64-3.1.0/build/x86_64-w64-mingw32-x86_64-w64-mingw32-crt/../../mingw-w64-crt/crt/crt0_w.c:23: undefined reference to `wWinMain'
collect2: error: ld returned 1 exit status
```


このエラーは MeCab 自体のソースではなく、libtool に起因する。
GNU Libtool はライブラリ用のビルドツールで、これが吐き出すライブラリテスト用のラッパー EXE ソースが Windows の UNICODE 規約に対応できていないため、リンクエラーになっている。

MeCab のほうは悪くないので修正したくない。
そもそも修正できたとしても、ubuntu コンテナ上で Windows EXE のテストを走らせようがない。

どうしたものか。

make のログを眺めれば、リンクエラーの時点で MeCab バイナリのビルド自体は完了していることになっている。
libtool はその成果物をどこかに隠しもっている。
src/.libs という隠しディレクトリがあやしい。

```bash
$ cd src/.libs
$ ls
char_property.o         learner_tagger.o        lt-mecab-test-gen.c             mecab-test-gen_ltshwrapper*
connector.o             libmecab.a              mecab.exe*                      nbest_generator.o
context_id.o            libmecab.dll.a          mecab_ltshwrapper*              param.o
dictionary.o            libmecab.la             mecab-cost-train.exe*           string_buffer.o
dictionary_compiler.o   libmecab.lai            mecab-cost-train_ltshwrapper*   tagger.o
dictionary_generator.o  libmecab.o              mecab-dict-gen.exe*             tokenizer.o
dictionary_rewriter.o   libmecab-2.dll*         mecab-dict-gen_ltshwrapper*     utils.o
eval.o                  lt-mecab.c              mecab-dict-index.exe*           viterbi.o
feature_index.o         lt-mecab-cost-train.c   mecab-dict-index_ltshwrapper*   writer.o
iconv_utils.o           lt-mecab-dict-gen.c     mecab-system-eval.exe*
lbfgs.o                 lt-mecab-dict-index.c   mecab-system-eval_ltshwrapper*
learner.o               lt-mecab-system-eval.c  mecab-test-gen.exe*
```

その中に MaCab の実体である mecab.exe と libmecab-2.dll が見える。
叩いてみよう。

```bash
$ ./mecab.exe -v
mecab of 0.996

$ ./mecab.exe
本日は晴天なり
        險伜捷,荳闊ｬ,*,*,*,*,*
{       蜷崎ｩ・繧ｵ螟画磁邯・*,*,*,*,*
日は晴・險伜捷,荳闊ｬ,*,*,*,*,*
V       蜷崎ｩ・蝗ｺ譛牙錐隧・邨・ｹ・*,*,*,*
・險伜捷,荳闊ｬ,*,*,*,*,*
ﾈ・蜷崎ｩ・蝗ｺ譛牙錐隧・邨・ｹ・*,*,*,*
・險伜捷,荳闊ｬ,*,*,*,*,*
EOS

$ echo "本日は晴天なり" | ./mecab.exe | cat
本日    名詞,副詞可能,*,*,*,*,本日,ホンジツ,ホンジツ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
晴天    名詞,一般,*,*,*,*,晴天,セイテン,セイテン
なり    助動詞,*,*,*,文語・ナリ,基本形,なり,ナリ,ナリ
EOS 

```
こいつ、動くぞ！

相変わらずターミナルでは文字化けするが、UTF-8 の日本語がちゃんと形態素解析できている。すばらしい。

あれ、でもちょっと、それはヘンですよ？
まだ辞書を用意していないんですけど？
MeCabって辞書なしでも動かせるものなんだっけ？

辞書のパスを見るには `-D` オプション。

```bash
$ ./mecab.exe -D
filename:       D:\Tools\MeCab\etc\..\dic\ipadic\sys.dic
version:        102
charset:        UTF-8
type:   0
size:   392126
left size:      1316
right size:     1316
```

おぉっと、このパスはこの前 Windows 版 MeCab のインストーラで指定したパスだ。インストールしたままだったのを忘れてた。
そうか MeCab は Windows レジストリを見てるのか。
MeCab、おそろしい子！

辞書を作るコマンドも生成されているようだが、どうせ make install はできないので辞書を作るのは面倒だ。
今はこのインストール済み辞書（UTF-8版）をそのまま使わせてもらう。
自作辞書でも、Java から使うときに、実行時に辞書ファイルを指定できるのでご安心を。

## Mecab-java のビルド

Java バインディングには SWIG というツールを使う。

SWIG は、C++ ヘッダファイルとインターフェース記述ファイルから JNI ソース(C++)とそれに対応する Java クラスソースを生成する。

これらをコンパイルして初めて、Javaから MeCab(dll)を使えるようになる。

まずは MeCab 公式の SWIG 生成ソースが配布されているのでダウンロードしておく。

```
$ gw wget --no-check-certificate 'https://drive.google.com/uc?export=download&i
d=0B4y35FiV1wh7NHo1bEJxd0RnSzg' -O mecab-java-0.996.tar.gz
$ tar xzf mecab-java-0.966.tar.gz
```

しかし、このソースは linux 用なので使えない。
そもそも、MeCab のヘッダーファイルも修正してしまったので、どのみち SWIG ソースから作り直しなのだ。
後で Makefile とテストコードだけもらう。

MeCab 本体のソースにある siwg ディレクトリに移動し、SWIG ソースの生成から作り直す。

```bash
$ cd mecab-0.996/swig

$ ls
Makefile  MeCab.i  version.h  version.h.in

$ gw make java
swig -java -package org.chasen.mecab -c++ MeCab.i
../src/mecab.h:136: Warning 302: Identifier 'surface' redefined by %extend (ignored),
MeCab.i:74: Warning 302: %extend definition of 'surface'.
../src/mecab.h:848: Warning 302: Identifier 'set_sentence' redefined by %extend (ignored),
MeCab.i:95: Warning 302: %extend definition of 'set_sentence'.
mkdir -p ../java/org/chasen/mecab
mv -f MeCab_wrap.cxx ../java
mv -f *.java ../java/org/chasen/mecab

$ cd ../java
$ find .
.
./MeCab_wrap.cxx
./org
./org/chasen
./org/chasen/mecab
./org/chasen/mecab/DictionaryInfo.java
./org/chasen/mecab/Lattice.java
./org/chasen/mecab/MeCab.java
./org/chasen/mecab/MeCabConstants.java
./org/chasen/mecab/MeCabJNI.java
./org/chasen/mecab/Model.java
./org/chasen/mecab/Node.java
./org/chasen/mecab/Path.java
./org/chasen/mecab/Tagger.java
```

なんか警告がでるが、これも後回し。

javac は Windows 側の JDK を使う。
MeCab_wrap.cxx のコンパイルにには JDK 同梱の JNI ヘッダファイルが必要だ。
JDK に Windows 用のヘッダファイルが含まれるのでそれをインクルードパスに追加する。
ただホームディレクトリの外は Docker からは見えないのでシンボリックリンクを張る。（実際はただのファイルコピーだが）

あと、ダウンロードした macab-java-0.996 から Makefile と test.java を持ってくる。

```
$ export JAVA_HOME=/c/path/to/jdk
$ export PATH=$PATH:$JAVA_HOME/bin
$ javac -version
javac 1.8.0_91
$ ln -s $JAVA_HOME/include .
$ cp ../../mecab-java-0.996/test.java .
$ cp ../../mecab-java-0.996/Makefile .
$ ls
include/  Makefile  MeCab_wrap.cxx  org/  test.java
```

Makefile を mingw 向けに編集する。Java 関連はマニュアルでコンパイルするので削除。

```bash
TARGET=MeCab
CXX=x86_64-w64-mingw32-g++
INCLUDE=./include

PACKAGE=org/chasen/mecab

LIBS=-L../src/.libs -lmecab -lstdc++
INC=-I../src -I$(INCLUDE) -I$(INCLUDE)/win32

all:
        $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx  $(INC)
        $(CXX) -shared  $(TARGET)_wrap.o -o $(TARGET).dll $(LIBS)

clean:
        rm -fr *.jar *.o *.so *.class $(PACKAGE)/*.class

cleanall:
        rm -fr $(TARGET).java *.cxx
```

ビルドしてテストを走らせてみよう。

```bash

$ gw make
$ javac org/chasen/mecab/*.java
$ javac -encoding UTF-8 test.java
$ jar cfv MeCab.jar org/chasen/mecab/*.class
$ ls
include/  MeCab.dll*  MeCab_wrap.cxx  org/        test.java
Makefile  MeCab.jar   MeCab_wrap.o    test.class

$ java test
Cannot load the example native code.
Make sure your LD_LIBRARY_PATH contains '.'
java.lang.UnsatisfiedLinkError: C:\Users\KUMAZO\docker\mingw64\mecab-0.996\java\MeCab.dll: Can't find dependent libraries

$ ln -s ../src/.libs/libmecab-2.dll .

$ java test
0.996
太郎    名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
二郎    名詞,固有名詞,人名,名,*,*,二郎,ジロウ,ジロー
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
この    連体詞,*,*,*,*,*,この,コノ,コノ
本      名詞,一般,*,*,*,*,本,ホン,ホン
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
渡し    動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。      記号,句点,*,*,*,*,。,。,。
EOS

        BOS/EOS,*,*,*,*,*,*,*,*
太郎    名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
...
...

```

libmecab-2.dll が見えていないようなので、同じディレクトリ内にコピーしたら通るようになった。
Windows 側から実行するなら PATH が libmecab-2.dll のあるディレクトリに通っていればいいようだ。

## Javaから使ってみる。

適当なディレクトリに MeCab.dll, libmecab-2.dll, MaCab.jar をコピーし、PATH を通しておく。
MaCab.jar にはクラスパスも通す。
MeCab.dll をロードするのはアプリ側の責務だ。
辞書を指定したい場合には、Taggerコンストラクタに、mecab のパラメータ形式でパスを指定する。

```java
import org.chasen.mecab.Tagger;

public class MecabTest {

    static {
        System.loadLibrary("MeCab");
    }
    
    public static void main(String[] args) {
        String sample = "生方生子は生粋の生駒生まれだが生憎生霊への生贄としてその生身をささげ今生は苔生す森で生神を生業として生活し子を生すこともなく生涯生娘として生きた。";
        Tagger tagger = new Tagger ("");
        System.out.println (tagger.parse(sample));         
    }
}
```
<pre>
生方	名詞,固有名詞,人名,姓,*,*,生方,ウブカタ,ウブカタ
生子	名詞,固有名詞,人名,名,*,*,生子,イクコ,イクコ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
生粋	名詞,一般,*,*,*,*,生粋,キッスイ,キッスイ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
生駒	名詞,固有名詞,地域,一般,*,*,生駒,イコマ,イコマ
生まれ	名詞,一般,*,*,*,*,生まれ,ウマレ,ウマレ
だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
が	助詞,接続助詞,*,*,*,*,が,ガ,ガ
生憎	副詞,一般,*,*,*,*,生憎,アイニク,アイニク
生霊	名詞,一般,*,*,*,*,生霊,イキリョウ,イキリョー
へ	助詞,格助詞,一般,*,*,*,へ,ヘ,エ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
生贄	名詞,一般,*,*,*,*,生贄,イケニエ,イケニエ
として	助詞,格助詞,連語,*,*,*,として,トシテ,トシテ
その	連体詞,*,*,*,*,*,その,ソノ,ソノ
生身	名詞,一般,*,*,*,*,生身,ナマミ,ナマミ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
ささげ	動詞,自立,*,*,一段,連用形,ささげる,ササゲ,ササゲ
今生	名詞,一般,*,*,*,*,今生,コンジョウ,コンジョー
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
苔	名詞,一般,*,*,*,*,苔,コケ,コケ
生す	動詞,自立,*,*,五段・サ行,基本形,生す,ナス,ナス
森	名詞,一般,*,*,*,*,森,モリ,モリ
で	助詞,格助詞,一般,*,*,*,で,デ,デ
生神	名詞,固有名詞,地域,一般,*,*,生神,ウルカミ,ウルカミ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
生業	名詞,一般,*,*,*,*,生業,ナリワイ,ナリワイ
として	助詞,格助詞,連語,*,*,*,として,トシテ,トシテ
生活	名詞,サ変接続,*,*,*,*,生活,セイカツ,セイカツ
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
子	名詞,一般,*,*,*,*,子,コ,コ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
生す	動詞,自立,*,*,五段・サ行,基本形,生す,ナス,ナス
こと	名詞,非自立,一般,*,*,*,こと,コト,コト
も	助詞,係助詞,*,*,*,*,も,モ,モ
なく	形容詞,自立,*,*,形容詞・アウオ段,連用テ接続,ない,ナク,ナク
生涯	名詞,副詞可能,*,*,*,*,生涯,ショウガイ,ショーガイ
生娘	名詞,一般,*,*,*,*,生娘,キムスメ,キムスメ
として	助詞,格助詞,連語,*,*,*,として,トシテ,トシテ
生き	動詞,自立,*,*,一段,連用形,生きる,イキ,イキ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。	記号,句点,*,*,*,*,。,。,。
EOS
</pre>



## 動いた

少なくとも見かけ上は。
いい加減なこともしているので、使っていくうちになにか変な事もあるかもしれない。
実用上問題ないとは保証できない。

一方、Docker をクロスコンパイル環境として使えるかという検証としては、その有効性と可能性が確認できたのではないだろうか。

## イメージはもういらない

Dockerfile さえあればいいのだ。

```bash
$ docker rmi local/mingw64mecab
```

すっきり。

## 参考

* MeCab: Yet Another Part-of-Speech and Morphological Analyzer  
http://taku910.github.io/mecab/
* MeCab downloads  
https://drive.google.com/folderview?id=0B4y35FiV1wh7fjQ5SkJETEJEYzlqcUY4WUlpZmR4dDlJMWI5ZUlXN2xZN2s2b0pqT3hMbTQ&usp=drive_web
* スクリプト言語のバインディング  
https://taku910.github.io/mecab/bindings.html
* MeCab - 日本語形態素解析システム  
https://www.mlab.im.dendai.ac.jp/~yamada/ir/MorphologicalAnalyzer/MeCab.html
* 絶対領域(AbsoluteArea)の徒然: MeCab を MinGW-w64 でビルド。ついでに、Java バインディングもビルド  
http://absolutearea.blogspot.jp/2010/11/mecab-mingw-w64-java.html
* 絶対領域(AbsoluteArea)の徒然: Ubuntu 上に、Windows 64bit 用のクロスコンパイル環境を構築  
http://absolutearea.blogspot.jp/2012/09/ubuntu-windows-64bit.html
* 64bit Windows + python 2.7 + MeCab 0.996　な環境をつくる - Qiita  
http://qiita.com/h_kabocha/items/5bee9e9b852aed11411b
* patch for mecab-0.996 build on MinGW48_32 · GitHub  
https://gist.github.com/mugwort-rc/25d1be5b81b1234417aa
* 形態素解析エンジン MeCab 0.97 とその Python バインディングを MinGW でビルドする - 銀月の符号  
http://d.hatena.ne.jp/fgshun/20081109/1226221701
* 形態素解析エンジン MeCab 0.98pre3 野良ビルド - 銀月の符号  
http://d.hatena.ne.jp/fgshun/20090910/1252571625
* MeCab JavaバインディングをWindowsで / FULL Tablog 2  
http://ftablog.s56.xrea.com/index.php?itemid=167
* Debian GNU/LinuxでWindows用バイナリをビルドする方法 - ククログ(2011-10-13)  
http://www.clear-code.com/blog/2011/10/13.html
* Dockerにホストのフォルダをマウントしたい！ - Qiita  
http://qiita.com/dojineko/items/f623894ef2436bef890e 
* Stdcall and DLL tools of MSVC and MinGW  
http://wyw.dcweb.cn/stdcall.htm
* MeCab:Javaバインディング開発環境構築、実装参考ページ等 - Qiita  
http://qiita.com/YasuyukiKawai/items/13d669556ec1ea15f9ae
* takscape/cmecab-java  
https://github.com/takscape/cmecab-java
* JNIメモ(Hishidama's Java native interface Memo)  
http://www.ne.jp/asahi/hishidama/home/tech/java/jni.html
* JavaからCの処理を呼ぶ方法(JNI/JNA/SWIG) - Qiita  
http://qiita.com/kiida/items/9d26b850194fa1a02e67
* JNI-MinGW-DLL | MinGW  
http://www.mingw.org/wiki/JNI_MinGW_DLL
* libtool - Qiita  
http://qiita.com/false-git@github/items/a21dbd0f25d8cc91d017
* Chapter 3. Building All Kinds of Libraries — libtool - Autotools Mythbuster  
https://autotools.io/libtool/index.html
* Bug 1124436 – libtool wrapper corrupts command line on Windows  
https://bugzilla.redhat.com/show_bug.cgi?id=1124436
* MSVC and MinGW DLLs | MinGW  
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
* MinGW-w64でlibwinpthread-1.dllの静的リンク  
http://arithmeticoverflow.blog.fc2.com/blog-entry-65.html
* c++ - how to do static linking of libwinpthread-1.dll in mingw? - Stack Overflow  
http://stackoverflow.com/questions/13768515/how-to-do-static-linking-of-libwinpthread-1-dll-in-mingw
* Libtool: Inter-library dependencies  
https://www.gnu.org/software/libtool/manual/html_node/Inter_002dlibrary-dependencies.html
* how to link static library into dynamic library in gcc - Stack Overflow  
http://stackoverflow.com/questions/2649735/how-to-link-static-library-into-dynamic-library-in-gcc
* Link Options - Using the GNU Compiler Collection (GCC)   
https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Link-Options.html#Link-Options