みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

Pythonスクリプトをcx_Freezeでexeにする (Mac OS X編)

完全な情報をこちらに掲載しました.以下の文章は古い情報です.


 

cx_Freezeとは、Pythonスクリプトを、exeやappなどの実行ファイルに変換するためのソフトウエアです。cx_Freeze(http://cx-freeze.sourceforge.net/index.html)

この記事はみーの備忘録なので、過度な期待はしないようにお願いします。

cx_Freezeのインストール方法

みーが使用したソフトウエアのバージョンは以下の通りですが、これ以外のバージョンでも、適宜読み替えることで同様の手順でインストールが可能と想われます。

  • Mac OS X 10.8.2
  • Python 3.2
  • cx_Freeze 4.3.1

はじめに、XCodeにて Preference > Download > Components > Command Line Tools を選択、インストールします。

次にcx_Freezeのソースコードをダウンロードします。(http://cx-freeze.sourceforge.net/のSource code only (.tar.gz) )

この状態でビルドすると、以下のようなエラーが発生します。

cd cx_Freezeのダウンロードフォルダ
python setup.py build
running build
running build_py
running build_ext
gcc-4.2 not found, using clang instead
building "cx_Freeze.util" extension
Compiling with an SDK that doesn"t seem to exist: /Developer/SDKs/MacOSX10.6.sdk
Please check your Xcode installation
clang -fno-strict-aliasing -fno-common -dynamic -DNDEBUG -g -O3 -isysroot /Developer/SDKs/MacOSX10.6.sdk -arch i386 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk -I/Library/Frameworks/Python.framework/Versions/3.2/include/python3.2m -c source/util.c -o build/temp.macosx-10.6-intel-3.2/source/util.o
In file included from source/util.c:6:
/Library/Frameworks/Python.framework/Versions/3.2/include/python3.2m/Python.h:25:10: fatal error: 
      "stdio.h" file not found
#include 
         ^
1 error generated.
error: command "clang" failed with exit status 1

stdio.hが見つからないってあなた、、、基本中の基本のファイルじゃないですか。ほう。/Developer/SDKs/MacOSX10.6.sdkが見つからないとな。あれこれネットで情報を探すも、解決策がないので、ああもう、なんかめんどくさいなあ。えいや!ということで、/Developer/SDKs/MacOSX10.6.sdkの代替としてMacOSX10.8.sdkを適当にぶっこんでビルドしてみたらすんなり完了したので、ここに詳細を書き残します。

XCodeは以前/Developerフォルダにあれこれと保存していたようなのですが、Mountain Lionになってから/Applicationsに全部統一する流れになったようで、/Applications/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs の中の MacOSX10.8.sdk を /Developer/SDKs に MacOSX10.6.sdk とリネームしてコピーすればうまく動作するようです。/Applicationsへの統一は見た目スッキリですが、開発者としてはパスがすごく面倒なことになっているのでもうちょっと何とかしてほしいですねえ。

これでビルドが完了です。インストールします。 

python setup.py build

やっほーできたぜ〜。というテンションのまま、自分のプロジェクトのpython setup.py buildを実行すると、エラーが発生します。

cd ~/自分のプロジェクト
python setup.py build
copying /Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/site-packages/PyQt4/QtCore.so -> build/exe.macosx-10.6-intel-3.2/PyQt4.QtCore.so
copying QtCore.framework/Versions/4/QtCore -> build/exe.macosx-10.6-intel-3.2/QtCore
error: QtCore.framework/Versions/4/QtCore: No such file or directory

QtCore.framework/Versions/4/QtCore が存在しないようです。ってこれ相対パスの指定をミスっているだけやん。というわけで、/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/site-packages/cx_Freeze の freezer.pyの _CopyFile()を以下のように赤文字を追加し変更します。ただしこれはものすごく応急処置な修正で、Python + PyQtしか考慮していません。

    def _CopyFile(self, source, target, copyDependentFiles = False,
            includeMode = False):
        if not source.startswith("/"):
            if source.startswith("lib"):
                source = "/usr/lib/"+source
            else :
                source = "/Library/Frameworks/"+source
        normalizedSource = os.path.normcase(os.path.normpath(source))
        normalizedTarget = os.path.normcase(os.path.normpath(target))
        if normalizedTarget in self.filesCopied:
            return
        if normalizedSource == normalizedTarget:
            return
        self._RemoveFile(target)
        targetDir = os.path.dirname(target)
        self._CreateDirectory(targetDir)
        if not self.silent:
            sys.stdout.write("copying %s -> %s\n" % (source, target))
        shutil.copyfile(source, target)
        shutil.copystat(source, target)
        if includeMode:
            shutil.copymode(source, target)
        self.filesCopied[normalizedTarget] = None
        if copyDependentFiles:
            for source in self._GetDependentFiles(source):
                target = os.path.join(targetDir, os.path.basename(source))
                self._CopyFile(source, target, copyDependentFiles)

よくよく調べてみると、freezer.pyのfreeze()では、_CopyFile()から_GetDependentFiles()を呼び出しているようで、

command = "otool -L "%s"" % path

がos.popen()されるようです。ターミナルでotool -L を実行するとわかるのですが、絶対パスになっていたりファイル名だけだったりしているようで、このせいでファイルが見つからないエラーが出ているようです。

これでpython setup.py buildが動作するようになりました。

cx_Freezeの使い方

setup.pyを作成

import sys
from cx_Freeze import setup, Executable

includes = ["PyQt4.QtCore","PyQt4.QtGui","re",] 

base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(  name = "",
        version = "",
        options = {"build_exe": {"includes": includes}},
        executables = [Executable("main.py", base=base)])
  • includes で、 含めるべきモジュールを指定します。PyQtの場合は、PyQt4.QtCore のように指定します。
  • setup関数で、buildが実行されます。nameとversionを指定し、main.pyを実行するように指定します。

build を実行

buildするのは簡単です。cdコマンドでsetup.pyがあるディレクトリに移動して、 

python setup.py build

と実行すればよいです。build/exe.macosx-10.6-intel-3.2/??にビルドされます。でも、実行すると

SystemError: initialization of sip raised unreported exception

というエラーが出てしまいます。困った、、、解決法が見つかれば更新します。(2013.1.23)

bdist_macを実行

以下のコマンドで、.appファイルが/buildに作成されます。

python3.2 setup.py bdist_mac

もちろん、上記のシステムエラーが発生します。

なんか、、うまくいきませんね。