【问题标题】:How can I build my C extensions with MinGW-w64 in Python?如何在 Python 中使用 MinGW-w64 构建我的 C 扩展?
【发布时间】:2012-06-25 02:12:27
【问题描述】:

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

我根据this post 对 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 的 cmets 中提供了一些有用的指针后,我设法生成了 libpython27.a。但是,在遵循this post(倒数第二个)的建议后,我仍然遇到__imp_Py_InitModule4_64 错误。经过一番严肃的 Google-fu 之后,我设法绊倒了 this post,告诉我将 Py_InitModule4 行重命名为 Py_InitModule4_64。之后一切顺利。

【问题讨论】:

  • 你需要用gendef.exe python27.dlldlltool.exe --dllname python27.dll --def python27.def --output-lib libpython27.a创建libpython27.a并把它放在C:\Python27\libs
  • 我看到一些帖子提到了gendef。但是要找到有关如何生成 libpython27.a 文件的信息并不容易。你会碰巧知道吗?
  • 哦,好的,gendef 和 dlltool 是两个不同的命令。会尝试。谢谢。
  • 好的,它编译得很好,但是当我尝试导入模块时,我得到Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: DLL load failed: The specified procedure could not be found. 我想我一定是在某个地方丢失了一个文件。我对 gendef 所做的唯一事情就是将libpython27.a 放入C:\Python27\libs。我错过了什么吗?
  • 所以我按照this 帖子(倒数第二个)完成了所有步骤。但现在它不会编译,我得到这个build\temp.win-amd64-2.7\Release\cquant.o:cQuant.c:(.text+0x1f09): undefined reference to __imp_Py_InitModule4_64' collect2.exe: error: ld returned 1 exit status`

标签: python c compilation 64-bit


【解决方案1】:

这对我来说适用于 Python 3.3:

  1. 从 dll 创建静态 python 库

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

    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 中,只需:import myExtension

瞧!

【讨论】:

    【解决方案2】:

    我意识到这是一个老问题,但它仍然是最热门的搜索结果。 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 w.r.t. 的一些差异。逃跑。

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

    5. pipenv run python setup.py build -c mingw32 现在可以使用了。

    【讨论】:

    • 谢谢,是的,它已经为我工作了一段时间了。我们可能应该尝试在 distutils 中检查一些更永久的东西......
    【解决方案3】:

    这是 VC++ Build Tools 的示例代码 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())
      

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        我用这个线程来学习如何做一个 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 库,正如 Mark 上面所说的,并将它放在 ..\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(我也在其他地方找到)。

        所以我对 _palindrome.dll 进行了gendef,但在导出中找不到函数“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 [somedirectories]/Anaconda3/libs/libpython37.a -o _palindrome.pyd

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

        我有一个文件,_palindrome.pyd

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

        导入_回文

        从 _palindrome 导入 is_palindrome

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

        希望这对其他人有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-05
          • 2010-11-04
          • 2012-12-08
          • 2016-11-04
          • 2020-11-11
          • 2021-06-16
          相关资源
          最近更新 更多