Python扩展DLL安装

标签 python c++ windows

我有一个用 C++ 编写的大型程序,我希望通过 Python 使其可用。我编写了一个 python 扩展来公开一个接口(interface),python 代码可以通过该接口(interface)调用 C++ 函数。我遇到的问题是安装似乎很重要。
我能找到的所有文档似乎都表明我应该创建一个 setup.py这会创建一个 distutils.core.Extension .在我发现的每个示例中,正在创建的 Extension 对象都被提供了一个源文件列表,它会编译这些文件。如果我的代码是一两个文件,那就没问题了。不幸的是,它有几十个文件,并且我使用了一些相对复杂的 Visual Studio build设置。因此,至少可以说,通过列出 .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
Python 文档中有一个关于发布二进制扩展的部分,但在过去的四年中,它一直被保留为占位符。 github问题链接here也不是很有帮助;它归结为使用 distutils 构建或使用 enscons 构建。但是由于我的构建是一个相当复杂的过程,至少可以说,完全重写它以使用 enscons 是不可取的。
Python Documentation
在我看来,将文件放在 DLLs 目录中是错误的做法。鉴于我有一个 DLL 并且让 setuptools 编译一切本身似乎是不可行的,我应该如何安装我的扩展程序?
作为引用,这是我的初始化函数,以防不正确。
PyModuleDef bsModule{ PyModuleDef_HEAD_INIT, "bluespawn", "Bluespawn python bindings", -1, methods };
 
PyMODINIT_FUNC PyInit_bluespawn() {
    PyObject* m; 
    Py_Initialize();
    PyEval_InitThreads();
    PyGILState_STATE state = PyGILState_Ensure(); // Crashes without this. Call to PyEval_InitThreads() required for this. 
    m = PyModule_Create(&bsModule);
    PyGILState_Release(state);
    Py_Finalize();
    return m;
}
此处提供 python 接口(interface):https://github.com/ION28/BLUESPAWN/blob/client-add-pylib/BLUESPAWN-win-client/src/user/python/PythonInterface.cpp
编辑:我有一个可行的解决方案,我确信这不是最佳实践。我创建了一个非常小的 C 文件,它只是将它接收到的所有调用传递到我已经创建的大型 DLL 上。 C 文件负责初始化模块,但其他一切都在 DLL 中处理。它有效,但它似乎是一种非常糟糕的做事方式。我正在寻找的是一种更好的方法。

最佳答案

让我试着把你的帖子分成两个单独的问题:

  • 如何使用 setuptools
  • 以非平凡的编译过程打包 C++ 库
  • 是否可以分发带有预编译库的 python 包

  • 1. 如何使用 setuptools 打包一个具有非平凡编译过程的 C++ 库
    有可能的。我很惊讶地看到 setuptools提供了许多方法来覆盖编译过程,请参阅文档 here .例如,您可以使用关键字参数 extra_compile_args将额外的参数传递给编译器。
    此外,如 setup.py是一个python文件,你可以相对容易地编写一些代码来自动收集编译所需的所有文件。我自己在一个项目( github )中完成了这项工作,它对我来说效果很好。
    这是来自 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')])
    
    2.是否可以分发带有预编译库的python包
    我从您的编辑中了解到,您已经设法让它发挥作用,但我还是要说几句话。可以使用源代码分发发布预编译的二进制文件,也可以在 wheel 文件中发布手动编译的二进制文件,但不建议这样做。
    主要原因是与目标架构的兼容性。首先,您必须在您的发行版中包含两个 DLL,一个用于 x64,一个用于 x86。其次,您可能会失去一些不错的优化,因为您必须指示编译器忽略可用于特定 CPU 类型的优化(请注意,这也适用于正常的车轮分布)。如果您正在针对 Windows SDK 进行编译,您可能也希望使用用户的版本。此外,在您的版本中包含两个 DLL 可能会使其大小变得难以用于源代码分发。

    关于Python扩展DLL安装,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66749737/

    相关文章:

    python - 属性错误: 'function' object has no attribute <Variable>

    c++ - 需要指针时在参数列表中实例化类

    java - “GSSException Defective token detected”-尝试使用Kerberos对Windows上运行的Tomcat进行身份验证时

    c++ - 在 C++ 中设置编码的最正确方法是什么?

    python - 在关系一侧的子集上查询多对多关系

    python - 从文件中读取模式并使用python写入另一个文件

    Python 搪瓷 "id: invalid syntax"

    c++ - 运算符重载(混淆为什么调用这个运算符)C++

    c# - 存储集合的最佳实践方式

    windows - 虚拟硬盘镜像格式规范错误?