Windows 下再次成功复现编译并打包 MMCV 2.2.0 CUDA 算子 [torch 2.9.1+cu130] 复盘
Windows 下再次成功复现编译并打包 MMCV 2.2.0 CUDA 算子 [torch 2.9.1+cu130] 复盘
本文记录 LiveTalking 项目中在 Windows 下成功编译、验证、打包 mmcv 2.2.0 CUDA 算子的完整过程。目标是让后续遇到同类环境时,可以照着脚本和检查项复现,而不是重新试错。

如果您无法完成编译,请参考纯 Python+CPU 安装方法
MMCV Python 3.12 安装记录
在旧版组件下编译
[torch 2.7.1+cu126]
我们如何在 Windows 上成功编译 MMCV CUDA 算子
MMCV 编译前必做
MMCV 预防性短路径编译策略
多版本 CUDA+cuDNN 共存及切换策略
Windows CMD 多版本 CUDA & cuDNN 一键切换管理方案
Windows 多版本 CUDA + cuDNN 环境配置完全指南
Windows 本地编译 CUDA 扩展 Wheel 完全指南
Windows 本地编译 CUDA Extension Wheel 完全指南
最终结果
本次已经完成三件事:
- 成功编译
mmcv._extCUDA 扩展。 - 成功通过 MMCV 基础导入、扩展导入、CUDA ops、图像 I/O、MMEngine Config 验证。
- 成功打包 wheel 并保存依赖到
wheelhouse。
成功产物:
J:\PythonProjects4\LiveTalking\mmcv\mmcv\_ext.cp312-win_amd64.pyd
J:\PythonProjects4\LiveTalking\wheelhouse\mmcv-2.2.0-cp312-cp312-win_amd64.whl
J:\PythonProjects4\LiveTalking\wheelhouse\requirements-lock.txt
成功日志:
J:\PythonProjects4\LiveTalking\logs\build_mmcv_20260606_235910.log
J:\PythonProjects4\LiveTalking\logs\pack_mmcv_wheel_20260607_004534.log

验证命令和结果:
.\.venv\Scripts\python.exe -c "import mmcv; print(mmcv.__version__); print(mmcv.__file__)"
2.2.0
J:\PythonProjects4\LiveTalking\mmcv\mmcv\__init__.py

验证方法请见下文的第 8 章
我们如何在 Windows 上成功编译 MMCV CUDA 算子
.\.venv\Scripts\python.exe -c "import mmcv._ext; print('MMCV ext: OK')"
.\.venv\Scripts\python.exe -c "from mmcv.ops import DeformConv2d; print('CUDA ops: OK')"
.\.venv\Scripts\python.exe -c "from mmcv import imread; print('Image I/O: OK')"
.\.venv\Scripts\python.exe -c "from mmengine.config import Config; print('MMEngine Config: OK')"
验证扩展模块
验证 CUDA 算子
验证图像 I/O
验证 Config 系统
MMCV ext: OK
CUDA ops: OK
Image I/O: OK
MMEngine Config: OK

