QuickIR を cx_Freeze で Windows の EXEファイルにしてみた(1)
これがかなりハマった
QuickIRを Macアプリケーション化したのに次いで、今度は Windows のEXEファイル化に挑戦してみた。Macの場合にはpy2appを用いたが、今回はcx_Freezeを用いたところ、これが結構ハマったので備忘録として書いておこうと思う。途中で起きたトラブルも全部書いているため、かなり長い話になるが御容赦。
0. cx_Freezeとは
cx_Freeze は、Pythonスクリプトを Pythonなしでも単独で動作する実行可能モジュール(コマンド、
WindowsのEXEファイル、Mac OSアプリケーション等)に変換するツールだ。同種のツールとして py2app(Mac用)、
py2exe(Windows用)があるが、cx_Freeze はクロスプラットフォームなのが特徴。
また、py2exe は Python 3.4 までしか対応してなく、Python 3.5 では動作しないが、cx_Freeze では最新版(現時点では Version 5.0)で Python 3.5 に対応している。
cx_Freeze Version 5.0 は最近(2016/11/15)リリースされたばかりなので、
開発が続いていることがわかるが、py2exe はpy2exe 0.9.2.2が
2014年にリリースされたきり開発が止まっているようだ。ちなみにpy2exeの公式ホームページからはpython 2系の物へのリンクしか張られていない。python 3系の最新版は PyPI にある Version 0.9.2.2 なので、騙されないように。
1. 公式ホームページURL
cx_Freeze:<http://cx-freeze.sourceforge.net/>
2. ダウンロード、インストール方法
インストールには以下に示す3つの方法がある。
- pipを使用する
- Windows版のインストーラを使用する
- ソースからビルドしてインストールする
インストーラ、またはソースのtarボールは公式ホームページからダウンロードできる。
2.1 pipを使用してインストールする
Windows、Mac用の Pythonをインストールすると、一緒に pip というPython のパッケージ管理ユーティリティがインストールされている。(もしインストールされていなかったら、再インストールして Modify か、カスタムインストールで pipを選択するとインストールされるはず。)
pip が使えてインターネットに接続できる環境であれば、
とするとインストールできる。これが最も簡単な方法だと思われ、cx_Freeze のホームページでもインストール方法の一番最初に書かれている。
メインマシンの Mac Pro には、この方法でインストールしている。
$ sudo pip3 install --upgrade pip ※pip3を最新版にアップデート
$ pip3 install cx_Freeze
Collecting cx_Freeze
Downloading cx_Freeze-5.0.tar.gz (59kB)
Installing collected packages: cx-Freeze
Running setup.py install for cx-Freeze ... done
Successfully installed cx-Freeze-5.0
$ pip3 list | grep cx-Freeze
cx-Freeze (5.0)
|
2.2 Windows版のインストーラを用いる
PyPI という Pythonのパッケージアーカイブ "the Python Package Index" からインストーラをダウンロードする。
公式ホームページから PyPI の cx_Freeze パッケージへのリンクが張られているので、
「download directly from PyPI.」から、使用している Python のバージョンと 32bit/64bit に合ったEXE形式のインストーラ
をダウンロードする。インストール方法は、ダウンロードしたインストーラをダブルクリックして実行するだけ。
Wheeel(.whl)ファイルはPythonパッケージの配布形式の一つで、pipコマンドを使用してインストールできる(らしい。と言うのも、実際にやってないので良く分ってない。)
コマンドプロンプト(DOS窓)を開いて
C:¥> pip install cx_Freeze-5.0-cp35-cp35m-win32.whl
|
のようにしてインストールするそうだ。EXE形式のインストーラよりもサイズが小さくなるのでダウンロードに時間がかからないとか、インターネットに接続できない環境に持ち込む場合などに良く使用されるらしい。とは言え、依存関係があるパッケージがあると、pip がインターネット経由で持ってこようとするらしいのだが。
2.3 ソースからビルドしてインストールする
インストーラが提供されてなく、何が何でもソースから自力でビルド、インストールすることに悦びを感じるような人の場合は、ソースからインストールすることもできる。自分の場合は、CPUが Power PC G4/G5 という古〜い Macintosh には Python 3.5.2 をソースからビルドしてインストールしていることもあり、cx_Freeze もソースからインストールしてみた。
1) ソースのダウンロード
「download directly from PyPI.」から "cx_Freeze-5.0.tar.gz" をダウンロードする。
2) ビルド&インストール
$ tar xzf cx_Freeze-5.0.tar.gz
$ cd cx_Freeze-5.0
$ python3 setup.py build
running build
running build_py
creating build
creating build/lib.macosx-10.4-ppc-3.5
creating build/lib.macosx-10.4-ppc-3.5/cx_Freeze
〜略〜
running build_scripts
creating build/scripts-3.5
copying and adjusting cxfreeze -> build/scripts-3.5
copying and adjusting cxfreeze-quickstart -> build/scripts-3.5
changing mode of build/scripts-3.5/cxfreeze from 644 to 755
changing mode of build/scripts-3.5/cxfreeze-quickstart from 644 to 755
$ sudo python3 setup.py install
Password:
running install
running bdist_egg
running egg_info
creating cx_Freeze.egg-info
writing top-level names to cx_Freeze.egg-info/top_level.txt
writing dependency_links to cx_Freeze.egg-info/dependency_links.txt
writing cx_Freeze.egg-info/PKG-INFO
〜略〜
Installing cxfreeze script to /Library/Frameworks/Python.framework/Versions/3.5/bin
Installing cxfreeze-quickstart script to /Library/Frameworks/Python.framework/Versions/3.5/bin
Installed /Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/cx_Freeze-5.0-py3.5-macosx-10.4-ppc.egg
Processing dependencies for cx-Freeze==5.0
Finished processing dependencies for cx-Freeze==5.0
|
3. cx_Freeze を使ってみる
使い方は公式ホームページの Documentation
に纏められているので、これを参照しながらやってみる。
cx_Freeze は複数のプラットフォームをサポートしているので、大まかな方法はどのプラットフォームでも大差ない。そのため、以後は最も手近な Mac Pro上で作業している。OSは macOS 10.12.1 Sierra、Python は Version 3.5.2という環境だ。
1) サンプルプログラム
お決まりの「Hello World!」プログラムを用意。これを"main.py" というファイル名で保存する。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
def main():
print('Hello World!')
if __name__ == '__main__':
main()
|
2) setupスクリプトを作る
cx_Freeze を使うためには、distutils setup script を作成する必要がある。このスクリプトのファイル名には習わしとして"setup.py" が使われるが、必ず"setup.py" でなければいけないということはない。
"setup.py" というファイル名のスクリプトは py2app で作成してしまったので、ここでは名前を"cxFreeze_setup.py" としよう。
import sys
from cx_Freeze import setup, Executable
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
setup( name = 'sample',
version = '1.0',
description = 'sample application',
executables = [Executable('main.py', base=base)])
|
実は上のスクリプトファイルは少しだけ冗長だ。5行目、6行目の if文で base を設定しなおす処理があるが、これは Windowsの GUIアプリの場合だけに必要になる。逆に Windows のコンソールアプリ(コマンド)の場合は base を Win32GUI にしてはいけない。サンプルプログラムは GUIアプリではなく、Windows の場合はコマンドプロンプト(DOS窓)上で動くものなので、Windows の場合は if文をコメントアウト(行先頭に「#」を付けるとコメント行になる)してほしい。(if文を削除しないで。これは後に必要になってくるのだから。)
base が None の場合はデフォルト設定になり、デフォルトはコンソールアプリだ。
Mac や Linux の場合は、このままで問題ない。
3) 実行可能モジュール(コマンド)化してみる
実行可能モジュール化するのは簡単だ。以下のようにコマンドを叩けば、"build"ディレクトリが作られ、その下に "exe.〜" という名前のディレクトリができる。
$ python3 cxFreeze_setup.py build
running build
running build_exe
creating directory build/exe.macosx-10.6-intel-3.5
〜略〜
|
この "exe.〜" 内(上の例では"exe.macosx-10.6-intel-3.5" )に実行可能モジュールが作られるが、それ以外にも多くのファイルが置かれている。これらは実行時に必要とされる物なのだが、とりあえず main という名前の実行可能モジュール(コマンド)が出来ていればOK。
無事に出来ていたら、早速実行してみよう。
$ cd build/exe.macosx-10.6-intel-3.5/
$ ./main
$
|
何も出力されず、すぐにプロンプトに返ってきてしまう。動かん。。。
それならばとコマンドではなく、Macアプリを作らせてみる。Macアプリにするのも簡単だ。build の代わりに bdist_mac と指定するだけで良い。
$ cd ../..
$ rm -rf build
$ python3 cxFreeze_setup.py bdist_mac
running bdist_mac
running build
running build_exe
creating directory build/exe.macosx-10.6-intel-3.5
〜略〜
$ ls -l build
total 0
drwxr-xr-x 6 nai admin 204 11 18 19:29 exe.macosx-10.6-intel-3.5
drwxr-xr-x 3 nai admin 102 11 18 19:29 sample-1.0.app
|
今度は buildディレクトリ配下に sample-1.0.app というアプリが出来ている。しかし、これを Finderでダブルクリックしても何も起きない。やはりダメっぽい。
4) setupスクリプトが悪い?
実行に必要なパッケージなどが取り込まれていないのだろうか?少しsetupスクリプトを修正してみる。
import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {'packages': ['os', 'sys'], 'excludes': ['tkinter']}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
setup( name = 'sample',
version = '1.0',
description = 'sample application',
options = {'build_exe': build_exe_options},
executables = [Executable('main.py', base=base)])
|
$ python3 cxFreeze_setup.py build
$ cd build/exe.macosx-10.6-intel-3.5/
$ ./main
$
|
packagesリストで'os', 'sys' を取り込むようにしてみた(さすがに'tkinter' は要らないだろうと、excludesリストに指定している)が、やっぱり動かん。。。
4. 動かない原因を調査
動かない原因を調べてみたところ、cx_Freezeで固めて作った実行可能モジュール(コマンド、.appや.exe)は、__name__ が'__main__' ではなくなってしまうことが判明。
そこでサンプルプログラムを以下のように、__name__ を判定している if文を削除し、無条件に main関数を呼び出してみる。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
def main():
print('Hello World!', __name__)
main()
|
setupスクリプトも、より汎用的に修正。
import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
includes = []
include_files = []
packages = ['os', 'sys']
excludes = ['tkinter']
build_exe_options = {'includes': includes,
'include_files': include_files,
'packages': packages,
'excludes': excludes}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
exe = [
Executable('main.py', base=base, targetName='main')
]
setup( name = 'sample',
version = '1.0',
description = 'sample application',
options = {'build_exe': build_exe_options},
executables = exe )
|
これでやってみたところ、
$ python3 cxFreeze_setup.py build
$ cd build/exe.macosx-10.6-intel-3.5/
$ ./main
Hello World! main__main__
$
|
動いた!
この動作結果から、__name__ は__main__ の前に実行可能モジュール(コマンド)名が付加された名前になることが分る。(上の場合は'main' + '__main__' になっている。)
また、コマンド名をmainから変えてしまうと、動かなくなってしまうようだ。
$ mv main sample
$ ./sample
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/cx_Freeze/initscripts/__startup__.py", line 12, in <module>
__import__(name + "__init__")
ImportError: No module named 'sample__init__'
$
|
|