首页 > 解决方案 > 如果从 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)

标签: pythonsubprocesspyinstaller

解决方案


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

推荐阅读