背景
目前在做的定型机智能验布项目,软件是用pyqt5写的,打包工具是pyinstaller,上次去现场部署,客户提出软件启动太慢,测试发现:打包之前的启动时间8秒,打包成一个独立文件夹,启动时间是33秒。原本计划优化pyinstaller打包之后的启动用时,同事给推荐了打包工具Nuitka。
折腾了3天,勉强把项目打包成一个可独立运行的文件夹,启动竟比打包前还要快,在此记录一下打包过程中遇到的问题,方便后续查看。
Nuitka打包是真的慢,每次要半个小时左右,参数尽量写到配置文件里,这样就不用每次更改都重新打包,只需要更换配置文件即可。
Nuitka打包步骤
windows系统+PyCharm+配置了Conda虚拟环境
Nuitka安装
python -m pip install nuitka
#自从电脑上安装了加密软件,使用pip install 命令,会默认安装到base环境中,使用python -m pip install 命令才能安装到对应虚拟环境中
先用简单程序测试打包流程
项目的目录结构相对复杂,直接打包项目代码,一是每次测试打包的用时长,二是报错不方便排查。我刚开始就遇到了下面的问题1,折腾了一下午也没有解决,很是消耗耐心。
新创建一个项目,与项目代码配置同样的虚拟环境,一个名为 hello.py 的 python 文件
def talk(message):
return "Talk " + message
def main():
print(talk("Hello World"))
if __name__ == "__main__":
main()
保证代码能够正常运行之后,运行下面的打包命令:
python -m nuitka --mingw64 --standalone --show-memory --show-progress --output-dir=out hello.py
#第一次打包时,会提示你下载一个 C 语言缓存工具(以加速重复编译生成的 C 代码)和一个基于 MinGW64 的 C 语言编译器,输入yes即可。
–mingw64:Windows 上基于gcc的 MinGW64 编译器( --clang,–msvc=MSVC_VERSION,–mingw64)
–standalone:程序打包成一个可独立执行的文件夹(将这个文件夹复制到其他没有python环境的电脑上,依然可以运行)。与之对应的是–onefile:将程序打包成一个文件。
–show-memory:显示内存使用情况
–show-progress:显示打包进度
–output-dir=out:在项目文件根目录下生成一个out文件夹,out/hello.dist/hello.exe即是程序执行文件
hello.py:是你的程序入口文件
实战
项目文件结构如下:
说明:
data:输入、输出的图片、报告文件夹
out:打包生成的文件夹
processing:代码的主要内容,为了方便打包,将代码集中放置在了该包中
main.py:程序入口文件
打包命令:
python -m nuitka --mingw64 --standalone --show-memory --show-progress --enable-plugin=pyqt5 --include-package=processing --include-package-data=processing:* --include-data-dir=data=data --output-dir=out --remove-output main.py
–enable-plugin:启用插件,等号后跟插件名。在要打包的Python代码使用了一些特殊的包时,需要启用插件,Nuitka才能够正确打包。如:在代码中使用了pyqt5,就需要加上–enable-plugin=pyqt5。具体的插件列表可以使用nuitka --plugin-list来查看。
–include-data-dir:指定包含数据目录,指定目录会复制到生成 exe的目录中。
–remove-output:在打包结束后,清理打包过程中生成的临时文件。
–disable-console:在运行打包后的程序时,不会弹出控制台,而是直接运行GUI程序。测试打包时,为方便在控制台中查看报错信息,先不使用–disable-console。
运行程序
打包程序往往并不是一次就能够打包成功的,常常会出现缺少引用关系,忽略了一些数据文件等等情况。在这些情况下,我们需要根据运行程序后显示的报错信息,使用下面的选项来打出正确的包。
–include-package-data:包含给定软件包名称中的数据文件,如–include-package-data=processing:* 表示自动复制processing包中所有的数据文件。
–include-data-files:按文件名包含数据文件,等号后的格式为”SRC=DEST”。SRC指的是文件夹的路径,DEST指的是文件夹相对于打包结果的路径,其中DEST只能使用相对路径。如:–include-data-files=/Users/admin/Downloads/yolov5n.pt=./yolov5n.pt
–include-data-dir:包含文件夹中所有的数据文件,等号后的格式为”SRC=DEST”。使用方法与–include-data-files相同。
无需关注的选项
–follow-imports:作用是在打包过程中分析程序的引用关系。但是在 –standalone 和 –onefile 下,该选项是强制启用的,因此不需要额外加上。
实用命令
python -m nuitka --help # 查看 Nuitka 帮助手册
python -m nuitka --plugin-list # 查看 Nuitka 插件列表
问题记录
1 OSError: [WinError 17] 系统无法将文件移到不同的磁盘驱动器
打包命令:
python -m nuitka --standalone --show-memory --show-progress --output-dir=out main.py
报错信息:
Nuitka: Running data composer tool for optimal constant value handling.
Nuitka: Running C compilation via Scons.
Nuitka-Scons: Backend C compiler: cl (cl 14.2).
Backend C: 3.8%|▉ | 38/1007scons: *** [module.PIL.JpegPresets.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.MpegImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.McIdasImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.MicImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PcdImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PaletteFile.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.MspImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.MpoImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PixarImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PalmImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PcxImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.SpiderImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PpmImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.SunImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PsdImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.SgiImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PyAccess.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PdfImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PngImagePlugin.obj] 系统无法将文件移到不同的磁盘驱动器。
scons: *** [module.PIL.PdfParser.obj] 系统无法将文件移到不同的磁盘驱动器。
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\SconsCaching.py", line 259, in _writeClcacheStatistics
stats.save()
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\clcache\clcache\caching.py", line 785, in save
json.dump(self._dict, f, sort_keys=True, indent=4)
File "D:\Anaconda3\envs\yolov6\lib\contextlib.py", line 120, in __exit__
next(self.gen)
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\atomicwrites\atomicwrites.py", line 169, in _open
self.commit(f)
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\atomicwrites\atomicwrites.py", line 202, in commit
replace_atomic(f.name, self._path)
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\atomicwrites\atomicwrites.py", line 99, in replace_atomic
return _replace_atomic(src, dst)
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\atomicwrites\atomicwrites.py", line 79, in _replace_atomic
_handle_errors(windll.kernel32.MoveFileExW(
File "D:\Anaconda3\envs\yolov6\lib\site-packages\nuitka\build\inline_copy\atomicwrites\atomicwrites.py", line 76, in _handle_errors
raise WinError()
OSError: [WinError 17] 系统无法将文件移到不同的磁盘驱动器。
FATAL: Failed unexpectedly in Scons C backend compilation.
Nuitka:WARNING: Complex topic! More information can be found at https://nuitka.net/info/scons-backend-failure.html
Nuitka-Reports: Compilation crash report written to file 'nuitka-crash-report.xml'.
解决办法:
尝试更改缓存、临时目录的位置到项目文件所在的磁盘(设置环境变量NUITKA_CACHE_DIR、 SCONS_CACHE_DIR、TEMP、TMP),没有作用。
在打包命令中加上–mingw64,安装了mingw64编译器,就可以正常打包了,应该是默认使用的-cl编译器有问题。(第一次安装mingw64时报错,提示无法从github下载安装文件,再次尝试又可以了,可能是网络问题)
2 打包文件中缺失一些东西
打包命令:
python -m nuitka --mingw64 --standalone --show-memory --show-progress --enable-plugin=pyqt5 --include-package=processing --include-package-data=processing:* --include-data-dir=data=data --output-dir=out --remove-output main.py
报错信息:
D:\Projects\dingxingji_v0.1\concat_v0.5-exe\out\main.dist>main.exe
QSqlDatabase: QSQLITE driver not loaded
QSqlDatabase: available drivers:
数据库目录创建错误!
QSqlQuery::exec: database not open
Create table successed: fabric_info
QSqlQuery::exec: database not open
Create table successed: detect_info
解决办法:
找到虚拟环境中的QSQLITE驱动包,复制到可执行文件out/main.dist/pyqt5对应路径中
3 打包文件移植到其他电脑上,运行报错
报错信息:
Traceback (most recent call last):
File "D:\99PROJ~1\CONCAT~1.5-N\out\MAIN~1.DIS\multiprocessing\process.py", line 315, in _bootstrap
File "D:\99PROJ~1\CONCAT~1.5-N\out\MAIN~1.DIS\multiprocessing\process.py", line 108, in run
File "D:\99PROJ~1\CONCAT~1.5-N\out\MAIN~1.DIS\main.py", line 608, in deal_images
File "D:\99PROJ~1\CONCAT~1.5-N\out\MAIN~1.DIS\processing\utils\operate_model.py", line 23, in load_model
File "D:\99PROJ~1\CONCAT~1.5-N\out\MAIN~1.DIS\ctypes\__init__.py", line 373, in __init__
FileNotFoundError: Could not find module 'processing/configs/tensorrt_yolov8_0425.dll' (or one of its dependencies). Try using the full path with constructor syntax
tensorrt_yolov8_0425.dll 是加载trt模型,实现推断函数的文件(引用一个博主画的依赖关系示意图)
解决办法:
对于这类文件,可以在目标电脑上,安装依赖分析工具depends,查看缺失什么dll文件,然后在进行开发的Windows环境下查看相应的位置,并拷贝到当前*.exe运行的同一路径或者所包含的路径下。
depends使用方法
下载链接:https://www.dependencywalker.com/
depends无需安装,直接点击depends.exe运行即可。
双击depends.exe,打开缺少dll的exe或者dll(直接拖入,或者点击菜单栏上的file),运行如下:
参考资源
官网教程:https://nuitka.net/user-documentation/tutorial-setup-and-build.html
https://daobook.github.io/nuitka-doc/zh_CN/user-manual.html(中文)
https://www.acwing.com/blog/content/32947/ (条理很清楚)
Nuitka命令大全:https://www.bilibili.com/read/cv25190881/