维护层,核验于 2026-09-01。 本指南采用当前受支持的 CPython、PyPA、setuptools 与 Microsoft 工具链流程。文末逐字保留了完整的 2019 年导出内容。当年安装 Visual C++ 2017 后问题消失的结果仅属于历史环境;作者当时明确说明,所提出的 Windows 10 SDK 修复方法尚未亲自验证。
在 Windows 上,CPython C 扩展通常会编译成 .pyd 文件。构建成功需要四个方面彼此匹配:Python 解释器、其架构与 ABI、合适的 MSVC 工具链,以及 Windows SDK/UCRT 的头文件与库。现代项目应在 pyproject.toml 中声明 PEP 517 构建后端,再使用 python -m build 或 python -m pip install .;直接把 python setup.py 当成命令行构建工具的做法已经弃用。
Table of Contents
先弄清楚要构建什么
Python C API 是 CPython 专用接口。如果目标只是调用现成的 C 库,先判断 ctypes 或绑定生成器是否更合适。需要直接访问 CPython API、定义自有 Python 类型或进行紧密集成时,手写扩展才更适用。
本文采用 Windows 官方版 CPython 所支持的 Microsoft 编译器路径,不涉及编译 CPython 解释器本身、不讲从 Linux 交叉编译,也不讲把 MinGW 构建的目标文件与基于 MSVC 的 Python 运行时混用。
安装工具前先确定目标
构建前记录准确的解释器与目标架构:
where.exe python
python -c "import platform, struct, sys; print(sys.executable); print(sys.version); print(platform.machine()); print(8 * struct.calcsize('P'), 'bit')"
python -m pip --version
使用同一条解释器命令完成构建与测试。64 位 x64 Python 需要面向 x64 的工具链;Windows ARM64 与 32 位 Python 是不同的目标。普通 CPython 扩展 Wheel 还会绑定 Python/ABI 标签,除非项目经过有意设计并正确打包为 Limited API 与 Stable ABI。
不要仅凭操作系统推断兼容性。where.exe python 可能显示 Microsoft Store 别名、Conda 环境、虚拟环境,或 PATH 中排在更前的其他安装。
安装 MSVC 与 Windows SDK
使用 Visual Studio Installer 或 Microsoft C++ Build Tools,选择:
- 使用 C++ 的桌面开发;
- 当前受支持的 MSVC C++ x64/x86 构建工具组件;以及
- 当前 Windows SDK,它提供 Universal CRT 头文件与库。
具体工具集和 SDK 组件编号会随时间变化,因此应选择安装器当前提供的受支持组件,不要照抄旧构建日志里的版本号。
安装后打开 Developer PowerShell for Visual Studio,或打开面向解释器架构的本机/交叉工具命令提示符。Microsoft 推荐这些预配置 shell,因为 MSVC 依赖相互协调的 PATH、INCLUDE、LIB 与 LIBPATH。不要手工永久拼装这些环境变量。
验证同一个一致的构建 Shell
在开发者 shell 内进入项目目录,检查当前启用的工具:
where.exe python
where.exe cl
where.exe link
cl /Bv
python -c "import sys, sysconfig; print('base:', sys.base_prefix); print('include:', sysconfig.get_paths()['include']); print('EXT_SUFFIX:', sysconfig.get_config_var('EXT_SUFFIX'))"
$env:INCLUDE -split ';'
cl /Bv 会报告编译器详情。Python 头文件目录应属于 where.exe python 所显示的同一解释器。开发者 shell 中的 INCLUDE 列表应包含 MSVC 与 Windows SDK 路径,其中包括带版本号的 UCRT 目录。
Setuptools 通常能自动发现 Visual Studio,但开发者 shell 更便于诊断架构或 SDK 缺失问题。如果使用 Conda,请在同一个 shell 中激活目标环境并重新运行全部检查。
创建最小项目
在新目录中采用以下结构:
hello-extension/
├── pyproject.toml
└── src/
└── hello.c
发行包名是 hello-extension-example,可导入的扩展模块名则是 hello。两者可以不同,但 Extension 名称、C 初始化符号与 Python 导入名必须彼此一致。
编写 C 扩展
创建 src/hello.c:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
hello_add(PyObject *self, PyObject *args)
{
PyObject *left;
PyObject *right;
(void)self;
if (!PyArg_ParseTuple(args, "OO:add", &left, &right)) {
return NULL;
}
return PyNumber_Add(left, right);
}
static PyMethodDef hello_methods[] = {
{"add", hello_add, METH_VARARGS, PyDoc_STR("Return left + right.")},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef hello_module = {
PyModuleDef_HEAD_INIT,
"hello",
"Minimal CPython extension example.",
-1,
hello_methods
};
PyMODINIT_FUNC
PyInit_hello(void)
{
return PyModule_Create(&hello_module);
}
必须在可能受 CPython 定义影响的系统头文件之前包含 Python.h。PyMODINIT_FUNC 提供必需的导出/链接声明,函数名 PyInit_hello 与导入名 hello 对应。
该示例将两个 Python 对象交给 PyNumber_Add,因此错误会作为普通 Python 异常传播。示例刻意保持简短;生产扩展还要明确处理所有权、错误、模块状态、子解释器策略与自由线程构建策略。
在 pyproject.toml 中声明构建
创建 pyproject.toml:
[build-system]
requires = ["setuptools>=74.1"]
build-backend = "setuptools.build_meta"
[project]
name = "hello-extension-example"
version = "0.1.0"
description = "Minimal CPython C extension example"
requires-python = ">=3.9"
[tool.setuptools]
ext-modules = [
{name = "hello", sources = ["src/hello.c"]}
]
这个版本下限确保 setuptools 支持在 pyproject.toml 中声明扩展模块。构建隔离会把声明的后端安装到临时环境,但不会安装 MSVC 或 Windows SDK;后两者仍是系统工具。
继续用 setup.py 作为 setuptools 配置文件仍受支持,但直接执行 python setup.py build、install 或 bdist_wheel 已被弃用。应优先使用构建前端。
构建、安装与测试
在项目根目录的 Developer PowerShell 中运行:
python -m venv .venv
..venvScriptsActivate.ps1
python -m pip install --upgrade pip build
python -m build
Get-ChildItem .dist*.whl
$wheel = Get-ChildItem .dist*.whl | Sort-Object LastWriteTime | Select-Object -Last 1
python -m pip install --force-reinstall $wheel.FullName
python -c "import hello; print(hello.add(2, 3)); print(hello.__file__)"
预期计算结果为 5,随后显示已安装的 .pyd 路径。默认情况下,python -m build 会先生成 sdist,再从该 sdist 构建 Wheel,这也能检查 C 源文件是否被纳入源码发行包。
需要快速进行本地源码安装时,也可使用:
python -m pip install .
验证发行产物时,应使用全新虚拟环境和干净检出,并记录日志中显示的 Python、pip、构建后端、编译器与 SDK 版本。
理解 Wheel 与 ABI 的适用范围
Wheel 文件名包含 Python、ABI 与平台兼容标签。普通 Windows 扩展构建可能生成以 cp314-cp314-win_amd64.whl 结尾的名称;准确标签取决于解释器和目标。
不要通过改名 Wheel 标签或在 Python 版本之间复制 .pyd 来强行安装。请为每个受支持目标构建并测试,或有意采用 CPython Limited API 并配置 abi3 Wheel。Limited API 的源码设置与 Wheel 标签是两个独立决定,两者都必须正确。
自由线程 CPython 构建也是明确且独立的目标,需要修改扩展并使用单独标签的 Wheel;不要因为常规构建成功就声称支持自由线程版本。
若要覆盖多个 Python 版本与 Windows 架构的发布矩阵,应使用隔离的 CI 运行器——常见选择是 PyPA 的 cibuildwheel——并测试安装后的 Wheel,而不是从源码目录直接导入。
修复 Unable to find vcvarsall.bat
这条消息大多与旧 distutils 时代的发现逻辑或编译器安装不完整有关。当前维护层的处理顺序是:
- 确认项目支持正在使用的 Python 版本。
- 在项目支持的范围内更新构建前端与后端。
- 安装或修改当前 Microsoft C++ Build Tools,加入 C++ 工作负载与 Windows SDK。
- 打开正确的 Developer PowerShell 或本机工具提示符。
- 再次核对
where.exe cl、cl /Bv、解释器与架构。
不要从任意来源下载 vcvarsall.bat,不要从其他 Visual Studio 版本复制它,也不要把猜测出的编译器路径永久写进全局环境。若无人维护的包硬编码了过时编译器,请改用受维护版本、修补其构建配置,或在有意隔离的旧环境中构建,而不是削弱当前机器的安全性与一致性。
2019 年“安装 Visual C++ 2017 后该错误消失”的观察仍在文末存档中,作为当时环境的真实报告,而不是 2026 年的工具链处方。
修复缺失的 io.h 或 Python.h
io.h 属于 Windows SDK 提供的 Universal CRT 头文件。在 Developer PowerShell 中检查环境与已安装头文件:
$env:INCLUDE -split ';'
Get-ChildItem "${env:ProgramFiles(x86)}Windows Kits10Include" -Filter io.h -Recurse | Select-Object -First 5 FullName
python -c "import sysconfig; print(sysconfig.get_paths()['include'])"
若没有 io.h,通过 Visual Studio/Build Tools 安装器添加受支持的 Windows SDK。若文件存在但编译器看不到,请使用正确的开发者 shell 并检查 INCLUDE;不要把头文件复制到 Python 目录。
若缺少 Python.h,先确认准确解释器及其 sysconfig 头文件目录。不要将一套 Python 安装的头文件与另一套的库混用。如果目标 Python 发行版的开发文件不完整,请重新安装或修复该发行版。
诊断链接与导入失败
LNK1112或计算机类型冲突: Python、已编译目标文件和链接器面向不同架构。重新打开正确的本机/交叉工具 shell 并重新构建全部文件。- *缺少
python3XY.lib或存在未解析的Py符号:** 构建使用了错误的 Python 库目录、架构或调试/发布组合。重新核对sysconfig、解释器来源与过期构建产物。 ImportError: dynamic module does not define module export function (PyInit_...): setuptools 扩展名称与PyInit_name符号不一致。ImportError: DLL load failed: 依赖 DLL 缺失或不兼容。找到已安装的.pyd,再从开发者 shell 使用dumpbin /dependents pathtohello.pyd检查。- Wheel 被判定为不受支持: 其 Python、ABI 或平台标签与安装器支持的标签不匹配。请为该目标重新构建,不要重命名文件。
报告失败时,应提供完整的第一条编译器/链接器错误,而不只是最后一行“command failed”。还应提供上文解释器、编译器、架构与构建后端检查结果;若要公开报告,请先移除私有路径。
发布检查清单
分发 Wheel 之前:
- 从干净检出在隔离环境中构建;
- 构建 sdist 与 Wheel,再把生成的 Wheel 安装到全新环境;
- 针对已安装产物运行导入与功能测试;
- 测试所有对外声称支持的 Python、ABI 与 Windows 架构目标;
- 确保 sdist 包含所需的全部 C 源文件、头文件、许可证与生成文件;
- 检查外部 DLL 依赖及其再分发条款;
- 避免嵌入本机绝对路径、凭证或构建机器数据;以及
- 从 CI 使用最小权限凭证发布,并与不可信构建步骤隔离。
本地成功只能证明当前解释器/工具链组合可用,不能证明同一个 Wheel 支持其他 Python 版本、自由线程构建、win32、x64 或 ARM64。
一手官方文档
- Python:使用 C 或 C++ 扩展 CPython
- Python:构建 C 与 C++ 扩展
- Python:Windows 扩展背景
- Python:在 Windows 上使用与编译 Python
- Python:C API 与 ABI 稳定性
- Python:C 扩展与自由线程构建
- PyPA:为何弃用直接执行 `python setup.py`
- PyPA:平台兼容标签
- Setuptools:构建扩展模块
- PyPA build:隔离构建流程
- Microsoft:安装 C 与 C++ 支持
- Microsoft:使用 C++ Build Tools 命令行
- Microsoft:Universal CRT 头文件与库
- PyPA cibuildwheel:构建与测试 Wheel 矩阵
---
2019 年原始导出(逐字保留)
以下是完整原始导出,包含元数据与正文。它发表于 2019-04-23,并在两秒后完成最后修改。作者报告安装 Visual C++ 2017 后第一项错误消失,同时明确说尚未尝试所提出的 Windows 10 SDK 修复方法。本存档内未修改任何文字或标点。
---
id: 1900
title: 'Compile C Extension of Python on Windows'
slug: 'compile-c-extension-of-python-on-windows'
date: '2019-04-23T12:51:39'
modified: '2019-04-23T12:51:41'
status: 'publish'
link: 'https://blog.lazying.art/en/html/computer_internet/python/1900/compile-c-extension-of-python-on-windows.html'
author: 'Lachlan Chen'
categories:
- 'Python'
---
Unable to find vcvarsall.bat
I installed Visual C++ 2017 and the error get eliminated
c:miniconda3includepyconfig.h(59): fatal error C1083: Cannot open include file: ‘io.h’: No such file or directory
Installing Windows 10 SDK can solve this problem. But I have’t tried.
