python - 如果从 Pyinstaller 编译的应用程序调用,可执行文件找不到库
问题描述
从使用 构建的应用程序运行mysqlbinlog
进程时pyinstaller
,会出现以下错误消息:
mysqlbinlog: /opt/myapp/libz.so.1: version `ZLIB_1.2.9 'not found (required by mysqlbinlog)
我进行了几次测试并注意到发生这种情况是因为在安装应用程序的目录中pyinstaller
放置了旧版本的libz.so.1
,而不是所需的。mysqlbinlog
如果删除此文件/opt/myapp/libz.so.1
,则问题将得到解决,因为使用了库的系统版本。
一个简单的解决方案是libz.so.1
从捆绑包中删除,但我不确定之后该应用程序将在不同版本的系统上正常工作。
通过子进程运行命令时,有什么方法可以强制应用程序不使用位于应用程序安装目录中的库?
附加信息:
该应用程序使用以下命令构建在 docker 容器 centos:7.2.1511 中:
pyinstaller myapp.py --clean --name=myapp
此错误在 Ubuntu 20.04.3 LTS 上稳定模拟
mysqlbinlog 通过子进程运行,使用以下命令:
subprocess.run(command_args, check = True, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
解决方案
Linux 有一个环境变量 LD_LIBRARY_PATH,它指定搜索系统库的目录。Pyinstaller 以这样一种方式替换此变量,即在包本身中搜索程序所需的库,从而使 Pyinstaller 编译的应用程序的工作独立于系统库。
同时,LD_LIBRARY_PATH 变量的旧值保存在 LD_LIBRARY_PATH_ORIG 环境变量中。因此,在启动第三方进程时,您只需为其设置正确的环境:从 LD_LIBRARY_PATH_ORIG 写入 LD_LIBRARY_PATH 值。
文档摘录:
LD_LIBRARY_PATH / LIBPATH 注意事项
此环境变量用于发现库,它是库搜索路径 - 在 GNU/Linux 和 *BSD LD_LIBRARY_PATH 上使用,在 AIX 上它是 LIBPATH。
如果存在,PyInstaller 将原始值保存到 *_ORIG,然后修改搜索路径,以便捆绑代码首先找到捆绑的库。但是,如果您的代码执行系统程序,您通常不希望该系统程序加载您的捆绑库(可能与您的系统程序不兼容) - 它应该像通常那样从系统位置加载正确的库。
因此,您需要在使用系统程序创建子进程之前恢复原始路径。
替换环境变量的代码:
env = dict(os.environ) # make a copy of the environment
lp_key = 'LD_LIBRARY_PATH' # for GNU/Linux and *BSD.
lp_orig = env.get(lp_key + '_ORIG')
if lp_orig is not None:
env[lp_key] = lp_orig # restore the original, unmodified value
else:
# This happens when LD_LIBRARY_PATH was not set.
# Remove the env var as a last resort:
env.pop(lp_key, None)
p = Popen(system_cmd, ..., env=env) # create the process
推荐阅读
- reactjs - 从输入文件处理文件并稍后上传反应redux
- python - Keras - 深度学习模型不训练
- angularjs - 如何通过 github 从子文件夹部署 AngularJs 应用程序?
- python-3.x - python formattng 输出和输入
- jekyll - Jekyll paginate: offset offsetting all pages, not just first
- javascript - 如何使用 redux 选择列表项?
- css - 如何在 Edge 浏览器中使用 CSS 变量作为关键帧动画的背景色
- azure - 未找到服务器的 Azure SQL 弹性池
- python - matplotlib 自动排序时间值
- webrtc - Webrtc SDP Audio 在发送方不起作用,但接收方可以收听发送方的声音