使用PyInstaller在Windows系统下打包了一个带有mxnet-gpu的项目,遇到了一些依赖库和静态文件的问题,记录一下使用过程和遇到的问题及解决方法
安装
参考官方教程,直接通过pip命令安装,我使用的是3.6版本的python,直接安装成功。
1 | pip install pyinstaller |
将.py脚本打包为exe
输入命令
1 | pyinstaller main.py |
就可以将main.py
文件打包成一个.exe文件,位于命令执行目录的./dist
文件夹下,同时会在当前目录中生成一个./build
文件夹,存放构建时产生的临时文件,并且创建一个./main.spec
文件,记录了本次打包的一些配置参数
如果希望进行更多样的配置,可以通过参数来控制pyinstaller
的行为。例如通过--distpath
设置输出路径,--onefile
设置生成单文件等
1 | pyinstaller main.py --distpath "./output" # 设置生成目录 |
一些常用的命令参数及帮助说明如下表所示(官网):
参数 | 帮助说明 |
---|---|
-h, —help | show this help message and exit |
-v, —version | Show program version info and exit. |
—distpath DIR | Where to put the bundled app (default: ./dist) |
—workpath WORKPATH | Where to put all the temporary work files, .log, .pyz and etc. (default: ./build) |
-y, —noconfirm | Replace output directory (default: SPECPATH/dist/SPECNAME) without asking for confirmation |
—upx-dir UPX_DIR | Path to UPX utility (default: search the execution path) |
-a, —ascii | Do not include unicode encoding support (default: included if available) |
—clean | Clean PyInstaller cache and remove temporary files before building. |
—log-level LEVEL | Amount of detail in build-time console messages. LEVEL may be one of TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL (default: INFO). |
-D, —onedir | Create a one-folder bundle containing an executable (default) |
-F, —onefile | Create a one-file bundled executable. |
—specpath DIR | Folder to store the generated spec file (default: current directory) |
-n NAME, —name NAME | Name to assign to the bundled app and spec file (default: first script’s basename) |
—add-data |
Additional non-binary files or folders to be added to the executable. The path separator is platform specific, os.pathsep (which is ; on Windows and : on most unix systems) is used. This option can be used multiple times. |
—add-binary |
Additional binary files to be added to the executable. See the --add-data option for more details. This option can be used multiple times. |
利用.spec文件导入静态资源&动态链接库
除了直接对.py文件打包之外,还可以直接根据.spec文件进行打包:
1 | pyinstaller main.spec |
该文件在运行pyinstaller main.py
命令时自动创建
存在一种情况,pyinstaller
并不总能找到你在.py文件中import
的包,例如,mxnet
有CPU版本和GPU版本,GPU版本又是和CUDA对应的,如mxnet-cu101
,在打包时其相关的dll文件并不能被找到并包含到生成的输出路径中,因此执行main.exe
时会报错:
1 | RuntimeError: Cannot find the MXNet library. |
此时需要手动把对应的dll文件加入到输出路径的指定文件夹下,找到所需的dll文件路径为D:\Anaconda\anaconda3\envs\mxnet-gpu\Lib\site-packages\mxnet\libmxnet.dll
(也可以是相对路径),将其单斜杠改为双斜杠,通过--add-binary
参数加入到输出路径下的mxnet
文件夹中:
1 | pyinstaller main.py --add-binary "D:\\Anaconda\\anaconda3\\envs\\mxnet-gpu\\Lib\\site-packages\\mxnet\\libmxnet.dll;.\\mxnet" |
--add-binary
参数可以出现多次,用于应对多个dll文件的情况,就像这样:
1 | pyinstaller main.py --add-binary "D:\\Anaconda\\anaconda3\\envs\\mxnet-gpu\\Lib\\site-packages\\mxnet\\libmxnet.dll;.\\mxnet" --add-binary "SRC;DEST" --add-binary "SRC;DEST" |
这里面libmxnet.dll
如果写成*.dll
,就表示文件夹下的所有.dll
文件;分号隔开的下一项表示拷贝的目标位置,相对于输出的.exe文件所在路径而定。同理,参数--add-data
用于引入静态文件,可以引入一个文件夹、一个文件、一个目录下的文件。
如果不需要引入路径下的所有dll文件,只需要其中几个,导致最终的命令参数过长且不容易保存,那么使用.spec文件进行打包会是一个好的选择。运行一次命令pyinstaller main.py
会生成一个main.spec
文件,在这个文件中找到Analysis
,修改binaries
和datas
数组,可以手动引入dll和静态文件。数组的每一项是一个元组,作为pyinstaller
调用函数获取文件的参数,元组的第一个参数为源目录,第二个为目标目录,相对或绝对路径均可。一个完整的.spec配置文件示例如下:
1 | # -*- mode: python ; coding: utf-8 -*- |
更多.spec文件的用法请前往官网:Using Spec Files — PyInstaller 4.2 documentation