成功环境
项目根目录:
J:\PythonProjects4\LiveTalking
本地源码:
cd J:\PythonProjects4\LiveTalking\
git clone https://github.com/open-mmlab/mmcv.git
cd mmcv
Python / PyTorch / MMCV:
Python 3.12.11
torch 2.9.1+cu130
torch.version.cuda = 13.0
mmcv 2.2.0
CUDA Toolkit:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0
nvcc release 13.0, V13.0.88
cuDNN:
C:\Program Files\NVIDIA\CUDNN\v9.14
C:\Program Files\NVIDIA\CUDNN\v9.14\bin\13.0
C:\Program Files\NVIDIA\CUDNN\v9.14\include
Visual Studio / MSVC:
C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Auxiliary\Build\vcvars64.bat
C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Tools\MSVC\14.50.35717
cl version 19.50.35721
GPU 架构:
TORCH_CUDA_ARCH_LIST=8.6
一键编译
建议从普通 cmd.exe 打开,不要从 VS2022 Developer Prompt 打开。脚本会自己调用正确的 VS 2026 Insiders vcvars64.bat。
cd /d J:\PythonProjects4\LiveTalking
.venv\Scripts\activate.bat
subst Z: /D
subst Z: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
subst
rmdir /s /q mmcv\build
tools\build_mmcv.cmd
如果 subst Z: /D 或 rmdir /s /q mmcv\build 提示不存在,可以忽略。
编译前应确认:
subst
期望至少包含:
M:\: => J:\PythonProjects4\LiveTalking\mmcv
Z:\: => C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0
tools\build_mmcv.cmd 会自动保存日志:
J:\PythonProjects4\LiveTalking\logs\build_mmcv_YYYYMMDD_HHMMSS.log
tools\build_mmcv.cmd全量脚本示例:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "ROOT=J:\PythonProjects4\LiveTalking"
set "LOG_DIR=%ROOT%\logs"
set "MMCV_ROOT=%ROOT%\mmcv"
set "PYTHON_EXE=%ROOT%\.venv\Scripts\python.exe"
set "CUDA_ROOT=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
set "CUDA_DRIVE=Z:"
set "CUDA_SHORT=Z:\"
set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Auxiliary\Build\vcvars64.bat"
if not "%~1"=="--logged" (
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"') do set "LOG_TS=%%i"
set "LOG_FILE=%LOG_DIR%\build_mmcv_!LOG_TS!.log"
echo Writing build log: !LOG_FILE!
call "%~f0" --logged > "!LOG_FILE!" 2>&1
set "BUILD_EXIT=%ERRORLEVEL%"
type "!LOG_FILE!"
echo.
echo Build log saved: !LOG_FILE!
exit /b !BUILD_EXIT!
)
if not exist "%PYTHON_EXE%" (
echo Python not found: %PYTHON_EXE%
exit /b 1
)
if not exist "%VCVARS%" (
echo vcvars64.bat not found: %VCVARS%
exit /b 1
)
if not exist "%CUDA_ROOT%\bin\nvcc.exe" (
echo nvcc not found: %CUDA_ROOT%\bin\nvcc.exe
exit /b 1
)
subst M: "%MMCV_ROOT%" >nul 2>nul
subst %CUDA_DRIVE% "%CUDA_ROOT%" >nul 2>nul
if not exist "M:\tmp" mkdir "M:\tmp"
if not exist "M:\torch_ext" mkdir "M:\torch_ext"
if not exist "M:\cuda_cache" mkdir "M:\cuda_cache"
if not exist "M:\pip_cache" mkdir "M:\pip_cache"
if not exist "%CUDA_DRIVE%\bin\nvcc.exe" (
echo nvcc not found after subst: %CUDA_DRIVE%\bin\nvcc.exe
exit /b 1
)
set "INCLUDE="
set "LIB="
set "LIBPATH="
set "VSCMD_VER="
set "VSCMD_ARG_TGT_ARCH="
set "VSCMD_ARG_HOST_ARCH="
set "VSCMD_ARG_APP_PLAT="
set "VSCMD_START_DIR="
set "VSINSTALLDIR="
set "VCINSTALLDIR="
set "VCToolsInstallDir="
set "WindowsSdkDir="
set "WindowsLibPath="
set "UniversalCRTSdkDir="
set "UCRTVersion="
call "%VCVARS%" || exit /b 1
set "DISTUTILS_USE_SDK=1"
set "MSSdk=1"
set "CUDA_HOME=%CUDA_SHORT%"
set "CUDA_PATH=%CUDA_SHORT%"
set "CUDA_ROOT=%CUDA_SHORT%"
set "CudaToolkitDir=%CUDA_SHORT%"
set "CUDNN_HOME=C:\Program Files\NVIDIA\CUDNN\v9.14"
set "CUDNN_PATH=C:\Program Files\NVIDIA\CUDNN\v9.14\bin\13.0"
set "PATH=%CUDA_DRIVE%\bin;C:\Program Files\Microsoft Visual Studio\18\Insiders\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;%PATH%"
set "TMP=M:\tmp"
set "TEMP=M:\tmp"
set "TORCH_EXTENSIONS_DIR=M:\torch_ext"
set "CUDA_CACHE_PATH=M:\cuda_cache"
set "PIP_CACHE_DIR=M:\pip_cache"
set "MMCV_CUDA_ARGS=-allow-unsupported-compiler"
set "MAX_JOBS=1"
set "TORCH_CUDA_ARCH_LIST=8.6"
cd /d M:\
echo Using python: %PYTHON_EXE%
echo Using CUDA_HOME: %CUDA_HOME%
where nvcc
where cl
cl /Bv
echo Using tmp: %TMP%
echo Using torch extensions cache: %TORCH_EXTENSIONS_DIR%
echo Using CUDA cache: %CUDA_CACHE_PATH%
call "%PYTHON_EXE%" setup.py build_ext --inplace -j 1
set "BUILD_EXIT=%ERRORLEVEL%"
exit /b %BUILD_EXIT%

