Skip to content

Instantly share code, notes, and snippets.

@imba-tjd
Last active February 22, 2022 01:15
Show Gist options
  • Save imba-tjd/87738116c497a6ec07af4a137a04c5f2 to your computer and use it in GitHub Desktop.
Save imba-tjd/87738116c497a6ec07af4a137a04c5f2 to your computer and use it in GitHub Desktop.

Win下不安装MSVC使用Cython

vcruntime140.dll:涉及memset, strchr, wcschr等函数会用到

无法使用vcruntime140_app,会报ImportError: DLL load failed

ucrtbase不保证API不变,但是它是单个dll,更简洁;商店应用只能用ucrt,也是最正常的方式

编译用python setup.py build_ext -i -DMS_WIN64,结果在build和同级目录(-i)中,可用-c指定编译器。之后再bdist_wheel能成功,但不能直接bdist_wheel,因为好像不能在此verb的参数中定义宏。在setuptools.Extension中能定义宏,Extension.extra_compile_args能指定编译器,但暂时不想学。

可用pip直接构建wheel:pip wheel . --global-option build_ext --global-option -DMS_WIN64

手动使用gcc命令行

  • 先用cython命令行把py/pyx变成.c
  • 商店版Include的位置:$(python -c "import sysconfig; print(sysconfig.get_path('include')");python3.dll所在位置:sys.base_prefix
  • 在编译到(int)(SIZEOF_VOID_P == sizeof(void*))时失败:SIZEOF_VOID_P定义为4,但指针大小为8。解决办法:64位py要加-DMS_WIN64,32位py要加-m32(因为默认-m64但模块架构位数要和py相同,与系统无关)
  • 提示undefined reference to _imp__PyErr_Format,原因是那些都是头文件,还需要链接到python的实现
  • 原本的dll是vc的,要转换一下。但又要先生成def文件,推荐方法是pexports.exe python39.dll > python39.def,但好像此工具是vc的一部分,未测试。用了MinGW的gendef python39.dll。dlltool -D python39.dll -d python39.def -l libpython39.a 根据文档只有py2需要这样做
  • 编译时加上-L和-lpython39~~,但最终失败,ld.exe: skipping incompatible xxx~~ 这些不兼容的dll一般是架构位数不对
  • 与include文件夹同级的libs文件夹中也存在一个python39.lib,但也不兼容 好像不应使用它,就是不兼容的
  • 还有人说vcruntime140.dll也要这样做一遍

最终成功的命令

$pybase = $(python -c "print(__import__('sys').base_prefix+'\\')");
gcc -shared -fpic -Ofast -DMS_WIN64 -I ($pybase+"include") -L $pybase -L ($pybase+"libs") -lpython310 test.c -o test.pyd
# 最简化的:gcc -shared -DMS_WIN64 -I ($pybase+"include") -L $pybase -lpython310 test.c
  • 其实这是两步,.c到.o只要-D和-I,-o到pyd要-shared和-L和-l;Linux下直接对c用-shared -fPIC生成.o,再变为静态库用ar rcs xxx.a xxx.o
  • python3.dll少了新特性的函数实现,无法使用
  • 好像正常情况下没有链接$pybase,而是用的libs文件夹,但试了会报缺少某些函数实现 TODO: 研究这是什么情况

去掉msvcrt

-nostdlib的方式无法编译通过,-nolibc无效,-Wl,--exclude-libs,msvcrt.a无效。不打算深入研究,毕竟现在有specs方法了,而且如果MinGW本身是非ucrt编译的,再怎么调也是不安全的

无法识别%z的问题

不知道为什么,在PyOS_snprintf函数中出现的%zd会报unknown conversion type character 'z' in format,但是PyErr_Format里的就不会,而且无论是否定义了__USE_MINGW_ANSI_STDIO都没有改变。使用from cpython cimport array时会出现这样的代码。

其它

也许可以用 https://mingwpy.github.io/ (不更新了)、https://winpython.github.io/ 活着,但好像只是个带有一些包的发行版

Cython表示一定不要用Mingw64,也不要用dlltool生成,那些是被故意忽略的。反正就是各种表示只能用VC,否则就和其它所有东西不兼容:https://github.com/cython/cython/wiki/CythonExtensionsOnWindows#less-useful-information 。维护者还表示除非CPython也是MinGW编译的,否则就不要用它来编译模块。

好像这里有pexports:https://sourceforge.net/projects/mingw/files/MinGW/Extension/pexports/pexports-0.47/

正常情况下可用CFLAGS和LDFLAGS设置编译和链接的选项,但MSVC和MinGW(compiler_type为cygwin)都不支持,distutils没有读取环境变量:https://github.com/pypa/setuptools/blob/main/setuptools/_distutils/sysconfig.py#L191

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment