26

所以我有一些 Python C 扩展,我之前构建并用于在 Win7 中运行的 32 位 Python。但是,我现在已经切换到 64 位 Python,并且在使用 MinGW-w64 构建 C 扩展时遇到了问题。

我根据这篇文章对 distutils 进行了更改,但我收到了一些奇怪的错误,表明有问题:

$ python setup.py build
running build
running build_ext
building 'MyLib' extension
c:\MinGW64\bin\x86_64-w64-mingw32-gcc.exe -mdll -O -Wall -Ic:\Python27\lib\site-packages\numpy\core\include -Ic:\Python27\include -Ic:\Python27\PC -c MyLib.c -o build\temp.win-amd64-2.7\Release\mylib.o
MyLib.c: In function 'initMyLib':
MyLib.c:631:5: warning: implicit declaration of function 'Py_InitModule4_64' [-Wimplicit-function-declaration]
writing build\temp.win-amd64-2.7\Release\MyLib.def
c:\MinGW64\bin\x86_64-w64-mingw32-gcc.exe -shared -s build\temp.win-amd64-2.7\Release\mylib.o build\temp.win-amd64-2.7\Release\MyLib.def -Lc:\Python27\libs -Lc:\Python27\PCbuild\amd64 -lpython27 -o build\lib.win-amd64-2.7\MyLib.pyd
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x13d): undefined reference to `__imp_PyExc_ValueError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1275): undefined reference to `__imp_PyExc_ValueError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1eef): undefined reference to `__imp_PyExc_ImportError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1f38): undefined reference to `__imp_PyExc_AttributeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1f4d): undefined reference to `__imp_PyCObject_Type'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1f61): undefined reference to `__imp_PyExc_RuntimeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1fc7): undefined reference to `__imp_PyExc_RuntimeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x1ffe): undefined reference to `__imp_PyExc_RuntimeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x2042): undefined reference to `__imp_PyExc_RuntimeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x206c): undefined reference to `__imp_PyExc_RuntimeError'
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x208a): more undefined references to `__imp_PyExc_RuntimeError' follow
build\temp.win-amd64-2.7\Release\mylib.o:MyLib.c:(.text+0x20a7): undefined reference to `__imp_PyExc_ImportError'
collect2.exe: error: ld returned 1 exit status
error: command 'x86_64-w64-mingw32-gcc' failed with exit status 1

我用谷歌搜索了很多信息,但要找到明确的答案并不容易。有人可以对此有所了解吗?为了能够在 Win7 中为 64 位 Python 成功构建 C 扩展,我应该做哪些进一步的更改?

编辑:

在下面 cgohlke 的评论中提供了一些有用的指示后,我设法生成了libpython27.a. 但是,在遵循这篇文章的建议(倒数第二个)之后,我仍然遇到了__imp_Py_InitModule4_64错误。在经过一番严肃的 Google-fu 之后,我设法绊倒了这篇文章,告诉我将Py_InitModule4行重命名为Py_InitModule4_64. 在那之后,一切都顺利进行。

4

5 回答 5

15

这对我使用 Python 3.3 有用:

  1. 从 dll 创建静态 python 库

    python dll通常在C:/Windows/System32;在 msys 外壳中:

    gendef.exe python33.dll
    
    dlltool.exe --dllname python33.dll --def python33.def --output-lib libpython33.a
    
    mv libpython33.a C:/Python33/libs
    
  2. 使用 swig 生成包装器

    例如, swig -c++ -python myExtension.i

  3. wrapper 必须用 MS_WIN64 编译,否则当你在 Python 中导入类时你的电脑会崩溃

    g++ -c myExtension.cpp -I/other/includes
    
    g++ -DMS_WIN64 -c myExtension_wrap.cxx -IC:/Python33/include
    
  4. 共享库

    g++ -shared -o _myExtension.pyd myExtension.o myExtension_wrap.o -lPython33 -lOtherSharedLibs -LC:/Python33/libs -LC:/path/to/other/shared/libs
    
  5. 确保所有共享库(gdal、OtherSharedLibs)都在您的 PATH 中(Windows 不使用 LD_LIBRARY_PATH 或 PYTHONPATH)

  6. 在 Python 中,只需:导入 myExtension

瞧!

于 2013-04-03T19:38:20.187 回答
10

我意识到这是一个老问题,但它仍然是最热门的搜索结果。今天,在 2019 年,我能够做到这一点:

https://github.com/PetterS/quickjs/commit/67bc2428b8c0716538b4583f4f2b0a2a5a49106c

简而言之:

  1. 确保 64 位版本的 mingw-w64 在 PATH 中。
  2. 猴子补丁 distutils:
    import distutils.cygwinccompiler
    distutils.cygwinccompiler.get_msvcr = lambda: []
    
  3. shell wrt 转义的一些差异。

  4. extra_link_args = ["-Wl,-Bstatic", "-lpthread"]为了静态链接并且没有额外的运行时依赖。

  5. pipenv run python setup.py build -c mingw32现在工作。

