vscode中配置pybind11

windows环境下使用MSVC,在vscode中配置pybind11环境

pybind11 version:3.0.1 python version:3.14

一. cl.exe直接编译

前置准备

  • Visual Studio2022

  • Python,Pybind11

  • VScode

  • 在VScode中配置好MSVC

开始配置

  • 按下快捷键Ctrl+Shift+P 在顶部搜索框中输入==> Configure Default Build Task==

  • 选择C/C++ cl.exe build active file,VS Code会在.vscode文件夹中自动生成tasks.json

  • 对tasks.json文件进行如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
"version": "2.0.0",
"tasks": [
{
"label": "create-build-directories",
"type": "shell",
"command": "mkdir",
"args": [
"-Force",
"-Path",
"${workspaceFolder}/build,",
"${workspaceFolder}/build/obj,",
"${workspaceFolder}/build/dist,",
"${workspaceFolder}/src/binding"
],
"options": {
"cwd": "${fileDirname}",
"shell": {
"executable": "powershell.exe" // 指定使用 PowerShell 执行
}
},
"problemMatcher": [],
"detail": "用 PowerShell 自动创建 build 相关目录"
},
{
"type": "cppbuild",
"label": "C/C++: cl.exe build pybind11 file",
"command": "cl",
"args": [
"/O2",
"/LD",
"/EHsc",
"/source-charset:utf-8",
"/I",
"D:\\Environment\\Miniconda3\\envs\\p3_14_pybind\\Lib\\site-packages\\pybind11\\include",
"/I",
"D:\\Environment\\Miniconda3\\envs\\p3_14_pybind\\Include",
"/Fo\"${workspaceFolder}\\build\\obj\\${fileBasenameNoExtension}.obj\"", // .obj 输出到 obj 目录
"${workspaceFolder}\\src\\binding\\${fileBasenameNoExtension}.cpp",
"/link",
"/LIBPATH:\"D:\\Environment\\Miniconda3\\envs\\p3_14_pybind\\libs\"",
"python314.lib",
"/IMPLIB:\"${workspaceFolder}\\build\\obj\\${fileBasenameNoExtension}.lib\"", // 强制 .lib 输出到 obj 目录
"/OUT:\"${workspaceFolder}\\build\\dist\\${fileBasenameNoExtension}.pyd\"", // .pyd 仍输出到 dist 目录
"/EXPORT:PyInit_${fileBasenameNoExtension}" // 显式导出 Python 初始化函数(可选,增强兼容性)
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$msCompile"
],
"group": {
"kind": "build",
"isDefault": false
},
"dependsOn": "create-build-directories",
"detail": "compiler: cl.exe"
}
]
}
  • OK,大功告成,按下Ctrl+Shift+B 即可运行 (或在菜单中选择Terminal中的Run Task)

二. CMake构建

在 Windows 系统中,.pyd 文件本质上是一种特殊的动态链接库(DLL)

在 Linux/macOS 系统中,Python 扩展模块通常是 .so 文件,属于「共享库」

都会被CMake分配到 lib 目录下

CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 指定CMake的最低版本要求(3.15到4.0之间)
cmake_minimum_required(VERSION 3.15...4.0)
# 定义项目名称(MyProject)、版本(1.0.0)和使用的语言(C++)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)


# 设置C++标准为C++17
set(CMAKE_CXX_STANDARD 17)
# 强制要求使用指定的C++标准(不 fallback 到低版本)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 禁用编译器特定的扩展(确保代码跨编译器兼容)
set(CMAKE_CXX_EXTENSIONS OFF)

# 设置编码为UTF-8(兼容MSVC和其他编译器)
if(MSVC) # 针对Visual Studio编译器
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") # MSVC的UTF-8编译选项
else() # 针对GCC、Clang等编译器
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finput-charset=UTF-8 -fexec-charset=UTF-8") # 输入/输出编码均设为UTF-8
endif()

