1背景
最近使用pyinstaller打包python程序,启动程序的时候,发现了以下的报错信息
Failed to execute script "pyi_rth_pkgres" due to unhandled dll load failed while importing pyexpat
后面查阅了相关文档,比如根据stackoverflow的文章
重装了很多次pyinstaller。还是不行,后面只能乖乖去查询打包日志,发现了一连串的警告:
111781 WARNING: Library not found: could not resolve 'libcrypto-3-x64.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_hashlib.pyd'.
111781 WARNING: Library not found: could not resolve 'liblzma.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_lzma.pyd'.
111781 WARNING: Library not found: could not resolve 'LIBBZ2.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_bz2.pyd'.
111781 WARNING: Library not found: could not resolve 'libcrypto-3-x64.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_ssl.pyd'.
111781 WARNING: Library not found: could not resolve 'libssl-3-x64.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_ssl.pyd'.
111781 WARNING: Library not found: could not resolve 'ffi.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_ctypes.pyd'.
111781 WARNING: Library not found: could not resolve 'libexpat.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\pyexpat.pyd'.
111781 WARNING: Library not found: could not resolve 'zstd.dll', dependency of 'D:\\programs\\miniconda3\\Lib\\site-packages\\zstandard\\backend_c.cp312-win_amd64.pyd'.
111781 WARNING: Library not found: could not resolve 'libcrypto-3-x64.dll', dependency of 'D:\\programs\\miniconda3\\Lib\\site-packages\\cryptography\\hazmat\\bindings\\_rust.cp312-win_amd64.pyd'.
111781 WARNING: Library not found: could not resolve 'libssl-3-x64.dll', dependency of 'D:\\programs\\miniconda3\\Lib\\site-packages\\cryptography\\hazmat\\bindings\\_rust.cp312-win_amd64.pyd'.
111781 WARNING: Library not found: could not resolve 'sqlite3.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_sqlite3.pyd'.
111781 WARNING: Library not found: could not resolve 'tcl86t.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_tkinter.pyd'.
111781 WARNING: Library not found: could not resolve 'tk86t.dll', dependency of 'D:\\programs\\miniconda3\\DLLs\\_tkinter.pyd'.
2混沌时期
此时心中有很多疑问,主要还是以下2个:
(1)如果缺少dll,按理说本地源码也无法启动,但是本地是完美运行起来
(2)如果是因为缺少包没有安装,pip install都已经执行过,本地源码方式确实可以启动。
通过大量资料查询,也没发现任何的问题。此时最快的方法就是换一台电脑进行打包,发现另外一台电脑是可以打包出来,而且可以拷贝到其他电脑运行。
对比了这两台电脑不同之处,就是一台电脑使用conda环境下进行打包,另外一台电脑则不是。
3 发现根因
通过bing chat的帮助下,使用以下命令去提问
conda environment pyinstaller Library not found: could not resolve 'libexpat.dll',
相对之前搜索方式,增加了限定词。后面bing chat给出了更加详细的解决方案。
When using PyInstaller within a Conda environment, encountering the “Library not found: could not resolve ‘libexpat.dll’” error can be particularly tricky. Here are some steps to help you resolve this issue:
-
Ensure Expat is Installed in Conda Environment:
Make sure theexpat
library is installed in your Conda environment:conda install expat
-
Check Environment Variables:
Ensure that the Conda environment’s library paths are correctly set. You can activate your Conda environment and then check the paths:conda activate your_env_name echo %PATH% # On Windows echo $LD_LIBRARY_PATH # On Unix-like systems
-
Manually Include DLL:
If the library is still not found, you can manually include thelibexpat.dll
in your PyInstaller build. Locate thelibexpat.dll
file within your Conda environment (usually found inenvs/your_env_name/Library/bin
on Windows) and copy it to the same directory as your script or specify it in the.spec
file:a = Analysis(['your_script.py'], binaries=[('path/to/libexpat.dll', '.')], ...)
-
Use PyInstaller Hooks:
Create a custom hook to ensure PyInstaller includes thelibexpat
dependency:# hook-libexpat.py from PyInstaller.utils.hooks import collect_dynamic_libs binaries = collect_dynamic_libs('libexpat')
-
Rebuild the Executable:
After making these changes, try rebuilding your executable:pyinstaller your_script.py
-
Check Conda Compatibility:
Ensure that the versions of PyInstaller and Conda are compatible. Sometimes, updating or downgrading PyInstaller within the Conda environment can resolve such issues:conda update pyinstaller
These steps should help you resolve the issue with libexpat.dll
in your Conda environment. If you encounter any specific errors during these steps, feel free to share them, and I can assist further!
以上均是bingchat的回答,很明显,我少了conda install的方法,后面用bing chat的建议,将所有warn的library用conda install安转了一遍,重新打包,发现了打包后的程序可以启动。
4 思考
conda install 到底与之前用pip install有什么区别。
一、安装源不同;
二、支持语言范围不同;
三、安装思路不同;
四、软件包数量不同;
五、包存储的位置不同。
一 源不同
pip 从PyPI(Python Package Index)上拉取数据。上面的数据更新更及时,涵盖的内容也更加全面
conda 从 Anaconda.org 上拉取数据。虽然Anaconda上有一些主流Python包,但在数量级上明显少于PyPI,缺少一些小众的包
支持语言不同
pip 是 python 官方推荐的包下载工具,但是只能安装python包
conda 是一个跨平台(支持linux, mac, win)的通用包和环境管理器,它除了支持python外,还能安装各种其他语言的包,例如 C/C++, R语言等。
三 安装思路不同
pip 里的软件包为wheel版或源代码发行版。wheel属于已编译发新版的一种,下载好后可以直接使用;而源代码发行版必须要经过编译生成可执行程序后才能使用,编译的过程是在用户的机子上进行的
conda 里的软件包都是二进制文件,下载后即可使用,不需要经过编译。
在解析依赖关系方面,pip和conda也有不同的方式。pip主要依赖于Python的包管理工具,它会解析软件包的依赖并尝试自动安装所需的依赖项。但是,由于Python软件包的依赖关系可能复杂且不一致,使用pip安装一些软件包时可能会遇到依赖问题。而conda则使用了更复杂的依赖解析算法,能够解决依赖关系冲突的问题,并确保所安装的软件包能够兼容。
pip安装包时,尽管也对当前包的依赖做检查,但是并不保证当前环境的所有包的所有依赖关系都同时满足。当某个环境所安装的包越来越多,产生冲突的可能性就越来越大。
conda会检查当前环境下所有包之间的依赖关系,保证当前环境里的所有包的所有依赖都会被满足
四 软件包数量
安装源不同在于,pip install使用PyPI作为安装源,而conda install使用Anaconda存储库作为安装源。
由于使用不同的软件源,pip和conda提供的软件包数量也有所不同。就python的包数量来说,PyPI是Python社区的主要存储库,拥有数量庞大的软件包。因此,pip可以访问到数量众多的软件包和工具。而conda则专注于科学计算和数据科学领域,其存储库中包含了许多常用的科学计算库和工具,但总体软件包数量可能相对较少。
五 包的储存位置:
在conda虚拟环境下使用 pip install 安装的库: 如果使用系统的的python,则库会被保存在 ~/.local/lib/python3.x/site-packages 文件夹中;如果使用的是conda内置的python,则会被保存到 anaconda3/envs/current_env/lib/site-packages中
conda install 安装的库都会放在anaconda3/pkgs目录下。这样的好处就是,当在某个环境下已经下载好了某个库,再在另一个环境中还需要这个库时,就可以直接从pkgs目录下将该库复制至新环境而不用重复下载
5 使用总结
建议在创建和管理环境时尽可能使用conda,并使用conda的环境管理功能来管理软件包和依赖项。不行再使用pip安装。