于 2019-07-19T09:09:13.347 回答
0

我为 setuptools 创建了一个猴子补丁,让您可以轻松地在 Windows 上使用 mingw64 构建_ext。见https://github.com/imba-tjd/mingw64ccompiler

于 2022-01-27T11:16:08.610 回答
0

这是 VC++ 构建工具的示例代码 https://github.com/starnight/python-c-extension/tree/master/00-HelloWorld

你可以试试:

python setup.py -c mingw32

但是,这对我不起作用。

我的解决方案是:

  1. 安装 Anaconda 64 位 python 3.6

  2. 安装mingw64

  3. 将 mingw64/bin 添加到 PATH
  4. 从c文件编译dll

    gcc -c libmypy.c -IC:\Users\{user_name}\Anaconda3\pkgs\python-3.6.4-h6538335_1\include  
    gcc -shared -o libmypy.dll libmypy.o  -LC:\Users\{user_name}\Anaconda3\pkgs\python-3.6.4-h6538335_1\libs -lPython36
    
  5. 在 .py 脚本中加载 dll 文件

    from ctypes import *  
    m = cdll.LoadLibrary(r"C:\{path_to_dll}\libmypy.dll")  
    print(m.hello())
    
于 2018-09-19T14:56:34.320 回答
-1

我使用这个线程来学习如何制作 C 扩展,并且由于我学到的大部分内容都在其中,我想我也将最后的发现放在这里,以便其他人在寻找时可以找到它。

我并没有试图编译一些大的东西,只是 Hetland's Starting Python 中的示例。这是我所做的(示例 C pgm 称为 palindrome.c)。我正在使用带有 python 3.7 的 Anaconda,以及 MinGW64 的 TDM-GCC 版本。我将所有使用的工具放入我的 Path 中,并将 PYTHONPATH 中所需的所有路径以及 ..\Anaconda3 目录放入 PYTHON_HOME。我最终还是在某些事情上使用了显式路径。

如上所述,我使用 gendef.exe 和 dlltool.exe 创建了 libpython37.a 库,并将其放在 ..\Anaconda3\libs 中。

我按照赫特兰的处方:

gcc -c palindrome.c

gcc -I$PYTHON_HOME -I$PYTHON_HOME/包含 -c palindrome_wrap.c

第二次失败,编译器找不到 Python.h,以下工作:

gcc -I[一些目录]\Anaconda3\Include -c palindrome_wrap.c

然后我做了,正如许多人所说,包括 Hetland 3rd ed.,

gcc -shared palindrome.o palindrome_wrap.o [一些目录]/Anaconda3/libs/libpython37.a -o _palindrome.dll

这没有用。即使使用了加载库 cswu(我也在其他地方找到)。

所以我gendef'd _palindrome.dll 并且在导出中找不到函数“is_palindrome”。我浏览了一些 SWIG 文档,并在 %{ %} 部分及其下方(均为 extern)中声明了该函数,最终在 palindrome_wrap.c 中获得了应有的函数 extern。但是没有导出,所以我回到 palindrome.c 并将函数重新声明为:

declspec(dllexport) extern int __stdcall is_palindrome(char* text)

并在上面的两个地方用这个签名在 palindrome.i 中重新声明它。

部分成功!当我 gendef'd _palindrome.dll 时,它被列在导出部分,我可以使用 Load Library 进行 cswu 的调用。但仍然不按赫特兰所说和做的

导入_回文

在 Python 中。

再次回到所有来源,我无法弄清楚这一点。我终于从一开始就开始阅读 SWIG 文档,不遗余力——通过手册搜索并没有找到找到的地方。

在介绍部分结束时。2.7 并入构建系统,在示例 Make 过程下,它说:

“上面的示例将生成本机构建文件,例如 makefile、nmake 文件和 Visual Studio 项目,它们将调用 SWIG 并将生成的 C++ 文件编译为 _example.so (UNIX) 或 _example.pyd (Windows)。对于 Windows 上的其他目标语言通常会生成一个 dll,而不是 .pyd 文件。”

这就是最后一个问题的答案:

dll 的编译步骤应为:

gcc -shared palindrome.o palindrome_wrap.o [一些目录]/Anaconda3/libs/libpython37.a -o _palindrome.pyd

(我没有回去更改我的 declspec 声明,所以我不知道它们是否有必要,所以它们也仍然存在)。

我有一个文件,_palindrome.pyd

如果在 PYTHONPATH 中(我的是本地的)工作,然后可以做

导入_回文

从 _palindrome 导入 is_palindrome

并按照承诺在 python 中使用导出、正确包装和打包的 C 函数,使用 TDM-GCC 编译。gcc,它是 MinGW64 在不同的安装,知道如何做 .pyd 文件。我区分了 dll 和 pyd,因为它们的字节长度相同。它们在数百个点上都不相同。

希望这对其他人有帮助。

于 2020-03-26T14:30:41.827 回答