Pyinstaller可以将Python脚本打包成独立的可执行程序,支持多平台Windows,Linux和Mac OS X等。
用pip安装pyinstaller:
pip install pyinstaller
使用方法非常简单:
pyinstaller myscript.py
这会产生两个文件夹,build和dist,build保存着打包前的配置文件和完成后的结果信息,最终的打包程序位于dist中。
常用参数:
-h, –help
查看帮助
-y, –noconfirm
不询问
-n NAME, –name NAME
打包后文件名
–distpath DIR
指定打包位置
–add-data <SRC;DEST or SRC:DEST>
添加数据文件
–add-binary <SRC;DEST or SRC:DEST>
添加二进制文件
–hidden-import MODULENAME, –hiddenimport MODULENAME
打包隐藏的模块
-w, –windowed, –noconsole
有GUI的程序可以不提供命令行界面
–version-file FILE
加入版本文件
更多用法请见Using PyInstaller。
Pyinstaller在打包时会将尽可能地收集代码相关的依赖,包括python解释器,最后得到的软件包,其他用户无需安装其他依赖也能使用。但这同时也意味着打包出来的结果很可能非常庞大。比如我一个不到30k的脚本,最后打包出来的软件足足120m以上。
虽然Pyinstaller会尽力将所有的程序依赖都收集起来,但是难免会有遗漏的情况。在打包过程中经常可以看见报错信息,说找不到某某依赖,有时候这不会影响最后的运行结果,但有时候也会导致打包好的exe运行失败。
下面介绍几个常见bugs和解决方法。
python脚本可以运行,但是exe程序闪退,这时需要用命令行运行exe,直接在命令行输入完整路径,然后就会打印错误信息。
接下来按照这个方案一步步排查错误,Make sure everything is packaged correctly。
ImportError: DLL load failed: 找不到指定的模块
遇到这样的问题指的是缺少dll文件,但是这个错误往往会让人很困扰,因为它不会提示具体缺少什么dll文件。想要正确的把缺少的dll找到,就得查看打包运行时的错误警告,除了在shell查看以外,还可以在/build/name/warnname.txt中查看。
找出缺失的dlls以后,可以在打包时加上–add-binary选项:
pyinstaller --add-binary '/path/to/some.dll:.' myscript.py
或者在.spec文件中添加更多的dlls:
a = Analysis(...
binaries=[('/path/to/some.dll', '.'), ... ],
...)
或者在打包完成后,直接将dll文件复制到软件包即可。
例如,我在打包需要scipy的项目时遇到了这个bug,从错误信息中发现缺少了很多dlls,直接搜索其相应的名字,发现其实都在这个文件夹中,\Python36\Lib\site-packages\scipy\extra-dll。然后将这些dlls全部复制到打包程序文件夹中就行了。关于这个bug还可以参考pyinstaller/issues/3235。
ModuleNotFoundError: No module named ‘xxxxxx’
有时pyinstaller的分析器认为它已经找到了所有需要的模块,但实际却没有,这往往是存在隐藏的导入(hidden import)。当代码使用__import__或exec或eval等函数时,可能会出现隐藏的导入。当扩展模块使用python/c API进行导入时,也可能出现隐藏的导入。当这种情况发生时,分析器无法侦测,也不会有任何警告,只有在运行时才会出现错误。
解决办法是在打包时加上--hidden-import=some.module
选项,或者在.spec文件中指定:
a = Analysis(...
hiddenimports=['some.module', ... ],
...)
例如我在打包完成后,运行exe文件时遇到了错误:
ModuleNotFoundError: No module named 'scipy._lib.messagestream'
所以要重新打包并加上隐藏的导入:
pyinstaller --hidden-import=scipy._lib.messagestream myscript.py