# 配置二进制文件(.exe、.dll、.pyd等)的输出目录
# 全局输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Debug模式下的二进制输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/Debug)
# Release模式下的二进制输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/Release)


# 配置动态库文件(.so、.dylib等)的输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib/Debug)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib/Release)


# 配置静态库和目标文件(.lib、.a、.obj等)的输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib/Debug)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib/Release)


# 配置调试符号文件(PDB)的输出目录(主要用于MSVC)
set(CMAKE_PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/pdb)
set(CMAKE_PDB_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/pdb/Debug)
set(CMAKE_PDB_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/pdb/Release)

# 查找Python依赖(要求同时找到解释器和开发文件,用于链接Python API)
find_package(Python REQUIRED COMPONENTS Interpreter Development)


# 配置pybind11的路径并查找其配置(用于生成Python扩展模块)
if(WIN32) # Windows系统下的pybind11路径(示例路径,需根据实际环境调整)
set(pybind11_DIR "D:\\Environment\\Miniconda3\\envs\\compile_env\\Lib\\site-packages\\pybind11\\share\\cmake\\pybind11\\")
else() # 非Windows系统下的pybind11路径(示例路径,需根据实际环境调整)
set(pybind11_DIR "~/pybind11/build")
endif()
find_package(pybind11 CONFIG REQUIRED) # 查找pybind11的CMake配置


# 动态获取模块目录列表(替代手动指定MODULES_LIST)
# 匹配src目录下的所有直接子项(文件或目录),并存储到MODULES_LIST
file(GLOB MODULES_LIST LIST_DIRECTORIES TRUE "${CMAKE_SOURCE_DIR}/src/*")

# 遍历所有匹配到的项,仅处理目录(每个目录对应一个模块)
foreach(MOD_DIR IN LISTS MODULES_LIST)
if(IS_DIRECTORY ${MOD_DIR}) # 过滤出目录(排除直接文件)
# 从目录路径中提取最后一级目录名作为模块名(如"src/lexical"提取为"lexical")
get_filename_component(MOD_NAME ${MOD_DIR} NAME)
# 收集该模块目录下的所有C++源文件(.cpp)
file(GLOB MOD_SOURCES "${MOD_DIR}/*.cpp")
# 收集该模块目录下的所有头文件(.h、.hpp)
file(GLOB MOD_HEADERS "${MOD_DIR}/*.h" "${MOD_DIR}/*.hpp")
# 排除bindings.cpp(它是pybind11的接口文件,不放入核心库)
list(FILTER MOD_SOURCES EXCLUDE REGEX "bindings\\.cpp$")


# 如果该模块有源文件,则创建核心静态库(存储模块的核心逻辑)
if(MOD_SOURCES)
add_library(${MOD_NAME}_core STATIC ${MOD_SOURCES} ${MOD_HEADERS})
# 设置静态库的输出名称(确保与模块名关联)
set_target_properties(${MOD_NAME}_core PROPERTIES
OUTPUT_NAME ${MOD_NAME}_core
)

# 公开链接Python库(核心库可能依赖Python API,下游模块会自动继承)
target_link_libraries(${MOD_NAME}_core PUBLIC Python::Python)
endif()
endif()
endforeach()

# 手动配置模块间的依赖关系(示例:syntactic模块依赖lexical模块)
# 公开链接lexical_core,确保依赖syntactic_core的模块能自动获取lexical的符号
target_link_libraries(syntactic_core PUBLIC lexical_core)

# (如需AST模块依赖其他模块,取消下面注释并调整)
# target_include_directories(AST_core PUBLIC
# "${CMAKE_SOURCE_DIR}/src/lexical" # 允许AST访问lexical的头文件
# "${CMAKE_SOURCE_DIR}/src/syntactic" # 允许AST访问syntactic的头文件
# )
# target_link_libraries(AST_core PUBLIC lexical_core syntactic_core) # 链接依赖的核心库


