使用VSCode和CMake构建跨平台的CC++开发环境

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

使⽤VSCode和CMake构建跨平台的CC++开发环境
⽇前在学习制作的实战项⽬Breakout游戏时,希望能将这个⼩游戏开发成跨平台的,⽀持在多个平台运⾏。

⼯欲善其事必先利其器,⾸先需要做的⾃然是搭建⼀个舒服的跨平台C/C++开发环境,所以这篇⽂章主要就是记录环境搭建的整个过程,踩到的⼀些坑,以及对应的解决办法。

正⽂开始之前,先来阐述⼏个问题
为什么选择使⽤VSCode
实在⽤不习惯Visual Studio(也可能是⽤的太少了T▽T)
代码编辑⽅⾯更喜欢⽤轻量级的编辑器,⽐如Sublime或者VSCode
VSCode确实⽐较强⼤好⽤,插件丰富
为什么使⽤CMake
通⽤的编译构建⼯具,跨平台的关键,⼀份代码,CMake可以针对不同的系统编译构建⽣成不同的项⽬⼯程
源代码管理,编译更加⽅便(如果仅仅使⽤VSCode搭建开发环境,则每添加⼀个源⽂件,就要改动⼀下编译指令)
最终实现的开发流程是怎样的
VSCode编写代码
快捷键 Ctrl+Shift+B ,调⽤CMake完成本地项⽬⽣成(Mac快捷键 Command+Shift+B )
快捷键 Ctrl+B ,完成项⽬的编译构建与运⾏(Mac快捷键 Command+B )
快捷键 F5 ,完成项⽬的调试与运⾏(VSCode的F5调试运⾏,为了能够实现调试功能额外做了许多⼯作,所以启动会有些慢,因此在不需要调试的时候,直接使⽤Ctrl+B编译运⾏看效果会更快些)
开发⼯具
CMake与VSCode
CMake的获取,可以查看
VSCode的获取,可以查看
关于CMake与VSCode如何安装,⽐较简单,⽹上也有很多教程,这⾥就不详细介绍了
VSCode插件推荐安装
在VSCode的 Extensions ⾯板中搜索下⾯的插件名即可,记得看清作者名,不要下错啦
插件名作者描述
C/C++Microsoft提供C/C++的代码提⽰,跳转,调试等诸多功能,官⽅出品,基本是C/C++开发必备了
CMake twxs提供CMake语法的⾼亮显⽰以及代码段提⽰
⽰例项⽬
这⾥给出的⽂件⽬录情况,并不完整,但具有⼀定的代表性,不仅涉及源代码的编译,同样包含了静态库,动态库的加载,以及资源⽂件的读取等问题
Breakout
├── 3rd // 第三⽅库
│├── glfw // ⼀个静态库⽬录
│├── irrKlang-1.6.0 // ⼀个动态库⽬录
│└── FindIrrKlang.cmake // cmake⽂件
├── resources // 资源⽬录
│├── textures // 存放贴图⽂件
│└── shaders // 存放shader⽂件
├── src // 源代码⽬录
│├── game // 源代码⼦⽬录
││├── game.h
││└──
│└──
└── CMakeLists.txt // cmake⽂件
使⽤CMake
本⽂仅着重介绍为了完成⽰例项⽬开发,解决特定问题⽽使⽤的⼀些cmake语句,详细的cmake介绍可以查看这份还不错的或者这篇cmake的所有语句都写在CMakeLists.txt中,cmake会根据该⽂件中的配置完成最终的编译,构建,打包,测试等⼀系列任务
⼀个简单的CMakeLists.txt如下所⽰,完整的⽂件可以查看
# cmake最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 设置PROJECT_NAME变量
set(PROJECT_NAME Breakout)
# 设置⼯程名
project (${PROJECT_NAME})
# 查找当前⽬录下的所有源⽂件并存⼊DIR_SRCS变量
aux_source_directory(src DIR_SRCS)
# 添加⼀个可编译的⽬标到⼯程
add_executable (${PROJECT_NAME} ${DIR_SRCS})
如何编译⼀个⽂件夹下的所有源代码
在开发过程中,由于架构设计或是为了便于管理与查找,源⽂件⼀般会根据不同的功能存放在不同的⽂件夹中,⽂件夹中⼜可能嵌⼊⽂件夹,所以需要⼀条语句能够获取所有的源⽂件进⾏编译,⽽不⽤每新创建⼀个源⽂件,就修改⼀次编译指令
# 递归列出所有源⽂件
file (GLOB_RECURSE SOURCE_FILES *.cc)
# 添加⼀个可编译的⽬标到⼯程
add_executable (${PROJECT_NAME} ${SOURCE_FILES})
上⾯这条file命令会递归列出所有.cc⽂件,并存⼊SOURCE_FILES变量,然后将SOURCE_FILES表⽰的所有.cc⽂件添加到⽬标即可,从⽽解决多源⽂件编译问题
如何引⼊⼀个第三⽅静态库
为了不重复造轮⼦,开发中不可避免的要引⼊其他第三⽅库。

