最近つくったコンソールゲーム、『一石二鳥』を実行ファイル化したときの話。相変わらず趣味レベルなのに断定口調で書きまくるが、確証はないのであしからず。これまでもcx_freezeを使ってpythonスクリプトをexe化したことはあったんだけど、今回のスクリプトはちょっと以前のと作りが違ったわけですよ。具体的には次のような部分。

以前の

  • GUIにpygameやtkinterなどpythonのGUIモジュールを使う
  • スクリプト内で使うモジュールは標準モジュールか、同階層に存在する自作モジュール

今回の

  • GUIにシェルを使う
  • スクリプト内で標準モジュールと自作モジュールを併用しており、それらは階層化したディレクトリに偏在している

同じところ

  • 外部の静的ファイル(画像とかDBファイルとか)を使っている

それにともないcx_freezeの実行スクリプトを書き換える必要があった。

import sys, os
from cx_Freeze import setup, Executable

# ==============================
# 全ディレクトリをsys.pathに登録する ここから…
# ==============================
def makeDirs():
    dirs = []
    dirs.append(os.getcwd())
    for directory in dirs:
        files = os.listdir(directory)
        for f in files:
            path = directory + "¥¥" + f
            if os.path.isdir(path):
                dirs.append(path)
    return dirs

def makePaths():
    dirs = makeDirs()
    for directory in dirs:
        if directory in sys.path:
            pass
        else:
            sys.path.insert(0, directory)

makePaths()
# ==============================
# …ここまで
# ==============================

exe = Executable(
    script = "KillBirds.py",   # ファイル名
    icon   = "python.ico",     # アイコン
    base   = "Console")        # デフォルトが"Console"なんでNoneでもよい

setup(
    name        = 'MyPython',
    version     = '1.0',
    description = 'application',
    executables = [exe],
    options     = {
        "build_exe":{"includes":[], "excludes":[], "packages":[]}
        },
    ) 

GUIに関連するのが base = "Console" の部分で、自作モジュールの偏在に関連するのが「ここから…」「…ここまで」の部分だ。前者はともかく後者で何をやっているのかというと、俺が命名するところの「奥義:相対インポートなんていーらない」ですね。すなわち、別階層の自作モジュールをインポートするためには本来トップレベルファイルから相対インポートをするのだが、相対パスを毎度書くのが面倒くさいので、モジュール読み込みディレクトリの候補が格納されているsys.pathに、使用予定の全ディレクトリパスを書き加えちまおうという技である。いやー、これは相対インポートレポートの調査をしているときに発見したものだったが、やっぱり自分で発見した技が実際に役立つというのは嬉しいものだ。

けど実はこの処理って、スクリプトにもう書いてあるものなんだよなー。どうしてcx_freeze実行ファイルのほうにも必要なのかはわからん。まあともかく、これを実行すればexeファイルの入ったビルドフォルダが出来上がる。もちろんそのフォルダには静的ファイルは入っていないんで、それを加えたら完成だ。

さて今回のスクリプトはすでに述べたようコンソール上で動作するゲームなので、完成したexeファイルを実行するとコマンドプロンプトが立ち上がる(いや正しくいうと base = "Console" って指定したからなのだけども)。しかし、プログラムがエラーを吐くとコマンドプロンプトが消えちまう。不便だ。それはこの形で立ち上がったコマンドプロンプトには /C なるオプションが設定されているからである。これは指定されたコマンドを実行したあと、コマンドプロンプトを終了するというオプションで、そりゃ、エラーが起きてプログラムが終了したら消えますわって話だ。まあ、仲間に遊ばせるとき内部エラーが見えたら恥ずかしいのでさっさと消えてもらった方がいい、というなら別なんだけど、そんなことはないので、プログラム終了後もコマンドプロンプトが消えないオプション /K を設定する方法をとる。他にもやりようはあると思うんだけど、今回俺が見つけた方法は以下。

  • 作ったexeファイル(/Cで立ち上がるファイル)のショートカットを作る
  • そのショートカットのプロパティから、リンク先の内容を cmd /K exeファイルの名前 という風に書き換える e- xeファイルの実行はショートカットのほうから行う

こうすると「まず/Kでcmdが立ち上がり」「その中でexeファイルが/Cで立ち上がる」ということになる。これならexeファイルの/Cのcmdが終了しちまっても、それを実行している/Kのcmdは終了しないのでエラー内容を見ることができるって寸法だ。うん、まあ俺の環境ではそれでオーケイだった。

これまでのexe化関連記事をまとめとく。

以下の記事からリンクされています