之前寫了一個小工具,將excel配置表轉換為json、xml、lua等配置文件。最近在學習egret,正好需要轉換配置文件,剛好就用上了。然而當我想把工具拷到工作目錄時,就發愁了。之前我為了方便擴展,把程序拆分得太細:
xzc@xzc-HP-ProBook-4446s:~/Documents/code/github/py_exceltools$ ls -lh 總用量 80K drwxrwxr-x 2 xzc xzc 4.0K 7月 27 23:03 bin drwxrwxr-x 2 xzc xzc 4.0K 7月 27 23:03 client -rw-rw-r-- 1 xzc xzc 7.7K 7月 27 23:03 decoder.py -rw-rw-r-- 1 xzc xzc 893 7月 27 23:03 error.py -rw-rw-r-- 1 xzc xzc 16K 7月 27 23:03 example.xlsx -rw-rw-r-- 1 xzc xzc 131 7月 27 23:03 lancher.bat -rw-rw-r-- 1 xzc xzc 127 7月 27 23:03 lancher.sh -rw-rw-r-- 1 xzc xzc 3.9K 7月 27 23:03 loader.py -rw-rw-r-- 1 xzc xzc 705 7月 27 23:03 loader.spec -rw-rw-r-- 1 xzc xzc 2.4K 7月 27 23:03 README.md drwxrwxr-x 2 xzc xzc 4.0K 7月 27 23:03 server -rw-rw-r-- 1 xzc xzc 4.0K 7月 27 23:03 writer_json.py -rw-rw-r-- 1 xzc xzc 7.2K 7月 27 23:03 writer_lua.py -rw-rw-r-- 1 xzc xzc 5.6K 7月 27 23:03 writer_xml.py
如此多的文件,放到工作目錄不太好組織,也容易與項目的源代碼混在一起。畢竟我用的vs code分不清哪些才是工程內文件。何況以后還要給策划用,還得裝python和openpyxl庫,部署比較麻煩。於是想嘗試一下把python腳本打包為一個exe文件。
google了一下,常用的工具不外乎pyinstaller和py2exe。兩者的功能都差不多,但是發現pyinstaller有一個參數 --onefile,即腳本都打包成單個可運行文件,這不正是我要的么。於是下載安裝來嘗試一下:
py_exceltools$pip install pyinstaller py_exceltools$pyinstaller -F loader.py ... tuple index out of range
安裝過程很順利,但是打包時卻報了個錯("tuple index out of range")。google一下"pyinstaller tuple index out of range",在github中發現是pyinstaller3.2.1不兼容python3.6.1。但是看看issue的回復,在dev版本是修復了。於是想試一下開發版本,不過看了一眼README,發現OS X、Linux、Win三個平台的CI都是failing:
想想還是算了吧,免得折騰半天又不能用。直接把本機的python從3.6.1降為3.5,再從新安裝pyinstaller,運行"pyinstaller -F loader.py"打包,一切順利,在dist目錄下生成了一個loader.exe。試運行下loader.exe,結果卻是這樣的:
Traceback (most recent call last): File "loader.py", line 96, in <module> options.timeout,options.suffix,options.srv_writer,options.clt_writer ) File "loader.py", line 25, in __init__ self.srv_writer = importlib.import_module( "writer_" + srv_writer ) File "importlib\__init__.py", line 126, in import_module File "<frozen importlib._bootstrap>", line 986, in _gcd_import File "<frozen importlib._bootstrap>", line 969, in _find_and_load File "<frozen importlib._bootstrap>", line 956, in _find_and_load_unlocked ImportError: No module named 'writer_lua' Failed to execute script loader 請按任意鍵繼續. . .
缺失了模塊writer_lua,這是我自己寫的一個轉換為Lua配置的模塊。pyinstaller本來有分析腳本依賴的模塊的,但是我這個程序是根據運行時傳入的參數動態加載模塊的,因為我並不能預先知道用戶要把excel轉換為什么類型的文件。全部加載所有模塊,是一個解決方案,但不太合適,因為我本來的寫法是:規定了模塊的接口,新增模塊時,不需要修改我原有的代碼,會自動加載新模塊。再次搜索了一下,居然沒有找到相同的案例。閱讀了下pyinstaller的手冊(https://pythonhosted.org/PyInstaller/spec-files.html),發現可以用spec配置文件來打包各種數據的,比如程序的icon,甚至自定義的一些二進制文件。在http://pythonhosted.org/PyInstaller/when-things-go-wrong.html#listing-hidden-imports和http://pythonhosted.org/PyInstaller/hooks.html#understanding-pyinstaller-hooks中提到可以使用hiddenimports選項來導入隱藏的模塊。
查看了下pyinstaller打包的過程,運行"pyinstaller -F loader.py"時確實在當前目錄下生成了一個loader.spec文件:
# -*- mode: python -*- block_cipher = None a = Analysis(['loader.py'], pathex=['E:\\linux_share\\github\\py_exceltools'], binaries=[], datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='loader', debug=False, strip=False, upx=True, console=True )
在hiddenimports中加入自己動態加載的模塊,變成hiddenimports=['writer_lua','writer_xml','writer_json'],重新打包。注意,重新打包時不要再運行"pyinstaller -F loader.py"了,因為這個指令會重新生成spec文件,把你修改的覆蓋了。直接用"pyinstaller loader.spec"來打包。
加入動態加載的模塊后,整個exe有7M多,運行正常。但是在64bit系統打包出來的程序,是64bit的,不能在32bit下運行。整個工具放在github上:https://github.com/changnet/py_exceltools