使用调试 Python 安装在 Windows 上构建 Python-C-Extension

Building a Python-C-Extension on Windows with a debug Python installation

如果我在 Windows 上从源代码构建 CPython,当我想 pip 安装包含 C-Extension 的包时遇到问题。链接库时似乎发生了错误。

例如安装cython时(但在其他C扩展包上也会出现同样的错误):

LINK : fatal error LNK1104: cannot open file 'python38.lib'

error: command 'C:\Program Files (x86)\Microsoft Visual Studio19\Enterprise\VC\Tools\MSVC.23.28105\bin\HostX86\x86\link.exe' failed with exit status 1104

打不开“python38.lib”是因为调试模式下的“.lib”文件调用的是“python38_d.lib”。

一个最小的可重现示例是(在命令行上)基于 Quick Reference of the CPython developer guide:

git clone --branch v3.8.0 https://github.com/python/cpython.git
cd cpython
git checkout v3.8.0
.\PCbuild\build.bat -e -d
.\PCbuild\win32\python_d.exe -m ensurepip
.\PCbuild\win32\python_d.exe -m pip install pip --upgrade -vv
.\PCbuild\win32\python_d.exe -m pip install setuptools --upgrade -vv
.\PCbuild\win32\python_d.exe -m pip install cython -vv

结果distutils.sysconfig.get_config_vars()是:

{'BINDIR': '...\cpython\PCbuild\win32',
 'BINLIBDEST': ...\cpython\Lib',
 'EXE': '.exe',
 'EXT_SUFFIX': '_d.cp38-win32.pyd',
 'INCLUDEPY': '...\cpython\include;...\cpython\PC',
 'LIBDEST': '...\cpython\Lib',
 'SO': '_d.cp38-win32.pyd',
 'VERSION': '38',
 'exec_prefix': '...\cpython',
 'prefix': '...\cpython',
 'srcdir': '...\cpython'}

有什么我想念的吗?是否在 Python 上构建 C-Extensions-debug 构建在 Windows 上根本不受支持?如果支持:我该怎么做?

针对 pythonXY.lib 的链接在 Windows 上有点偷偷摸摸。当您查看 linking 的命令行时,您会看到没有 python-library 传递给 linker,即“link.exe”。注意:这也是 Linux 的情况,但在 Linux 上则不必,因为所需的符号将由 python-可执行文件提供。

然而,很容易to check via dumpbin /dependents resulting.pyd,有对pythonXY.dll的依赖,同时将extra_link_args = ["/VERBOSE:LIB"]添加到扩展定义并触发详细模式linker 会显示 linker 使用 pythonXY.lib.

偷偷摸摸的部分:Microsoft Compler 有一个 convinience-pragma #pragma comment(lib, ...) to automatically trigger linking of a library, which is also used in Python-headers:

#               if defined(_MSC_VER)
                        /* So MSVC users need not specify the .lib
                        file in their Makefile (other compilers are
                        generally taken care of by distutils.) */
#                       if defined(_DEBUG)
#                               pragma comment(lib,"python39_d.lib")
#                       elif defined(Py_LIMITED_API)
#                               pragma comment(lib,"python3.lib")
#                       else
#                               pragma comment(lib,"python39.lib")
#                       endif /* _DEBUG */
#               endif /* _MSC_VER */

如你所见,要link针对debug版本,需要定义_DEBUG.

_DEBUGdistutils 在 Windows 上自动定义,如果 build_ext 使用选项 --debug 调用,例如

python setup.py build_ext -i --debug

可以 translated to pip

pip install --global-option build --global-option --debug XXXXX

可以大致解释为:在安装之前触发 build 命令(其中还包括 build_ext-命令)和选项 --debug


构建调试 C 扩展时的另一个巧妙之处是 more to it on Windows:

#ifdef _DEBUG
#       define Py_DEBUG
#endif

定义 Py_DEBUG 宏意味着不兼容的 ABI until Python3.8, because it also assumed Py_TRACE_REFS 这会导致 PyObject 的内存布局不同,并且在发布模式中缺少一些附加功能。

然而,由于 Python3.8,人们可能可以通过提供缺失的 pythonXY_d.lib/pythonYX.lib 作为 symlink link 到另一个版本。

这段代码有点 hacky,但适用于 MSVC19,允许调试应用程序而无需构建调试 python 库。

#ifdef _DEBUG
#define _DEBUG_WAS_DEFINED
#undef _DEBUG
#endif

#include "Python.h"

#ifdef _DEBUG_WAS_DEFINED
#define _DEBUG
#undef _DEBUG_WAS_DEFINED
#endif