# 遍历模块目录,为存在bindings.cpp的模块创建pybind11扩展(Python接口)
foreach(MOD_DIR IN LISTS MODULES_LIST)
if(IS_DIRECTORY ${MOD_DIR}) # 仅处理目录
# 提取模块名(同核心库的命名逻辑)
get_filename_component(MOD_NAME ${MOD_DIR} NAME)
# 绑定文件路径(pybind11的接口实现文件)
set(BINDINGS_FILE "${MOD_DIR}/bindings.cpp")
# 如果存在bindings.cpp,则生成Python扩展模块
if(EXISTS ${BINDINGS_FILE})
# 创建pybind11模块(类型为MODULE,源文件为bindings.cpp)
pybind11_add_module(${MOD_NAME} MODULE ${BINDINGS_FILE})
# 设置模块输出属性:
# - 输出名称与模块名一致
# - 移除默认前缀(如Linux下的"lib")
# - Windows下强制后缀为.pyd(Python扩展的标准后缀)
set_target_properties(${MOD_NAME} PROPERTIES
OUTPUT_NAME ${MOD_NAME}
PREFIX ""
SUFFIX ".pyd"
)
# 链接该模块的核心库(获取核心逻辑的实现)
target_link_libraries(${MOD_NAME} PRIVATE ${MOD_NAME}_core)
endif()
endif()
endforeach()

tasks.json

如果使用miniconda,在进行需先使用==conda activate [name]==激活对应环境,再进行cmake ..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{
"version": "2.0.0",
"tasks": [
{
"label": "cmake-clean",
"type": "shell",
"command": "cmake --build . --target clean; del * -Recurse -Force -Confirm:$false",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build",
"shell": {
"executable": "powershell.exe"
}
},
"detail": "清理 build 目录"
},
{
"label": "cmake-debug",
"type": "shell",
// CMake 生成 Debug 任务激活 Conda 环境
"command": "& { conda activate p3_14_pybind; cmake -DCMAKE_BUILD_TYPE=Debug .. }",
"dependsOn": "cmake-clean",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build",
"shell": {
"executable": "powershell.exe"
}
},
"detail": "在指定 Conda 环境下生成 Debug 模式的 CMake 构建文件"
},
{
"label": "cmake-release",
"type": "shell",
// CMake 生成 Release 任务激活 Conda 环境
"command": "& { conda activate p3_14_pybind; cmake -DCMAKE_BUILD_TYPE=Release .. }",
"dependsOn": "cmake-clean",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build",
"shell": {
"executable": "powershell.exe"
}
},
"detail": "在指定 Conda 环境下生成 Release 模式的 CMake 构建文件"
},
{
"label": "build-debug",
"type": "shell",
"command": "cmake --build . --config Debug -j 8",
//"dependsOn": "cmake-debug",
"group": {
"kind": "build",
"isDefault": false
},
"problemMatcher": ["$gcc"],
"options": {
"cwd": "${workspaceFolder}/build",
"shell": {
"executable": "powershell.exe"
}
},
"detail": "编译 Debug 模式的项目"
},
{
"label": "build-release",
"type": "shell",
"command": "cmake --build . --config Release -j 8",
//"dependsOn": "cmake-release",
"group": "build",
"problemMatcher": ["$gcc"],
"options": {
"cwd": "${workspaceFolder}/build",
"shell": {
"executable": "powershell.exe"
}
},
"detail": "编译 Release 模式的项目"
}
]
}

launch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"configurations": [
{
"name": "Python Debugger: Current File (Release)",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"preLaunchTask": "build-release",
"env":{
"PYTHONPATH":"${workspaceFolder}/build/lib/Release"
}
},
{
"name": "Python Debugger: Current File (Debug)",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"preLaunchTask": "build-debug",
"env":{
"PYTHONPATH":"${workspaceFolder}/build/lib/Debug"
}
}
]
}