cx_Freezeでexeを作る

# hoge.py
print("hoge hoge hoge")
# setup.py
from cx_Freeze import setup, Executable
setup(
    name='hoge',
    options = dict(
        build_exe = dict(
            create_shared_zip = False,      ### library.zip ではなく hoge.zip を生成する
            append_script_to_exe = True,    ### .zip を .exe に含める
            packages = [],
            excludes = ['email', 'unittest', 'doctest', 'pyreadline', 'pdb', 'bdb', 'dummy_threading'],
            includes = [],
            ),
        ),
    executables = [
        Executable(
            'hoge.py',
            base = 'Console',
            )
        ],
    )
$ python setup.py build
$ tree .
.
|-- build
|   `-- exe.win32-3.3
|       |-- _bz2.pyd
|       |-- hoge.exe
|       |-- python33.dll
|       `-- unicodedata.pyd
|-- hoge.py
`-- setup.py

2 directories, 6 files

$ build/exe.win32-3.3/hoge
hoge hoge hoge
  • create_shared_zip=Falseとappend_script_to_exe=Trueでlibrary.zipを.exeに含めることが出来た。
  • append_script_to_exe=False(初期値)にするとhoge.zipが作られる。
  • py2exeみたいに.pydや.dllも.exeに含めるにはどうしたらいいんだろう?

gtags.el/anything-gtags.elのバッファが残りすぎて気になる

;; gtags-find-tag で検索時に以前のバッファが残らないようにクリアする
(setq gtags-select-buffer-single t)

;; anything-gtagsでgtagsの候補バッファを奪うときに違う名前でバッファを作りまくるのでどんどん残る。
;; gtags-select-buffer-singleを真似して事前に以前のバッファをクリアする
(defadvice ag-hijack-gtags-select-mode (before before-ag-hijack-gtags-select-mode activate)
  "clear buffers before hijack"
  (setq anything-buffers nil) ;; resume用の変数もクリア。この後に来るanythingの候補が入るので直前のresumeはできる。
  (let (now-buffer-list now-buffer)
    (setq now-buffer-list (buffer-list))
    (while now-buffer-list
      (setq now-buffer (car now-buffer-list))
      (if (string-match "*anything gtags*" (buffer-name now-buffer))
          (kill-buffer now-buffer))
      (setq now-buffer-list (cdr now-buffer-list)))))

前から気になってた。これでまたemacs生活が快適になった。
ところで最近anythingからフォークしたhelmというのを見かけるのですが、どう違うの?

呼び出し履歴を取得する

環境

  • Windows7 SP1
  • VisualStudio2012 SP1
  • SCons-2.3.0

やってみた

$ make run-x86
make run TARGET_ARCH=x86
make[1]: ディレクトリ `/home/github/shive/blogpost/20130918-stacktrace' に入ります
scons -Qj4 TARGET_ARCH=x86 build
cl /Fo.obj\x86\main.obj /c .obj\x86\main.cpp /TP /nologo /Zi /MD /EHsc /GR- /Od /Fdmain.pdb /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_MBCS /D_WINDOWS /DWIN32 /DNDEBUG /DVC_EXTERN /DWIN32_LEAN_AND_MEAN /DNOMINMAX /DSTRICT
main.cpp
link /nologo /DEBUG /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /OUT:.obj\x86\main.exe imagehlp.lib .obj\x86\main.obj
scons -Qj4 TARGET_ARCH=x86 run
".obj\x86\main.exe"
---- BEGIN BACKTRACE ----
1 : 0x011c1638 : main : mycode::foo : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(112) :         backtrace();
2 : 0x011c1648 : main : mycode::bar : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(116) :         foo();
3 : 0x011c1658 : main : mycode::baz : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(120) :         bar();
4 : 0x011c1668 : main : mycode::hoge : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(124) :         baz();
5 : 0x011c16a8 : main : main : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(131) :         mycode::hoge();
6 : 0x011c3bde : main : __tmainCRTStartup : f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(536) : ?
7 : 0x75d3336a : kernel32 : BaseThreadInitThunk : ?(0) : ?
---- END BACKTRACE ----

