pyinstaller で wxpython を使用した python コードを Linux 向けにバイナリ化する

結構はまったので、メモ代わりに書いておく。

  • まず最初に、pyinstaller とは
    • python のコードをバイナリ化してくれるプログラム(Unix系、Windows、ともに対応している)
    • 他にも似たようなものとして freeze(Unix系のみ)、py2exe(Windowsのみ)などが存在する
  • Linux 向けバイナリ作成時の基本的な使用方法

ほぼ README.txt に書いてある内容だが、個人的に 1 ファイルのバイナリにしたいので「Makespec.py」に「-F」を付けている点が異なる。「-F」をつけない場合、複数のバイナリファイルが作成され、それらを配布する必要がある。

$ cd source/linux #初回のみ実行する
$ python ./Make.py #初回のみ実行する
$ make #初回のみ実行する
$ cd ../../ #初回のみ実行する
$ python Configure.py #初回のみ実行する

$ python Makespec.py -F /path/to/yourscript.py #ここで「/path/to/yourscript.spec」が作成される
$ python Build.py /path/to/yourscript.spec #ここで「/path/to/yourscript」が作成される

通常は、この手順で「/path/to/yourscript」にバイナリが作成され、実行できるようになる。
だが、おそらく wxpython のような python で記述されていないライブラリを使用しているプログラムの場合、

ImportError: libwx_gtk2u_richtext-2.8.so.0: cannot open shared object file: No such file or directory

などのようなエラーが出力されて実行できない。(この辺は python コードの内容により変わるかも)

    • wxpython を利用した python コードから Linux 向けバイナリを作成する方法
      • 「$ python Makespec.py -F /path/to/yourscript.py」実行後に「/path/to/yourscript.spec」を編集して libwx_gtk2u_* のライブラリを含むように設定する

$ cd source/linux #初回のみ実行する
$ python ./Make.py #初回のみ実行する
$ make #初回のみ実行する
$ cd ../../ #初回のみ実行する
$ python Configure.py #初回のみ実行する

$ python Makespec.py -F /path/to/yourscript.py #ここで「/path/to/yourscript.spec」が作成される
$ vim /path/to/yourscript.spec #ここで libwx_gtk2u_* のライブラリを含むように設定する
$ python Build.py /path/to/yourscript.spec #ここで「/path/to/yourscript」が作成される

      • 「/path/to/yourscript.spec」の書き方

http://pyinstaller.hpcf.upr.edu/docs/Manual_v1.1.html#toc-class-table-of-contents
にあるように、作成するバイナリに別途共有ライブラリを追加したい場合には以下のような書式になる。

collect = COLLECT(a.binaries +
[('readme', '/my/project/readme', 'DATA')], ...)

具体的には、libwx というディレクトリに「libwx_gtk2u_*」のライブラリを置いた場合、spec ファイル内の

exe = EXE( pyz,
a.scripts,
a.binaries,
name='yourscript',
debug=False,
strip=False,
upx=False,
console=1 )

の箇所を以下のように変更する。(ファイル名に付いているバージョンなどは各環境で異なってくると思います)