一键打包 wheel
编译成功并验证通过后,运行:
rmdir /s /q mmcv\build
tools\pack_mmcv_wheel.cmd
tools\pack_mmcv_wheel.cmd全量脚本示例:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "ROOT=J:\PythonProjects4\LiveTalking"
set "LOG_DIR=%ROOT%\logs"
set "MMCV_ROOT=%ROOT%\mmcv"
set "PYTHON_EXE=%ROOT%\.venv\Scripts\python.exe"
set "CUDA_ROOT=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
set "CUDA_DRIVE=Z:"
set "CUDA_SHORT=Z:\"
set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Auxiliary\Build\vcvars64.bat"
set "WHEELHOUSE=%ROOT%\wheelhouse"
set "NINJA_DIR=C:\Program Files\Microsoft Visual Studio\18\Insiders\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja"
if not "%~1"=="--logged" (
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"') do set "LOG_TS=%%i"
set "LOG_FILE=%LOG_DIR%\pack_mmcv_wheel_!LOG_TS!.log"
echo Writing wheel pack log: !LOG_FILE!
call "%~f0" --logged > "!LOG_FILE!" 2>&1
set "PACK_EXIT=%ERRORLEVEL%"
type "!LOG_FILE!"
echo.
echo Wheel pack log saved: !LOG_FILE!
exit /b !PACK_EXIT!
)
if not exist "%PYTHON_EXE%" (
echo Python not found: %PYTHON_EXE%
exit /b 1
)
if not exist "%VCVARS%" (
echo vcvars64.bat not found: %VCVARS%
exit /b 1
)
if not exist "%CUDA_ROOT%\bin\nvcc.exe" (
echo nvcc not found: %CUDA_ROOT%\bin\nvcc.exe
exit /b 1
)
if not exist "%WHEELHOUSE%" mkdir "%WHEELHOUSE%"
subst M: "%MMCV_ROOT%" >nul 2>nul
subst %CUDA_DRIVE% "%CUDA_ROOT%" >nul 2>nul
if not exist "M:\tmp" mkdir "M:\tmp"
if not exist "M:\torch_ext" mkdir "M:\torch_ext"
if not exist "M:\cuda_cache" mkdir "M:\cuda_cache"
if not exist "M:\pip_cache" mkdir "M:\pip_cache"
if not exist "%CUDA_DRIVE%\bin\nvcc.exe" (
echo nvcc not found after subst: %CUDA_DRIVE%\bin\nvcc.exe
exit /b 1
)
set "INCLUDE="
set "LIB="
set "LIBPATH="
set "VSCMD_VER="
set "VSCMD_ARG_TGT_ARCH="
set "VSCMD_ARG_HOST_ARCH="
set "VSCMD_ARG_APP_PLAT="
set "VSCMD_START_DIR="
set "VSINSTALLDIR="
set "VCINSTALLDIR="
set "VCToolsInstallDir="
set "WindowsSdkDir="
set "WindowsLibPath="
set "UniversalCRTSdkDir="
set "UCRTVersion="
call "%VCVARS%" || exit /b 1
set "DISTUTILS_USE_SDK=1"
set "MSSdk=1"
set "CUDA_HOME=%CUDA_SHORT%"
set "CUDA_PATH=%CUDA_SHORT%"
set "CUDA_ROOT=%CUDA_SHORT%"
set "CudaToolkitDir=%CUDA_SHORT%"
set "CUDNN_HOME=C:\Program Files\NVIDIA\CUDNN\v9.14"
set "CUDNN_PATH=C:\Program Files\NVIDIA\CUDNN\v9.14\bin\13.0"
set "PATH=%CUDA_DRIVE%\bin;%NINJA_DIR%;%PATH%"
set "TMP=M:\tmp"
set "TEMP=M:\tmp"
set "TORCH_EXTENSIONS_DIR=M:\torch_ext"
set "CUDA_CACHE_PATH=M:\cuda_cache"
set "PIP_CACHE_DIR=M:\pip_cache"
set "MMCV_CUDA_ARGS=-allow-unsupported-compiler"
set "MAX_JOBS=1"
set "TORCH_CUDA_ARCH_LIST=8.6"
cd /d M:\ || exit /b 1
echo Using python: %PYTHON_EXE%
echo Using CUDA_HOME: %CUDA_HOME%
where nvcc
where cl
cl /Bv
echo Using tmp: %TMP%
echo Using torch extensions cache: %TORCH_EXTENSIONS_DIR%
echo Using CUDA cache: %CUDA_CACHE_PATH%
echo Wheel output: %WHEELHOUSE%
call "%PYTHON_EXE%" setup.py build_ext --inplace -j 1 || exit /b 1
call "%PYTHON_EXE%" setup.py bdist_wheel --dist-dir "%WHEELHOUSE%" || exit /b 1
echo Downloading build/runtime dependencies...
call "%PYTHON_EXE%" -m pip download -d "%WHEELHOUSE%" ^
addict ^
mmengine ^
numpy ^
opencv-python ^
packaging ^
Pillow ^
pyyaml ^
regex ^
yapf ^
setuptools ^
wheel ^
Cython ^
ninja || exit /b 1
call "%PYTHON_EXE%" -m pip freeze > "%WHEELHOUSE%\requirements-lock.txt" || exit /b 1
echo.
echo Saved files to: %WHEELHOUSE%
echo Note: torch and CUDA are not bundled by this script.
exit /b 0
脚本会:
- 复用成功编译环境。
- 重新执行
build_ext --inplace。 - 执行
bdist_wheel。 - 下载构建和运行依赖到
wheelhouse。 - 输出
requirements-lock.txt。
成功后确认:
dir wheelhouse

关键文件:
mmcv-2.2.0-cp312-cp312-win_amd64.whl
requirements-lock.txt

打包日志:
J:\PythonProjects4\LiveTalking\logs\pack_mmcv_wheel_YYYYMMDD_HHMMSS.log
离线或半离线安装思路
在目标环境中,如果 torch / CUDA 已经按同版本准备好,可以使用:
pip install --no-index --find-links wheelhouse mmcv
或者直接安装 wheel:
pip install wheelhouse\mmcv-2.2.0-cp312-cp312-win_amd64.whl
注意:当前 pack_mmcv_wheel.cmd 明确不保存 torch 和 CUDA Toolkit。torch 与 CUDA 需要按项目目标环境单独安装和匹配。