make[1]: ディレクトリ `/home/github/shive/blogpost/20130918-stacktrace' から出ます

重要なとこ

  • RtlCaptureStackBackTrace : 関数呼び出しのリターンアドレス一覧を取得
  • SymGetModuleInfo : モジュール名の取得
  • SymGetSymFromAddr : 関数アドレスからシンボル名の取得
  • SymGetLineFromAddr : 関数アドレスからファイル名と行番号の取得

あとがき

  • 最適化を入れた場合当然関数呼び出しが一部無くなるが、無くなったなりの履歴でちゃんと動く
  • x64でもちゃんと動いた
  • 当然だがデバッグシンボルありでビルドしないとちゃんと情報が取れない
  • 呼び出した行番号ではなく戻る行番号を取得して表示しているので微妙にズレる
  • この機能を使えば呼び出し元がどこそこだったらこう動く!とか気持ち悪いこともできるなぁ

参考

sconsの便利っぷり

動作環境

何やってるのか?

  • ディレクトリを指定したらソースファイルを検索して.exeをコンパイル&実行
  • VisualStudio2008/2010/2012でビルド&プロジェクトファイル作成
  • MinGWでビルド

どう使うのか?

  • scons呼び出しはMakefileにまとめました。
$ cd scons-test/test001-forwin/

$ ls -1
main.cpp
Makefile
SConstruct
stdafx.cpp
stdafx.h
### 元になるファイルはこの5つだけ

$ make -s msvcproj-all
Adding 'test-win32vc9 - Debug|win32' to 'test-win32vc9.vcproj'
Adding 'test-win32vc9 - Release|win32' to 'test-win32vc9.vcproj'
Adding 'test-x64vc9 - Debug|x64' to 'test-x64vc9.vcproj'
Adding 'test-x64vc9 - Release|x64' to 'test-x64vc9.vcproj'
Adding 'test-win32vc10 - Debug|win32' to 'test-win32vc10.vcxproj'
Adding 'test-win32vc10 - Release|win32' to 'test-win32vc10.vcxproj'
Adding 'test-x64vc10 - Debug|x64' to 'test-x64vc10.vcxproj'
Adding 'test-x64vc10 - Release|x64' to 'test-x64vc10.vcxproj'
Adding 'test-win32vc11 - Debug|win32' to 'test-win32vc11.vcxproj'
Adding 'test-win32vc11 - Release|win32' to 'test-win32vc11.vcxproj'
Adding 'test-x64vc11 - Debug|x64' to 'test-x64vc11.vcxproj'
Adding 'test-x64vc11 - Release|x64' to 'test-x64vc11.vcxproj'
### VisualStudioの各バージョン向けのプロジェクトファイル出力
### ちなみに実は2010と2012は同じ内容
### ちょっと修正すればx86/x64はひとつに統合できる

$ make -s build-all
(省略)
### 全ターゲットをビルドしてみます

$ ls -1
bin/
main.cpp
Makefile
obj/
SConstruct
stdafx.cpp
stdafx.h
test-win32vc10.vcxproj
test-win32vc10.vcxproj.filters
test-win32vc11.vcxproj
test-win32vc11.vcxproj.filters
test-win32vc9.vcproj
test-x64vc10.vcxproj
test-x64vc10.vcxproj.filters
test-x64vc11.vcxproj
test-x64vc11.vcxproj.filters
test-x64vc9.vcproj

$ ls -1 bin/
test-mingw32-debug.exe*
test-mingw32-release.exe*
test-win32vc10-debug.exe*
test-win32vc10-debug.map
test-win32vc10-debug.pdb
test-win32vc10-release.exe*
test-win32vc10-release.map
test-win32vc10-release.pdb
test-win32vc11-debug.exe*
test-win32vc11-debug.map
test-win32vc11-debug.pdb
test-win32vc11-release.exe*
test-win32vc11-release.map
test-win32vc11-release.pdb
test-win32vc9-debug.exe*
test-win32vc9-debug.exe.manifest
test-win32vc9-debug.map
test-win32vc9-debug.pdb
test-win32vc9-release.exe*
test-win32vc9-release.exe.manifest
test-win32vc9-release.map
test-win32vc9-release.pdb
test-x64vc10-debug.exe*
test-x64vc10-debug.map
test-x64vc10-debug.pdb
test-x64vc10-release.exe*
test-x64vc10-release.map
test-x64vc10-release.pdb
test-x64vc11-debug.exe*
test-x64vc11-debug.map
test-x64vc11-debug.pdb
test-x64vc11-release.exe*
test-x64vc11-release.map
test-x64vc11-release.pdb
test-x64vc9-debug.exe*
test-x64vc9-debug.exe.manifest
test-x64vc9-debug.map
test-x64vc9-debug.pdb
test-x64vc9-release.exe*
test-x64vc9-release.exe.manifest
test-x64vc9-release.map
test-x64vc9-release.pdb

$ ls -1 obj/
test-mingw32-debug/
test-mingw32-release/
test-win32vc10-debug/
test-win32vc10-release/
test-win32vc11-debug/
test-win32vc11-release/
test-win32vc9-debug/
test-win32vc9-release/
test-x64vc10-debug/
test-x64vc10-release/
test-x64vc11-debug/
test-x64vc11-release/
test-x64vc9-debug/
test-x64vc9-release/

$ bin/test-mingw32-debug
hoge hoge hoge.

$ bin/test-x64vc11-release
HOGE HOGE HOGE.

$ make -s run-debug msvc_version=10.0 msvc_arch=x86
hoge hoge hoge.

$ make -s run-debug msvc_version=10.0 msvc_arch=x86 toolset=mingw
hoge hoge hoge.

解説

  • ようするに こんな風 にSConstructを書けばいろんなコンパイラに対応したビルド・実行・VisualStudioIDE用のファイル出力などが出来ますよ、というサンプルです。
  • configureみたいなこともできるようですが、勉強不足で試してません。
  • CMakeと違ってプロジェクトファイルに入るファイルパスが相対(に出来る)のがナイス。リポジトリにアップして共有できます。
  • 細かい解説は…、億劫なのでドキュメントを読んでください。

何回もビルドされるVisualStudio2010のバグ

VisualStudio2010 でWin32/C++プロジェクトを弄っていたら、あるタイミングから何回ビルドしてもビルドしますか?っと聞かれるようになった。何も編集していないので意味がわからん。

http://social.msdn.microsoft.com/Forums/vstudio/ja-JP/ccac59d3-9df8-477f-983c-3dce4d344f0c/vs2010-c

↑これかなとも思って作りなおしたりいろいろしてたけど、原因がわかった。

hoge.cpp をビルドしない設定にしたままプロジェクトに追加してあったんだけど、うっかりファイルを削除してしまっていた。この状態だとビルドは通るけど、更新チェックに引っかかるらしく何度もビルドされていまう。プロジェクトからエントリを削除すれば現象は改善した!数時間ハマった。しょうもない…。