exe = EXE( pyz,
a.scripts,
a.binaries + [('libwx_gtk2u_qa-2.8.so.0.5.0','libwx/libwx_gtk2u_qa-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_adv-2.8.so.0.5.0','libwx/libwx_gtk2u_adv-2.8.so.0.5.0','BINARY')] + [('libwx_baseu-2.8.so.0','libwx/libwx_baseu-2.8.so.0','BINARY')] + [('libwx_gtk2u_media-2.8.so.0.5.0','libwx/libwx_gtk2u_media-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_media-2.8.so.0','libwx/libwx_gtk2u_media-2.8.so.0','BINARY')] + [('libwx_gtk2u_core-2.8.so.0','libwx/libwx_gtk2u_core-2.8.so.0','BINARY')] + [('libwx_gtk2u_xrc-2.8.so.0','libwx/libwx_gtk2u_xrc-2.8.so.0','BINARY')] + [('libwx_gtk2u_aui-2.8.so.0','libwx/libwx_gtk2u_aui-2.8.so.0','BINARY')] + [('libwx_gtk2u_svg-2.8.so.0','libwx/libwx_gtk2u_svg-2.8.so.0','BINARY')] + [('libwx_gtk2u_ogl-2.8.so.0.5.0','libwx/libwx_gtk2u_ogl-2.8.so.0.5.0','BINARY')] + [('libwx_baseu_xml-2.8.so.0','libwx/libwx_baseu_xml-2.8.so.0','BINARY')] + [('libwx_gtk2u_gizmos_xrc-2.8.so.0','libwx/libwx_gtk2u_gizmos_xrc-2.8.so.0','BINARY')] + [('libwx_gtk2u_core-2.8.so.0.5.0','libwx/libwx_gtk2u_core-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_aui-2.8.so.0.5.0','libwx/libwx_gtk2u_aui-2.8.so.0.5.0','BINARY')] + [('libwx_baseu-2.8.so.0.5.0','libwx/libwx_baseu-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_gl-2.8.so.0','libwx/libwx_gtk2u_gl-2.8.so.0','BINARY')] + [('libwx_gtk2u_ogl-2.8.so.0','libwx/libwx_gtk2u_ogl-2.8.so.0','BINARY')] + [('libwx_gtk2u_gizmos_xrc-2.8.so.0.5.0','libwx/libwx_gtk2u_gizmos_xrc-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_gizmos-2.8.so.0','libwx/libwx_gtk2u_gizmos-2.8.so.0','BINARY')] + [('libwx_baseu_net-2.8.so.0.5.0','libwx/libwx_baseu_net-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_stc-2.8.so.0.5.0','libwx/libwx_gtk2u_stc-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_adv-2.8.so.0','libwx/libwx_gtk2u_adv-2.8.so.0','BINARY')] + [('libwx_gtk2u_gizmos-2.8.so.0.5.0','libwx/libwx_gtk2u_gizmos-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_richtext-2.8.so.0','libwx/libwx_gtk2u_richtext-2.8.so.0','BINARY')] + [('libwx_gtk2u_gl-2.8.so.0.5.0','libwx/libwx_gtk2u_gl-2.8.so.0.5.0','BINARY')] + [('libwx_baseu_net-2.8.so.0','libwx/libwx_baseu_net-2.8.so.0','BINARY')] + [('libwx_baseu_xml-2.8.so.0.5.0','libwx/libwx_baseu_xml-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_richtext-2.8.so.0.5.0','libwx/libwx_gtk2u_richtext-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_svg-2.8.so.0.5.0','libwx/libwx_gtk2u_svg-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_html-2.8.so.0.5.0','libwx/libwx_gtk2u_html-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_xrc-2.8.so.0.5.0','libwx/libwx_gtk2u_xrc-2.8.so.0.5.0','BINARY')] + [('libwx_gtk2u_html-2.8.so.0','libwx/libwx_gtk2u_html-2.8.so.0','BINARY')] + [('libwx_gtk2u_stc-2.8.so.0','libwx/libwx_gtk2u_stc-2.8.so.0','BINARY')] + [('libwx_gtk2u_qa-2.8.so.0','libwx/libwx_gtk2u_qa-2.8.so.0','BINARY')],
name='yourscript',
debug=False,
strip=False,
upx=False,
console=1 )

とりあえず、これで 1 つのファイルにバイナリをまとめることができました。
ただ、60 MB くらいのファイルサイズになり、起動時間も多少重くなった感じだったので、あまり使い勝手は良くないかもしれません。

他のライブラリに関しても、環境によってはライブラリが存在しないという現象が出てくるかもしれません。
そのときには同様にライブラリ追加の記述をしてやることで回避できると思います。

「strace yourscript」のように、「strace」を利用すると、「open()」されているライブラリファイルを調査できるので、どのライブラリをバイナリに含めるべきかヒントが得られるかと思います。

ただ、この設定ファイルの記述方法はライブラリファイルが増えた場合に不便です。
もっと良い方法があるのかもしれませんが、自分の調査した範囲ではこれが限界でした。