Pythonのpy2exeで沼ったこと

# 背景
正直こんなことするの自分しかいないな～と思いながら記載します。

# 基本
```main.py
if __name__ == '__main__':
    print 'Hello World'
```
…こんなのexe化する必要ありませんが。
```setup.py
from distutils.core import setup
import py2exe
option = {
    'compressed': 1,
    'optimize': 2,
    'bundle_files': 2,
    'dist_dir': './dist'
}
option_build = {
    'build_base': './build'
}
console_option = {
    'script': 'main.py',
    'dest_base': 'Test'
}
setup(
    options={
        'py2exe': option,
        'build': option_build,
    },
    console=[
        console_option
    ],
    zipfile=None
)
```
実行方法
```
python setup.py py2exe
```
でdistフォルダ配下にexeファイルができあがります。

# 議題
問題はscipyを用いて2次元補間を行おうとした時です。
```main.py
import numpy
from scipy.interpolate import interp2d

if __name__ == '__main__':
    x = numpy.linspace(0, 4, 5)
    y = numpy.linspace(0, 4, 5)
    arr = numpy.arange(25).reshape(5, -1)
    rep_x = numpy.linspace(0, 9, 10)
    rep_y = numpy.linspace(0, 9, 10)
    f = interp2d(x, y, arr, kind='linear')
    result = f(rep_x, rep_y)
    print result
```

# 問題1：MSVCP90.dll
py2exeでexeを作ろうとした時です。
```
error: [Errno 2] No such file or directory: 'MSVCP90.dll'
```
まず皆さん引っかかるのがこちら。

## 解決1
PATHが通っていればこのエラーは出ません。
scipyインストールしているのなら、
Python/Lib/site-packages/scipy/extra-dllにあるので、
どこかにコピーすれば解消されます。
…私はPython/DLLsに配置しちゃったけど解消されました。

# 問題2：他のdll
```
error: [Errno 2] No such file or directory: 'libbispeu.KX7AQLB2Z5NFTCADW76YV5UMLBQNDALD.gfortran-win32.dll'
```
このDLL何？

## 解決2
上のパスにあったので配置。
これが10回ぐらい続くと思いますが、
性懲りもせず配置→実行→エラー→配置を繰り返します。

# 問題3：モジュールエラー
ようやくexeが生成されたので実行したところ、
```
Traceback (most recent call last):
  …
  File "scipy\linalg\_flapack.pyo", line 10, in __load
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
```
？？？
なんでEXE実行エラー発生するの？

## 解決3
setup.pyを変更
```setup.py
from distutils.core import setup
import py2exe
option = {
    'compressed': 1,
    'optimize': 2,
    'bundle_files': 2,
    'dist_dir': './dist',
    'includes': ['scipy',
                 'scipy.linalg.*']
}
option_build = {
    'build_base': './build'
}
console_option = {
    'script': 'main.py',
    'dest_base': 'Test'
}
setup(
    options={
        'py2exe': option,
        'build': option_build,
    },
    console=[
        console_option
    ],
    zipfile=None
)
```
includesで明確に定義しました。

# 問題4：問題2再発生
```
error: [Errno 2] No such file or directory: 'libdfft_sub.W6GELNDTSY664K2YNUHFV652SZYRXMUS.gfortran-win32.dll'
```

## 解決4
解決2と同様

# 問題5：再度発生エラー
ようやくexeが生成されたので実行したところ、
```
Traceback (most recent call last):
  …
  File "scipy\linalg\_flapack.pyo", line 10, in __load
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
```
問題3と同じエラー…。
なんでや工藤！

## 解決5
実はpy2exe実行時の際にdllコピーしているんですが、
1ファイルだけコピーできてなかったので手動でコピーしました。

# 問題6：それでも変わらずエラー
```
Traceback (most recent call last):
  …
  File "_ufuncs.pyx", line 1, in init scipy.special._ufuncs
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
```
ちょっと変わりました。

## 解決6
StackOverflowでこのエラー見たことあります。
setup.pyのincludeに「'scipy.special.*'」を追加します。

# 問題7：もう何回目？のエラー
```
Traceback (most recent call last):
  …
 File "_shortest_path.pyx", line 20, in init scipy.sparse.csgraph._shortest_path
ImportError: No module named _validation
```

## 解決7
これもStackOverflowでこのエラー見たことあります。
(もう結構調べたんで)
setup.pyのincludeに「'scipy.sparse.csgraph._validation'」を追加します。

# 問題8：いい加減にしてくれ…のエラー
```
Traceback (most recent call last):
  …
  File "messagestream.pxd", line 5, in init scipy.spatial.qhull 
ImportError: No module named messagestream
```

## 解決8
これも調査済。
setup.pyのincludeに「'scipy._lib.messagestream'」を追加します。

# 最終系
```setup.py
from distutils.core import setup
import py2exe
option = {
    'compressed': 1,
    'optimize': 2,
    'bundle_files': 2,
    'dist_dir': './dist',
    'includes': ['scipy',
                 'scipy.special.*',
                 'scipy.linalg.*',
                 'scipy.sparse.csgraph._validation',
                 'scipy._lib.messagestream']
}
option_build = {
    'build_base': './build'
}
console_option = {
    'script': 'main.py',
    'dest_base': 'Test'
}
setup(
    options={
        'py2exe': option,
        'build': option_build,
    },
    console=[
        console_option
    ],
    zipfile=None
)
```

これで実行できました。

# 備考
PyInstallerだと…なんて調べたくないです。
(そもそもpy2exeの方が実行速度速いと聞いたのでpy2exeにしました)