Python 扩展 Dll 安装

Jam*_*ell 5 c++ python windows

我有一个用 C++ 编写的大型程序,我希望通过 Python 使其可用。我编写了一个 python 扩展来公开一个接口,python 代码可以通过该接口调用 C++ 函数。我遇到的问题是安装似乎很重要。

我能找到的所有文档似乎都表明我应该创建一个setup.py创建distutils.core.Extension. 在我发现的每个示例中,正在创建的 Extension 对象都有一个源文件列表,它会编译这些文件。如果我的代码是一两个文件,那就没问题了。不幸的是,它有几十个文件,我使用了许多相对复杂的 Visual Studio 构建设置。因此,至少可以说,通过列出 .c 文件来构建似乎具有挑战性。

我目前已将我的 Python 扩展配置为构建为 .dll 并链接到 python39.lib。我尝试将扩展名更改为 .pyd 并将文件包含在 manifest.in 中。创建 setup.py 并运行后,它创建了一个 .egg 文件,我验证该文件确实包含我创建的 .pyd。但是,安装后,当我将模块导入python时,模块完全为空(并且我验证了没有调用PyInit_[module]函数)。Python dll Extension Import说如果我将扩展名更改为 .pyd 并将文件放在 python 安装的 Dlls 目录中,我可以导入 dll。我遇到了两个问题。

首先,在我看来,它不是很容易分发。我想把它打包成一个 python 轮子,我不确定一个轮子是如何做到这一点的。第二个更有问题 - 它并不完全有效。它调用我的扩展程序的初始化函数,并且我已经在 WinDbg 中验证它正在返回一个 python 模块。然而,这是我总是从控制台得到的。

>>> import bluespawn
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: initialization of bluespawn did not return an extension module
Run Code Online (Sandbox Code Playgroud)

Python 文档中有一个部分是关于发布二进制扩展的,但在过去四年中,它一直被用作占位符。此处链接的 github 问题也没有那么有用;归结为要么使用 distutils 构建,要么使用 enscons 构建。但是由于我的构建是一个相当复杂的过程,至少可以说,完全重写它以使用 enscons 不太理想。 Python 文档

在我看来,将文件放在 DLLs 目录中是解决此问题的错误方法。鉴于我有一个 DLL 并且让 setuptools 编译所有内容本身似乎不可行,我应该如何安装我的扩展?

作为参考,这是我的初始化函数,以防万一。

>>> import bluespawn
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: initialization of bluespawn did not return an extension module
Run Code Online (Sandbox Code Playgroud)

python 接口在这里可用:https : //github.com/ION28/BLUESPAWN/blob/client-add-pylib/BLUESPAWN-win-client/src/user/python/PythonInterface.cpp

编辑:我有一个可行的解决方案,我确信这不是最佳实践。我创建了一个非常小的 C 文件,它只是将它接收到的所有调用传递到我已经创建的大型 DLL 上。C 文件负责初始化模块,但其他所有内容都在 DLL 内处理。它有效,但它似乎是一种非常糟糕的做事方式。我正在寻找的是一种更好的方式来做到这一点。

kma*_*ork 3

让我尝试将您的帖子分为两个单独的问题:

  1. 如何使用 setuptools 通过不平凡的编译过程打包 C++ 库
  2. 是否可以分发带有预编译库的Python包

1. 如何使用 setuptools 通过不平凡的编译过程打包 C++ 库

有可能的。我很惊讶地发现它提供了许多方法来覆盖编译过程,请参阅此处的setuptools文档。例如,您可以使用关键字参数将额外的参数传递给编译器。另外,与Python文件一样,您可以相对轻松地编写一些代码来自动收集编译所需的所有文件。我自己在一个项目( github )中完成了这个工作,它对我来说效果很好。extra_compile_argssetup.py

这是来自 的一些代码setup.py

libinjector = Extension('pyinjector.libinjector',
                        sources=[str(c.relative_to(PROJECT_ROOT))
                                 for c in [LIBINJECTOR_WRAPPER, *LIBINJECTOR_SRC.iterdir()]
                                 if c.suffix == '.c'],
                        include_dirs=[str(LIBINJECTOR_DIR.relative_to(PROJECT_ROOT) / 'include')],
                        export_symbols=['injector_attach', 'injector_inject', 'injector_detach'],
                        define_macros=[('EM_AARCH64', '183')])
Run Code Online (Sandbox Code Playgroud)

2. 是否可以分发带有预编译库的python包

我从您的编辑中了解到您已经设法使其正常工作,但无论如何我还是要说几句话。可以使用源发行版发布预编译的二进制文件,也可以在 Wheel 文件中发布手动编译的二进制文件,但不建议这样做。

主要原因是与目标架构的兼容性。首先,您必须在发行版中包含两个 DLL,一个用于 x64,一个用于 x86。其次,您可能会失去一些不错的优化,因为您必须指示编译器忽略可用于特定 CPU 类型的优化(请注意,这也适用于正常的轮分布)。如果您针对 Windows SDK 进行编译,您可能也想使用用户的版本。此外,在您的发行版中包含两个 DLL 可能会使发行版的大小变得对于源代码发行版来说很尴尬。