Py学习  »  Python

如何在Windows上正确定位Python中C扩展模块的DLL依赖关系?

Daniel • 3 年前 • 492 次点击  

我正在使用PyBind11为Python开发一个C++扩展。我的扩展依赖于CGAL,它依赖于MPFR和GMP。CGAL只是头文件,所以没有问题,但MPFR和GMP编译到共享库(Windows上的DLL文件)。

每种操作系统上的一切都可以编译和链接。在Windows上,我必须提供 CMAKE_PREFIX_PATH CMake的选项,以帮助它找到库的位置(在我的例子中,这些库是通过Miniconda安装的,所以它们位于 C:\Miniconda3\Library ),从而正确定位CGAL、GMP和MPFR。在编译和链接之后,Linux和Mac上的一切都运行良好,但在Windows上,出现了一个问题。简单地说,当我试图用Python导入扩展模块时,我遇到了错误

ImportError: DLL load failed while importing <mylibrary>: The specified module could not be found.

我知道这个错误意味着我的扩展所依赖的DLL(在本例中是MPFR和GMP)无法定位。如果我将包含这些DLL的目录添加到我的路径中,我希望这可以解决问题,但根据我读到的几篇文章,Python显然不再在路径中搜索DLL( example 1 , example 2 , example 3 , python docs ).相反,我们应该打电话 os.add_dll_directory() 告诉Python在哪里可以找到DLL。这对我来说毫无意义,因为作为库作者,我不知道,也不认为我应该依赖于用户决定安装库的位置。

例如,用户可能手动安装MPFR,也可能通过Conda或VCPKG安装MPFR,谁知道呢。每一个都会把它放在一个完全不同的位置,我不知道。在我的Python模块中,我怎么知道在哪里可以找到这些库呢?

This question 非常相似,但是DLL是与库源代码打包的,所以它们的位置很明显,而且 操作系统。添加_dll_目录() 这是一个明智的解决方案。在我的例子中,我依赖于用户可以在任何地方安装的外部DLL。

我将列举我迄今为止找到的次优解决方案。它们都不是特别好。

解决方案1 为了证实我的怀疑,即这些是找不到的库,我可以通过手动将相关的DLL文件复制到与编译的扩展模块相同的目录中来解决问题。当然,这个解决方案是手动的,不健壮,每次我进行新构建时都必须这样做。它也不是一个很好的解决方案,必须向想要编译我的库的用户描述。

解决方案2 在构建库的轮子时,我找到了Python脚本 delvewheel ,它会搜索控制盘所依赖的DLL并将它们添加到控制盘中(它实际上会找到DLL,因为它查看路径!!!)在分发控制盘的情况下,这似乎解决了问题,这很好,但在用户希望在本地手动编译和测试我的库的情况下,这没有帮助,除非他们也完成了构建控制盘的步骤,使用delvewheel修复它,然后安装它,这对本地开发/测试工作流没有意义。

解决方案3 我可以将DLL文件的位置硬编码为调用 操作系统。添加_dll_目录() .这是可行的,但当然,这很糟糕,因为这取决于我在哪里安装了它们!显然不适合将代码分发给其他人。

解决方案4 我可以要求用户指定一个指向DLL位置的特殊环境变量,然后将该环境变量的值传递给 操作系统。添加_dll_目录() 。这意味着用户每次要运行我的库时都必须指定一个特殊的环境变量。不太好。此外,如果多个依赖项位于不同的位置,它也不会起作用。

解决方案5 我还可以解析路径并将其中的每个位置传递给 操作系统。添加_dll_目录() ,就像这样。

if "add_dll_directory" in dir(os):
  for d in os.environ['path'].split(';'):
    if os.path.isdir(d):
      os.add_dll_directory(d)

这是可行的,但它似乎绕过了Python的全部目的,即不查看路径并使用 操作系统。添加_dll_目录() 首先。这有什么问题吗?这个 python changelog 这似乎表明安全性可能是一个问题?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/132905
 
492 次点击