关键脚本
本次最终使用两个脚本:
tools\build_mmcv.cmd
tools\pack_mmcv_wheel.cmd
它们的核心策略一致:
set "CUDA_ROOT=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
set "CUDA_DRIVE=Z:"
set "CUDA_SHORT=Z:\"
set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Auxiliary\Build\vcvars64.bat"
并显式设置:
set "DISTUTILS_USE_SDK=1"
set "MSSdk=1"
set "CUDA_HOME=Z:\"
set "CUDA_PATH=Z:\"
set "CUDA_ROOT=Z:\"
set "CudaToolkitDir=Z:\"
set "CUDNN_HOME=C:\Program Files\NVIDIA\CUDNN\v9.14"
set "CUDNN_PATH=C:\Program Files\NVIDIA\CUDNN\v9.14\bin\13.0"
set "TMP=M:\tmp"
set "TEMP=M:\tmp"
set "TORCH_EXTENSIONS_DIR=M:\torch_ext"
set "CUDA_CACHE_PATH=M:\cuda_cache"
set "PIP_CACHE_DIR=M:\pip_cache"
set "MMCV_CUDA_ARGS=-allow-unsupported-compiler"
set "MAX_JOBS=1"
set "TORCH_CUDA_ARCH_LIST=8.6"
短路径映射策略
Windows 下编译 CUDA 扩展时,真正危险的不只是 CUDA 安装路径,而是构建系统拼出来的长路径,例如:
J:\PythonProjects4\LiveTalking\mmcv\build\temp.win-amd64-cpython-312\Release\mmcv\ops\csrc\pytorch\cuda\fused_spconv_ops_cuda.obj
nvcc + cl + ninja 在处理深层目标文件、依赖文件、临时文件时容易出现路径过长或写入失败。
本次采用的短路径:
subst M: J:\PythonProjects4\LiveTalking\mmcv
subst Z: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
映射后:
源码根目录: M:\
构建目录: M:\build\...
临时目录: M:\tmp
PyTorch 扩展缓存: M:\torch_ext
CUDA 缓存: M:\cuda_cache
pip 缓存: M:\pip_cache
CUDA: Z:\
nvcc: Z:\bin\nvcc.exe
include: Z:\include
这能同时缩短:
- 源码路径。
- 构建输出路径。
- 临时文件路径。
- CUDA Toolkit 路径。
- PyTorch / CUDA / pip 缓存路径。
为什么 CUDA_HOME=Z:\ 而不是 CUDA_HOME=Z:
之前脚本使用裸盘符:
set "CUDA_HOME=Z:"
PyTorch 的 torch.utils.cpp_extension 在拼路径时可能生成:
Z:bin\nvcc
Z:include
这不是我们想要的绝对路径。最终改为:
set "CUDA_HOME=Z:\"
set "CUDA_PATH=Z:\"
set "CUDA_ROOT=Z:\"
set "CudaToolkitDir=Z:\"
这样编译命令稳定生成:
Z:\bin\nvcc
Z:\include
为什么要清理 VS 环境变量
如果从 VS2022 Developer Prompt 启动,环境里可能已经存在:
INCLUDE
LIB
LIBPATH
VSCMD_VER
VSINSTALLDIR
VCINSTALLDIR
VCToolsInstallDir
WindowsSdkDir
即使脚本后续调用 VS 2026 vcvars64.bat,旧的 VS2022 include/lib 仍可能混入编译命令,导致工具链错位。
因此脚本在调用 vcvars64.bat 前先清理:
set "INCLUDE="
set "LIB="
set "LIBPATH="
set "VSCMD_VER="
set "VSCMD_ARG_TGT_ARCH="
set "VSCMD_ARG_HOST_ARCH="
set "VSCMD_ARG_APP_PLAT="
set "VSCMD_START_DIR="
set "VSINSTALLDIR="
set "VCINSTALLDIR="
set "VCToolsInstallDir="
set "WindowsSdkDir="
set "WindowsLibPath="
set "UniversalCRTSdkDir="
set "UCRTVersion="
call "%VCVARS%" || exit /b 1
验证重点:
where cl
cl /Bv
成功时应看到:
C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\cl.exe
而不是:
D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.42.34433\...
mmcv/setup.py 的关键修改
本次编译不是只靠环境变量完成,还修改了 mmcv/setup.py 的构建入口。
1. 允许控制 Ninja
原始代码直接使用:
cmd_class = {'build_ext': BuildExtension}
修改为:
use_ninja = os.getenv('MMCV_USE_NINJA', '1') != '0'
if hasattr(BuildExtension, 'with_options'):
cmd_class = {'build_ext': BuildExtension.with_options(use_ninja=use_ninja)}
else:
cmd_class = {'build_ext': BuildExtension}
作用:
- 保持默认使用 Ninja。
- 必要时可以通过
MMCV_USE_NINJA=0禁用 Ninja 做排错。
2. Windows CUDA 编译时补 USE_CUDA
PyTorch 2.9.1 的头文件 torch/csrc/dynamo/compiled_autograd.h 中有 Windows CUDA 保护分支:
#if defined(_WIN32) && (defined(USE_CUDA) || defined(USE_ROCM))
但 MMCV 原始构建只定义了:
MMCV_WITH_CUDA
没有定义:
USE_CUDA
结果 fused_spconv_ops_cuda.cu 编译时进入了不适合 Windows CUDA 的模板分支,触发:
torch/csrc/dynamo/compiled_autograd.h(1134): error C2872: "std"
最终在 setup.py 的 CUDA 分支中加入:
if platform.system() == 'Windows':
define_macros += [('USE_CUDA', None)]
成功后失败命令中应出现:
-DUSE_CUDA
这个补丁是本次 torch 2.9.1+cu130 + CUDA 13.0 + Windows + MMCV 2.2.0 成功编译的关键。
mmcv/setup.py全量脚本示例:
import glob
import os
import platform
import re
from pkg_resources import DistributionNotFound, get_distribution, parse_version
from setuptools import find_packages, setup
EXT_TYPE = ''
try:
import torch
if torch.__version__ == 'parrots':
from parrots.utils.build_extension import BuildExtension
EXT_TYPE = 'parrots'
elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \
os.getenv('FORCE_MLU', '0') == '1':
from torch_mlu.utils.cpp_extension import BuildExtension
EXT_TYPE = 'pytorch'
elif (hasattr(torch, 'is_musa_available') and torch.is_musa_available()) \
or os.getenv('FORCE_MUSA', '0') == '1':
from torch_musa.utils.musa_extension import BuildExtension
EXT_TYPE = 'pytorch'
else:
from torch.utils.cpp_extension import BuildExtension
EXT_TYPE = 'pytorch'
use_ninja = os.getenv('MMCV_USE_NINJA', '1') != '0'
if hasattr(BuildExtension, 'with_options'):
cmd_class = {'build_ext': BuildExtension.with_options(use_ninja=use_ninja)}
else:
cmd_class = {'build_ext': BuildExtension}
except ModuleNotFoundError:
cmd_class = {}
print('Skip building ext ops due to the absence of torch.')
def choose_requirement(primary, secondary):
"""If some version of primary requirement installed, return primary, else
return secondary."""
try:
name = re.split(r'[!<>=]', primary)[0]
get_distribution(name)
except DistributionNotFound:
return secondary
return str(primary)
def get_version():
version_file = 'mmcv/version.py'
with open(version_file, encoding='utf-8') as f:
exec(compile(f.read(), version_file, 'exec'))
return locals()['__version__']
def parse_requirements(fname='requirements/runtime.txt', with_version=True):
"""Parse the package dependencies listed in a requirements file but strips
specific versioning information.
Args:
fname (str): path to requirements file
with_version (bool, default=False): if True include version specs
Returns:
List[str]: list of requirements items
CommandLine:
python -c "import setup; print(setup.parse_requirements())"
"""
import sys
from os.path import exists
require_fpath = fname
def parse_line(line):
"""Parse information from a line in a requirements text file."""
if line.startswith('-r '):
# Allow specifying requirements in other files
target = line.split(' ')[1]
for info in parse_require_file(target):
yield info
else:
info = {'line': line}
if line.startswith('-e '):
info['package'] = line.split('#egg=')[1]
else:
# Remove versioning from the package
pat = '(' + '|'.join(['>=', '==', '>']) + ')'
parts = re.split(pat, line, maxsplit=1)
parts = [p.strip() for p in parts]
info['package'] = parts[0]
if len(parts) > 1:
op, rest = parts[1:]
if ';' in rest:
# Handle platform specific dependencies
# http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies
version, platform_deps = map(str.strip,
rest.split(';'))
info['platform_deps'] = platform_deps
else:
version = rest # NOQA
info['version'] = (op, version)
yield info
def parse_require_file(fpath):
with open(fpath) as f:
for line in f.readlines():
line = line.strip()
if line and not line.startswith('#'):
yield from parse_line(line)
def gen_packages_items():
if exists(require_fpath):
for info in parse_require_file(require_fpath):
parts = [info['package']]
if with_version and 'version' in info:
parts.extend(info['version'])
if not sys.version.startswith('3.4'):
# apparently package_deps are broken in 3.4
platform_deps = info.get('platform_deps')
if platform_deps is not None:
parts.append(';' + platform_deps)
item = ''.join(parts)
yield item
packages = list(gen_packages_items())
return packages
install_requires = parse_requirements()
try:
# OpenCV installed via conda.
import cv2 # NOQA: F401
major, minor, *rest = cv2.__version__.split('.')
if int(major) < 3:
raise RuntimeError(
f'OpenCV >=3 is required but {cv2.__version__} is installed')
except ImportError:
# If first not installed install second package
CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3',
'opencv-python>=3')]
for main, secondary in CHOOSE_INSTALL_REQUIRES:
install_requires.append(choose_requirement(main, secondary))
def get_extensions():
extensions = []
if os.getenv('MMCV_WITH_OPS', '1') == '0':
return extensions
if EXT_TYPE == 'parrots':
ext_name = 'mmcv._ext'
from parrots.utils.build_extension import Extension
# new parrots op impl do not use MMCV_USE_PARROTS
# define_macros = [('MMCV_USE_PARROTS', None)]
define_macros = []
include_dirs = []
op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/parrots/*.cpp')
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda'))
op_files.remove('./mmcv/ops/csrc/pytorch/cuda/iou3d_cuda.cu')
op_files.remove('./mmcv/ops/csrc/pytorch/cpu/bbox_overlaps_cpu.cpp')
op_files.remove('./mmcv/ops/csrc/pytorch/cuda/bias_act_cuda.cu')
cuda_args = os.getenv('MMCV_CUDA_ARGS')
extra_compile_args = {
'nvcc': [cuda_args, '-std=c++14'] if cuda_args else ['-std=c++14'],
'cxx': ['-std=c++14'],
}
if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1':
define_macros += [('MMCV_WITH_CUDA', None)]
extra_compile_args['nvcc'] += [
'-D__CUDA_NO_HALF_OPERATORS__',
'-D__CUDA_NO_HALF_CONVERSIONS__',
'-D__CUDA_NO_HALF2_OPERATORS__',
]
ext_ops = Extension(
name=ext_name,
sources=op_files,
include_dirs=include_dirs,
define_macros=define_macros,
extra_compile_args=extra_compile_args,
cuda=True,
pytorch=True)
extensions.append(ext_ops)
elif EXT_TYPE == 'pytorch':
ext_name = 'mmcv._ext'
from torch.utils.cpp_extension import CppExtension, CUDAExtension
# prevent ninja from using too many resources
try:
import psutil
num_cpu = len(psutil.Process().cpu_affinity())
cpu_use = max(4, num_cpu - 1)
except (ModuleNotFoundError, AttributeError):
cpu_use = 4
os.environ.setdefault('MAX_JOBS', str(cpu_use))
define_macros = []
# Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a
# required key passed to PyTorch. Even if there is no flag passed
# to cxx, users also need to pass an empty list to PyTorch.
# Since PyTorch1.8.0, it has a default value so users do not need
# to pass an empty list anymore.
# More details at https://github.com/pytorch/pytorch/pull/45956
extra_compile_args = {'cxx': []}
if platform.system() != 'Windows':
if parse_version(torch.__version__) <= parse_version('1.12.1'):
extra_compile_args['cxx'] = ['-std=c++14']
else:
extra_compile_args['cxx'] = ['-std=c++17']
else:
if parse_version(torch.__version__) <= parse_version('1.12.1'):
extra_compile_args['cxx'] = ['/std:c++14']
else:
extra_compile_args['cxx'] = ['/std:c++17']
include_dirs = []
library_dirs = []
libraries = []
extra_objects = []
extra_link_args = []
is_rocm_pytorch = False
try:
from torch.utils.cpp_extension import ROCM_HOME
is_rocm_pytorch = True if ((torch.version.hip is not None) and
(ROCM_HOME is not None)) else False
except ImportError:
pass
if os.getenv('MMCV_WITH_DIOPI', '0') == '1':
import mmengine # NOQA: F401
from mmengine.utils.version_utils import digit_version
assert digit_version(mmengine.__version__) >= digit_version(
'0.7.4'), f'mmengine >= 0.7.4 is required \
but {mmengine.__version__} is installed'
print(f'Compiling {ext_name} with CPU and DIPU')
define_macros += [('MMCV_WITH_DIOPI', None)]
define_macros += [('DIOPI_ATTR_WEAK', None)]
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp')
extension = CppExtension
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
dipu_root = os.getenv('DIPU_ROOT')
diopi_path = os.getenv('DIOPI_PATH')
dipu_path = os.getenv('DIPU_PATH')
vendor_include_dirs = os.getenv('VENDOR_INCLUDE_DIRS')
nccl_include_dirs = os.getenv('NCCL_INCLUDE_DIRS')
pytorch_dir = os.getenv('PYTORCH_DIR')
include_dirs.append(dipu_root)
include_dirs.append(diopi_path + '/include')
include_dirs.append(dipu_path + '/dist/include')
include_dirs.append(vendor_include_dirs)
include_dirs.append(pytorch_dir + 'torch/include')
if nccl_include_dirs:
include_dirs.append(nccl_include_dirs)
library_dirs += [dipu_root]
libraries += ['torch_dipu']
elif is_rocm_pytorch or torch.cuda.is_available() or os.getenv(
'FORCE_CUDA', '0') == '1':
if is_rocm_pytorch:
define_macros += [('MMCV_WITH_HIP', None)]
define_macros += [('MMCV_WITH_CUDA', None)]
if platform.system() == 'Windows':
define_macros += [('USE_CUDA', None)]
cuda_args = os.getenv('MMCV_CUDA_ARGS')
extra_compile_args['nvcc'] = [cuda_args] if cuda_args else []
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \
glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp')
extension = CUDAExtension
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/pytorch'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda'))
elif (hasattr(torch, 'is_mlu_available') and
torch.is_mlu_available()) or \
os.getenv('FORCE_MLU', '0') == '1':
from torch_mlu.utils.cpp_extension import MLUExtension
def get_mluops_version(file_path):
with open(file_path) as f:
for line in f:
if re.search('MLUOP_MAJOR', line):
major = line.strip().split(' ')[2]
if re.search('MLUOP_MINOR', line):
minor = line.strip().split(' ')[2]
if re.search('MLUOP_PATCHLEVEL', line):
patchlevel = line.strip().split(' ')[2]
mluops_version = f'v{major}.{minor}.{patchlevel}'
return mluops_version
mmcv_mluops_version = get_mluops_version(
'./mmcv/ops/csrc/pytorch/mlu/mlu_common_helper.h')
mlu_ops_path = os.getenv('MMCV_MLU_OPS_PATH')
if mlu_ops_path:
exists_mluops_version = get_mluops_version(
mlu_ops_path + '/bangc-ops/mlu_op.h')
if exists_mluops_version != mmcv_mluops_version:
print('the version of mlu-ops provided is %s,'
' while %s is needed.' %
(exists_mluops_version, mmcv_mluops_version))
exit()
try:
if os.path.exists('mlu-ops'):
if os.path.islink('mlu-ops'):
os.remove('mlu-ops')
os.symlink(mlu_ops_path, 'mlu-ops')
elif os.path.abspath('mlu-ops') != mlu_ops_path:
os.symlink(mlu_ops_path, 'mlu-ops')
else:
os.symlink(mlu_ops_path, 'mlu-ops')
except Exception:
raise FileExistsError(
'mlu-ops already exists, please move it out,'
'or rename or remove it.')
else:
if not os.path.exists('mlu-ops'):
import requests
mluops_url = 'https://github.com/Cambricon/mlu-ops/' + \
'archive/refs/tags/' + mmcv_mluops_version + '.zip'
req = requests.get(mluops_url)
with open('./mlu-ops.zip', 'wb') as f:
try:
f.write(req.content)
except Exception:
raise ImportError('failed to download mlu-ops')
from zipfile import BadZipFile, ZipFile
with ZipFile('./mlu-ops.zip', 'r') as archive:
try:
archive.extractall()
dir_name = archive.namelist()[0].split('/')[0]
os.rename(dir_name, 'mlu-ops')
except BadZipFile:
print('invalid mlu-ops.zip file')
else:
exists_mluops_version = get_mluops_version(
'./mlu-ops/bangc-ops/mlu_op.h')
if exists_mluops_version != mmcv_mluops_version:
print('the version of provided mlu-ops is %s,'
' while %s is needed.' %
(exists_mluops_version, mmcv_mluops_version))
exit()
define_macros += [('MMCV_WITH_MLU', None)]
mlu_args = os.getenv('MMCV_MLU_ARGS', '-DNDEBUG ')
mluops_includes = []
mluops_includes.append('-I' +
os.path.abspath('./mlu-ops/bangc-ops'))
mluops_includes.append(
'-I' + os.path.abspath('./mlu-ops/bangc-ops/kernels'))
extra_compile_args['cncc'] = [mlu_args] + \
mluops_includes if mlu_args else mluops_includes
extra_compile_args['cxx'] += ['-fno-gnu-unique']
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/mlu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/common/mlu/*.mlu') + \
glob.glob(
'./mlu-ops/bangc-ops/core/**/*.cpp', recursive=True) + \
glob.glob(
'./mlu-ops/bangc-ops/kernels/**/*.cpp', recursive=True) + \
glob.glob(
'./mlu-ops/bangc-ops/kernels/**/*.mlu', recursive=True)
extra_link_args = [
'-Wl,--whole-archive',
'./mlu-ops/bangc-ops/kernels/kernel_wrapper/lib/libextops.a',
'-Wl,--no-whole-archive'
]
extension = MLUExtension
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/mlu'))
include_dirs.append(os.path.abspath('./mlu-ops/bangc-ops'))
elif (hasattr(torch.backends, 'mps')
and torch.backends.mps.is_available()) or os.getenv(
'FORCE_MPS', '0') == '1':
# objc compiler support
from distutils.unixccompiler import UnixCCompiler
if '.mm' not in UnixCCompiler.src_extensions:
UnixCCompiler.src_extensions.append('.mm')
UnixCCompiler.language_map['.mm'] = 'objc'
define_macros += [('MMCV_WITH_MPS', None)]
extra_compile_args = {}
extra_compile_args['cxx'] = ['-Wall', '-std=c++17']
extra_compile_args['cxx'] += [
'-framework', 'Metal', '-framework', 'Foundation'
]
extra_compile_args['cxx'] += ['-ObjC++']
# src
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp')
# TODO: support mps ops on torch>=2.1.0
if parse_version(torch.__version__) < parse_version('2.1.0'):
op_files += glob.glob('./mmcv/ops/csrc/common/mps/*.mm') + \
glob.glob('./mmcv/ops/csrc/pytorch/mps/*.mm')
extension = CppExtension
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/mps'))
elif (os.getenv('FORCE_NPU', '0') == '1'):
print(f'Compiling {ext_name} only with CPU and NPU')
try:
import importlib
from torch_npu.utils.cpp_extension import NpuExtension
extra_compile_args['cxx'] += [
'-D__FILENAME__=\"$$(notdir $$(abspath $$<))\"'
]
extra_compile_args['cxx'] += [
'-I' + importlib.util.find_spec(
'torch_npu').submodule_search_locations[0] +
'/include/third_party/acl/inc'
]
extra_compile_args['cxx'] += [
'-I' + importlib.util.find_spec(
'torch_npu').submodule_search_locations[0] +
'/include/third_party/hccl/inc'
]
define_macros += [('MMCV_WITH_NPU', None)]
extension = NpuExtension
if parse_version(torch.__version__) < parse_version('2.1.0'):
define_macros += [('MMCV_WITH_XLA', None)]
if parse_version(torch.__version__) >= parse_version('2.1.0'):
define_macros += [('MMCV_WITH_KPRIVATE', None)]
except Exception:
raise ImportError('can not find any torch_npu')
# src
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/common/npu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/npu/*.cpp')
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/npu'))
elif hasattr(torch, 'musa') or os.getenv('FORCE_MUSA', '0') == '1':
from torch_musa.testing import get_musa_arch
from torch_musa.utils.musa_extension import MUSAExtension
define_macros += [('MMCV_WITH_MUSA', None),
('MUSA_ARCH', str(get_musa_arch()))]
os.environ['MUSA_ARCH'] = str(get_musa_arch())
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/musa/*.mu') + \
glob.glob('./mmcv/ops/csrc/pytorch/musa/*.cpp')
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/pytorch'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/musa'))
extension = MUSAExtension
else:
print(f'Compiling {ext_name} only with CPU')
op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \
glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp')
extension = CppExtension
include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common'))
# Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses
# c++14 features, the argument ['std=c++14'] must be added here.
# However, in the windows environment, some standard libraries
# will depend on c++17 or higher. In fact, for the windows
# environment, the compiler will choose the appropriate compiler
# to compile those cpp files, so there is no need to add the
# argument
if 'nvcc' in extra_compile_args and platform.system() != 'Windows':
if parse_version(torch.__version__) <= parse_version('1.12.1'):
extra_compile_args['nvcc'] += ['-std=c++14']
else:
extra_compile_args['nvcc'] += ['-std=c++17']
ext_ops = extension(
name=ext_name,
sources=op_files,
include_dirs=include_dirs,
define_macros=define_macros,
extra_objects=extra_objects,
extra_compile_args=extra_compile_args,
library_dirs=library_dirs,
libraries=libraries,
extra_link_args=extra_link_args)
extensions.append(ext_ops)
return extensions
setup(
name='mmcv' if os.getenv('MMCV_WITH_OPS', '1') == '1' else 'mmcv-lite',
version=get_version(),
description='OpenMMLab Computer Vision Foundation',
keywords='computer vision',
packages=find_packages(),
include_package_data=True,
classifiers=[
'Development Status :: 4 - Beta',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Utilities',
],
url='https://github.com/open-mmlab/mmcv',
author='MMCV Contributors',
author_email='openmmlab@gmail.com',
install_requires=install_requires,
extras_require={
'all': parse_requirements('requirements.txt'),
'tests': parse_requirements('requirements/test.txt'),
'build': parse_requirements('requirements/build.txt'),
'optional': parse_requirements('requirements/optional.txt'),
},
python_requires='>=3.7',
ext_modules=get_extensions(),
cmdclass=cmd_class,
zip_safe=False)
失败问题与解决记录
1. nvcc not found after subst
现象:
nvcc not found after subst: Z:\bin\nvcc.exe
原因:
Z: 盘符已被映射到其他 CUDA 版本,例如 CUDA 13.1,脚本想映射 CUDA 13.0 失败但错误被吞掉。
处理:
subst Z: /D
subst Z: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
subst
确认:
Z:\: => C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0
2. CUDA 13.1 混入
现象:
where nvcc
Z:\bin\nvcc.exe
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.1\bin\nvcc.exe
...
只要第一项是 Z:\bin\nvcc.exe 且 Z: 指向 CUDA 13.0,就可以接受。脚本把 Z:\bin 放到 PATH 最前面,确保实际使用 CUDA 13.0。
3. VS2022 MSVC 14.42 混入
现象:
编译命令里出现:
D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.42.34433
处理:
- 不从 VS2022 Developer Prompt 启动。
- 脚本调用
vcvars64.bat前清理 VS 旧变量。 - 打印
where cl和cl /Bv做确认。
最终成功使用:
C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Tools\MSVC\14.50.35717
4. compiled_autograd.h(1134): error C2872: "std"
原因:
PyTorch 2.9.1 的 Windows CUDA 保护分支依赖 USE_CUDA,MMCV 原始构建没有定义该宏。
处理:
在 mmcv/setup.py 中为 Windows CUDA 编译定义:
define_macros += [('USE_CUDA', None)]
5. 路径过长风险
处理:
统一映射和缓存:
subst M: J:\PythonProjects4\LiveTalking\mmcv
subst Z: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
set TMP=M:\tmp
set TEMP=M:\tmp
set TORCH_EXTENSIONS_DIR=M:\torch_ext
set CUDA_CACHE_PATH=M:\cuda_cache
set PIP_CACHE_DIR=M:\pip_cache
编译日志中必须检查的内容
每次复现时,先看日志开头。
CUDA:
Using CUDA_HOME: Z:\
Z:\bin\nvcc.exe
MSVC:
C:\Program Files\Microsoft Visual Studio\18\Insiders\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x64\cl.exe
短路径缓存:
Using tmp: M:\tmp
Using torch extensions cache: M:\torch_ext
Using CUDA cache: M:\cuda_cache
构建命令中应看到:
Z:\bin\nvcc
-IZ:\include
-IC:\Program Files\NVIDIA\CUDNN\v9.14\include
-DMMCV_WITH_CUDA
-DUSE_CUDA
-gencode=arch=compute_86,code=sm_86
注意事项
- 不要混装
mmcv和mmcv-full。本项目使用本地源码mmcv 2.2.0。 .venv\Lib\site-packages\mimictalk_mmcv_local.pth应指向本地源码目录,确保import mmcv命中仓库内源码。- 不要把
cl.exe文件本身加入 PATH;PATH 应加入工具所在目录。 CUDA_PATH、PATH第一项nvcc、PyTorch 编译 CUDA 版本必须一致。本次是cu130/ CUDA 13.0。- 如果换 GPU,调整
TORCH_CUDA_ARCH_LIST。RTX 3090 使用8.6。 - 如果换 Python / torch / CUDA / MSVC 任一主版本,都应重新验证
setup.py宏补丁是否仍然需要。 - wheelhouse 中不包含 torch 和 CUDA Toolkit,需要目标环境单独准备。
- 构建前清理
mmcv\build可以避免 Ninja 复用旧参数。
最小复现命令清单
cd /d J:\PythonProjects4\LiveTalking
.venv\Scripts\activate.bat
subst Z: /D
subst Z: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.0"
subst
rmdir /s /q mmcv\build
tools\build_mmcv.cmd
验证:
.\.venv\Scripts\python.exe -c "import mmcv; print(mmcv.__version__); print(mmcv.__file__)"
.\.venv\Scripts\python.exe -c "import mmcv._ext; print('MMCV ext: OK')"
.\.venv\Scripts\python.exe -c "from mmcv.ops import DeformConv2d; print('CUDA ops: OK')"
.\.venv\Scripts\python.exe -c "from mmcv import imread; print('Image I/O: OK')"
.\.venv\Scripts\python.exe -c "from mmengine.config import Config; print('MMEngine Config: OK')"
打包:
rmdir /s /q mmcv\build
tools\pack_mmcv_wheel.cmd
dir wheelhouse
结论
这次成功不是单一参数解决的,而是同时固定住了几个容易漂移的点:
torch 2.9.1+cu130对应 CUDA 13.0。nvcc通过Z:\bin\nvcc.exe固定到 CUDA 13.0。- MSVC 通过 VS 2026 Insiders
vcvars64.bat固定到14.50.35717。 - 源码、构建输出、临时目录、缓存目录都通过
M:缩短。 setup.py为 Windows CUDA 编译补充USE_CUDA,避开 PyTorch 2.9.1 头文件分支问题。- 编译和打包都保存日志,后续能直接从日志回溯。
后续在同一机器上复现,优先使用 tools\build_mmcv.cmd 和 tools\pack_mmcv_wheel.cmd,不要手工拼接长环境变量。
更多推荐


所有评论(0)