正常情况下,这个第三⽅库也会是⼀个CMake⼯程(或是库的开发者直接提供已经编译好的库)
以⽰例项⽬引⼊的库为例
添加submodule引⼊glfw(glfw正好是github上的⼀个开源项⽬),或是直接将第三⽅库的源码放到⾃⼰的⽬录中
git submodule add https:///glfw/glfw.git
使⽤add_subdirectory命令,将glfw所在的⽂件夹添加到编译的任务列表中
# 保证glfw dir被编译
add_subdirectory (${GLFW_DIR})
将glfw的头⽂件⽬录添加到头⽂件搜索路径中
# 添加头⽂件搜索路径
include_directories (${GLFW_DIR}/include)
链接glfw库,target_link_libraries命令⽤来链接⽬标与库⽂件,第⼀个参数就是我们的构建⽬标,后⾯可以跟多个参数,来表⽰链接多个库
# 添加链接库
target_link_libraries (${PROJECT_NAME} glfw)
动态库的加载问题
以引⼊的irrKlang库为例,它并不是⼀个开源项⽬,不过好在它提供了已经在多个平台上编译好的库,所以我们需要根据不同的平台来设置引⼊不同的库⽂件
利⽤find_package引⼊外部依赖包,它可以帮我们找到官⽅预定义的许多依赖包模块,当未在官⽅预定义的依赖中找到时,会再查找FindXXX.cmake⽂件,执⾏该⽂件从⽽找到XXX库。

更详细的介绍可以查看这篇
# IrrKlang
find_package (IrrKlang REQUIRED)
先新建FindIrrKlang.cmake⽂件,由它来负责具体的irrKlang库加载。

部分语句如下所⽰,主要是根据当前平台的不同,设置不同的头⽂件路径,库路径,库所在⽬录等变量。

⽤到的find_library语句可以实现直接根据库的base name(即不需要lib,so等),找到对应的库,并存⼊IRRKLANG_LIBRARY变量
find_path(IRRKLANG_INCLUDE_DIR NAMES irrKlang.h PATHS "${3RD_DIR}/irrKlang-1.6.0/include")
IF(WIN32) # win32平台
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(STATUS "Using MSVC")
set (IRRKLANG_LIB_DIR "${3RD_DIR}/irrKlang-1.6.0/lib/Win32-visualStudio")
set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/Win32-visualStudio")
find_library(IRRKLANG_LIBRARY NAMES irrKlang PATHS ${IRRKLANG_LIB_DIR})
elseif("${CMAKE_CXX_COMPILED_ID}" STREQUAL "GNU")
message(STATUS "Using GCC")
set (IRRKLANG_LIB_DIR "${3RD_DIR}/irrKlang-1.6.0/lib/Win32-gcc")
set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/Win32-gcc")
find_library(IRRKLANG_LIBRARY NAMES libirrKlang.a PATHS ${IRRKLANG_LIB_DIR})
endif()
elseif(APPLE) # mac平台
set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/macosx-gcc")
find_library(IRRKLANG_LIBRARY NAMES libirrklang.dylib PATHS "${3RD_DIR}/irrKlang-1.6.0/bin/macosx-gcc")
elseif(UNIX AND NOT APPLE) # 等同于linux平台
set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/inux-gcc")
find_library(IRRKLANG_LIBRARY NAMES IrrKlang PATHS "${3RD_DIR}/irrKlang-1.6.0/bin/linux-gcc")
endif()
将找到的irrKlan头⽂件添加到头⽂件搜索路径中
include_directories (${IRRKLANG_INCLUDE_DIR})
链接irrKlang库
# 添加链接库
target_link_libraries (${PROJECT_NAME} glfw ${IRRKLANG_LIBRARY})
utf-8编码格式的代码通过visual studio编译报错问题
跨平台的代码,⼀般使⽤utf-8编码格式的代码,更加通⽤,也可以保证在MacOS或者Linux平台上的正常编译。

