如何在Windows上使用MinGW-W64创建C扩展/嵌入Python
问题描述
是否可以(以及如何)使用MinGW-W64在Windows上构建Python的C扩展或嵌入Python?
让我们以以下cython扩展foo.pyx
为例:
print("foo loaded")
如果应该嵌入解释器,则可以通过cython -3 foo.pyx
或cython -3 --embed foo.pyx
从中生成C代码。
解决方案
虽然并不真正支持mingw-w64
-编译器(唯一支持的Windows编译器是MSVC),但它可以用于创建C扩展或嵌入Python.但是不能保证,这在将来的版本中不会中断。
distutils
不支持mingw-w64
,因此设置setup.py文件没有任何好处-这些步骤必须手动执行。
首先我们需要一些通常由distutils
提供的信息:
- Headers:我们需要指向Python Includes的路径。有关查找它们的方法,请参阅SO-post。
- dll:mingw-w64的链接器与MSVC的链接器的工作方式不同:需要使用python-dll和notpython-lib。因此,我们需要指向
pythonXY.dll
的路径,该路径通常位于python.exe
旁边。
创建/生成C代码后,可以通过
构建扩展x86_64-w64-mingw32-gcc -shared foo.c -DMS_WIN64 -O2 <other_options> -I <path_to_python_include> -L <path_to_python_dll> -lpython37 -o foo.pyd
重要细节如下:
- 只使用
-O2
进行优化,将<other_options>
留空- 可能是可以的
- 重要的是定义
MS_WIN64
-宏(例如通过-DMS_WIN64
)。为了在Windows上构建x64,必须设置它,但它只能开箱即用for MSVC(定义_WIN64
可能会有略微不同的结果):
#ifdef _WIN64
#define MS_WIN64
#endif
如果不这样做,至少对于Cython生成的文件,编译器将生成以下错误消息:
error: enumerator value for ‘__pyx_check_sizeof_voidp’ is not an integer constant
201 | enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
pyd
只是一个伪装的DLL,所以我们需要-shared
选项,这意味着将创建一个动态库(即Linux世界中的共享对象)。重要的是,python库(
pythonXY
)应该是DLL本身,而不是lib
(参见SO-post)。我们使用指向pythonXY.dll
(在我的例子中是python37)的路径,而不是指向pythonXY.lib
的路径,就像MSVC的情况一样。
您可能应该向生成的PYD文件添加适当的后缀,为简单起见,我在这里使用旧的约定。
嵌入式Python:
在这种情况下,应该生成一个可执行文件(例如,C文件是由Cython使用--embed
选项:cython -3 --embed foo.pyx
生成的),因此命令行如下所示:
x86_64-w64-mingw32-gcc foo.c -DMS_WIN64 -O2 <other_options> -I <path_to_python_include> -L <path_to_python_dll> -lpython37 -o foo.exe -municode
有两个重要区别:
- 不应再使用
-shared
,因为结果不再是动态库(这毕竟是*.pyd-file),而是可执行文件。 -municode
是必需的,因为对于Windows,Cython定义的是int wmain(int argc, wchar_t **argv)
,而不是int main(int argc, char** argv)
。如果没有此选项,则会出现类似 的错误消息
/build/mingw-w64-_1w3Xm/mingw-w64-4.0.4/mingw-w64-crt/crt/crt0_c.c:18: undefined reference to 'WinMain'
collect2: error: ld returned 1 exit status
将出现(有关详细信息,请参阅此SO-post)。
注意:要运行生成的可执行文件,需要完整的python分发(不仅是dll)(另请参阅SO-post),否则生成的可执行文件将中止,并返回错误(找不到pythondll,或者是python安装包或站点包-取决于必须运行exe的计算机的配置)。
mingw-w64
也可以在Linux上用于Windows的交叉编译,请参阅SO-post。
相关文章