但是visual studio默认编译⽂件的编码是utf-8 with bom,在没有中⽂的情况下,直接编译是没有问题的,然⽽当源⽂件含有中⽂时(⽐如中⽂注释),则可能会出现异常,报⼀些莫名其妙的语法错误。

解决办法是通过CMake语句通知MSVC编译时采⽤utf-8编码
# 设置MSVC编译编码
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>")
可执⾏⽂件的⼯作⽬录问题
当编译构建⽣成可执⾏⽂件后,我们希望可以直接通过命令⾏命令启动可执⾏⽂件来查看效果。

但是由于⼯作⽬录的问题,可能会导致出现资源⽂件找不到,或者库加载失败问题
对于visual studio⼯程,可以通过CMake语句设置其⼯作⽬录,但是这个⼯作⽬录仅在通过visual studio调试启动时才会⽣效,但对于直接启动可执⾏⽂件的情况仍然是没⽤的
# 设置⼯作⽬录
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY
${CMAKE_SOURCE_DIR}/resources
)
所以只能将资源⽂件⽬录放置到可执⾏⽂件所在⽬录下,以保证⼀定能加载到需要的资源,这可以通过CMake的⾃定义命令实现
# 复制资源⽂件到⼯作⽬录
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources
$<TARGET_FILE_DIR:${PROJECT_NAME}>/resources
)
这条语句的功能是在编译结束后,将指定的资源⽬录复制到⽣成的可执⾏⽂件所在的⽬录。

同样的,对于⼀些动态库,⽐如dylib,dll等也需要复制,不过注意最好在编译前就将它们复制到⽬标⽬录,使⽤PRE_BUILD指明命令执⾏的时机
# 复制库到⼯作⽬录
add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${IRRKLANG_BIN_DIR}
$<TARGET_FILE_DIR:${PROJECT_NAME}>
)
关于add_custom_command详细介绍可以查看
如何修改Mac上动态库的加载路径
在Mac上启动可执⾏⽂件时,⼀直遇到⼀个动态库⽆法加载的报错
dyld: Library not loaded: /usr/local/lib/libirrklang.dylib
Referenced from: ...
Reason: image not found
这是由于在编译⽣成动态库时,可以指定动态库的加载路径,⽐如我们引⼊的libirrklang库默认会到/usr/local/lib⽬录下查找dylib⽂件
简单的解决⽅式,⾃然是通过命令,将libirrklang.dylib⽂件安装到/usr/local/lib⽬录下,不过为了不“污染”其他⽬录,更希望可执⾏⽂件加载的是我们放置在其所在⽬录下的libirrklang.dylib⽂件。

为了实现这个⽬标,我们添加下⾯的CMake语句
if(APPLE)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND install_name_tool -change /usr/local/lib/libirrklang.dylib @executable_path/libirrklang.dylib ${PROJECT_NAME}
)
endif()
判断如果是Mac平台,则通过add_custom_command调⽤install_name_tool命令,来修改应该应⽤程序对动态库的查找路径。

其中的
@executable_path就表⽰可执⾏⽂件所在⽬录
关于动态库加载路径涉及到的rpath,executable_path,install_name_tool的详细介绍可以查看
利⽤VSCode的task.json执⾏指定命令
CMake的配置⽂件已经基本准备完毕了,接下来就是怎样结合VSCode更⽅便的调⽤CMake的问题了
通过Ctrl+Shift+B执⾏CMake编译本地⼯程
Ctrl+Shift+B是VSCode调⽤task的默认快捷键,task定义在.vscode⽬录下的task.json⽂件中,⼀般使⽤task来完成编译等任务,VSCode提供了丰富的参数配置,我们可以利⽤它完成很多⾃定义任务
定义⼀个task,执⾏ cmake .. 命令,完成本地项⽬的编译⽣成。

Linux会⽣成Makefile,MacOS⽣成Makefile或Xcode⼯程,Windows下⽣成Visual Studio⼯程。

注意这⾥的 .. 表⽰的是上层⽬录,因为我们会在项⽬根⽬录下新建⼀个build⽂件夹,然后在这个⽂件夹内完成⼀系列的编译⼯作,这样cmake⽣成的中间⽂件都在build⽬录,不会“污染”开发⽬录(将build⽬录加⼊.gitignorej即可忽略CMake所产⽣的所有中间⽂件),在编译出问题的时候也可以直接删除bui dl⽬录重新编译
{
"label": "cmake", // task的名字
"type": "shell",
"command": "cmake",
"args": [
// "-DCMAKE_BUILD_TYPE=${input:CMAKE_BUILD_TYPE}",
".."
],
"options": {
"cwd": "build" // 表⽰当前执⾏⽬录build⽂件夹
},
"group": "build",
"presentation": { // ⼀些控制台显⽰配置
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"dependsOn":["mkbuild"] // 依赖的任务,在本任务执⾏前先执⾏mkbuild任务
}
在 cmake .. 命令执⾏前,先通过task执⾏ mkdir 新建build⽂件夹。

其中通过windows参数,区分不同平台设置不同的参数
{
"label": "mkbuild",
"type": "shell",
"command": "mkdir", // 调⽤的命令
"args": [ // 命令参数
"-p",
"build"
],
"windows":{ // windows平台使⽤mkdir -Force build新建⽂件夹
"args": [
"-Force",
"build"
]
},
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
}
当按下Ctrl+Shift+B时,VSCode将弹出所有可执⾏的task,选择执⾏cmake task即可。

由于定义了依赖(dependsOn),在cmak task执⾏前,将⾃动先执⾏mkbuild task
通过Ctrl+B构建与运⾏可执⾏⽂件
在启动可执⾏⽂件前,先通过 cmake --build . 构建⽣成可执⾏⽂件
{
"label": "compile",
"type": "shell",
"command": "cmake --build .",
"options": {
"cwd": "build"
},
"group": "build",
"presentation": {
// Reveal the output only if unrecognized errors occur.
"reveal": "always",
"clear": true
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile"
}
在可执⾏⽂件⽣成后,通过task启动可执⾏⽂件。

其中的${workspaceFolderBasename}是VSCode的内置变量,表⽰⼯程名。

更多的内置变量介绍可以查看
{
"label": "run",
"type": "shell",
"command": "./${workspaceFolderBasename}",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
"options": {
"cwd": "build"
},
"windows":{
"options": {
"cwd": "build/Debug" // windows visual studio项⽬会默认多⽣成Debug/Release⽬录
},
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"dependsOn":["compile"] // 在run任务执⾏前先执⾏compile任务,确保修改的代码⽣效
}
通过Ctrl+B直接调⽤run任务。

由于编译运⾏这个任务经常要⽤到,如果依然通过Ctrl+Shift+B弹出所有任务,再进⼀步选择就会有些⿇烦,所以定义⼀个快捷键(读者可以⽤同样的⽅法设置⾃⼰喜欢的快捷键)来直接运⾏run任务
打开VSCode快捷键设置
在弹出的界⾯中输⼊“run build task”搜索,并修改其快捷键
点击右上⾓的翻转按钮,进⼊快捷键⽂件配置
在打开的⽂件中,为刚配置的快捷键添加参数,告诉它直接启动名字叫“run”的task
{
"key": "ctrl+b",
"command": "workbench.action.tasks.runTask",
"args" : "run"
}
利⽤VSCode的launch.json完成调试
VSCode通过.vscode⽬录下的launch.json实现对多个平台多种语⾔的调试⽀持
通过F5完成项⽬的调试与运⾏
配置launch.json⽂件以⽀持C/C++项⽬的调试
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https:///fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Debug", //名称
"type": "cppdbg", //调试类型,除使⽤msvc进⾏调试外,均为该类型
"request": "launch",
"program": "${workspaceFolder}/build/${workspaceFolderBasename}", //指定C/C++程序位置
"args": [], //指定运⾏参数
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build", //指定⼯作⽬录
"preLaunchTask": "compile", //在调试前会先调⽤这个task编译构建程序
"environment": [],
"externalConsole": false,
"osx": { //macOS的特定配置
// "miDebuggerPath": "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb-mi", //修改使⽤的lldb-mi,⼀般不需要修改 "MIMode": "lldb" //指定使⽤lldb进⾏调试
},
"linux": { //linux的特定配置
"MIMode": "gdb", //指定使⽤gdb调试
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
"windows": { //windows的特定配置
"type": "cppvsdbg", //指定使⽤msvc进⾏调试
"program": "${workspaceFolder}/build/Debug/${workspaceFolderBasename}.exe", //指定C/C++程序位置
}
}
]
}
F5是VSCode调试运⾏的默认快捷键,不需要额外配置,启动调试后的界⾯如下所⽰
通过使⽤本地⼯具完成调试
由于cmake在windows平台会默认⽣成Visual Stduio⼯程,所以也可以直接打开⽣成的解决⽅案,通过Visual Studio进⾏调试Mac平台可以使⽤ cmake .. -GXcode 命令指定⽣成XCode⼯程,然后通过XCode进⾏调试
参考链接
⽂中提到的完整的⽂件与⽂件。

相关文档
最新文档