0%

Ubuntu vscode 配置 c++ 开发环境(一)

前言

目前工作和学习中我基本都使用 vscode 作为主要开发工具,所以有必要记录一下我的常用 vscode 配置,不用每次换完机器都重新找各种教程配置一遍,这一篇主要是 vscode 本身相关的一些关于 c++ 项目的基本配置,包括代码补全、单文件项目和多文件项目的编译、调试和运行等,后面可能还会总结一下常用代码检查器、还有 ROS 相关的配置等。

前提条件

  • 系统需要安装 c++ 编译器等相关工具以及gdb,可以通过以下命令安装
    • sudo apt install build-essential gdb
  • vscode 需要安装 C/C++ 插件

下面主要分成三部分介绍,分别是代码补全、项目编译运行、以及调试,分别对应三个对应的配置文件。为了方便演示,我以下面一个代码文件结构进行演示:

FileCopier
├── bin---------------------------编译之后的二进制文件
│   └── ...
├── data--------------------------数据文件
│   ├── input_text.txt
│   └── output_text.txt
├── include-----------------------头文件
│   └── FileCopier.hpp
├── src---------------------------源代码
│   ├── FileCopier.cpp
│   └── main.cpp
└── .vscode-----------------------vscode 配置文件
    ├── c_cpp_properties.json
    ├── launch.json
    ├── settings.json
    └── tasks.json

项目(主要在 FileCopier.cpp 定义)的功能是:接受两个文件路径作为输入和输出参数,按行读取输入文件,显示在 console 上并输出到输出文件中,起一个复制的作用

单源文件/简单项目设置

代码补全

首先是代码补全,通过 Ctrl + Shift + p 运行 C/C++: Edit Configuration 会自动在当前打开的文件夹下的 .vscode 中生成一个 c_cpp_properties.json 文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/usr/local/include/**",
"${workspaceFolder}/include/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}

基本的模板会和上面有点区别,这里我主要修改了几个参数:

  • compilerPath 这个如果在 ubuntu 通过 build-essential 安装了 gcc 的话基本不用修改默认路径
  • cStandardcppStandard 规定代码中使用的 c/c++ 标准
  • intelliSenseMode 这一项是智能补全的模型,这里我没改变
  • includePath 这一项是比较关键的头文件的 include 路径,这里我根据我当前项目结构改成了:
    • "/usr/local/include/**":常用标准库的路径
    • "${workspaceFolder}/include/**":当前项目的头文件的 include 路径
    • 注意路径中如果用 * 意味着不会递归搜索,即只会搜索该文件夹下的文件而不会搜索其子文件夹中的文件,** 表示递归搜索

项目编译及运行

编译

vscode 中的项目编译和运行(以及其他 shell 脚本的运行)可以以 tasks 的形式配置,通过 vscode 窗口中,Terminal -> configuration tasks 选择 C/C++: g++ build active file 会自动在当前打开的文件夹下的 .vscode 中生成一个 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
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "C/C++: g++ build active project",
"command": "/usr/bin/g++",
"args": [
"-I",
"${workspaceFolder}/include",
"-g",
"${workspaceFolder}/src/*.cpp",
"-o",
"${workspaceFolder}/bin/${workspaceFolderBasename}"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": "build"
}
]
}

同样,基本的模板会和上面有点区别,这里我主要是根据项目结构修改了几个参数:

  • label: task 的名称,在之后会用到
  • command: 相当于输入我们想通过 task 运行的指令,指令的类型通过 type 限定,这里我们用 g++ 进行 c++ 项目的编译
  • args:针对上面用到的 command 给定的参数,这里就是 g++ 的参数,作为项目的话,通常头文件和输出二进制文件可能会不在一个文件夹里,所以我按照我的文件路径来设置,分别是:
    • -I:指定 include 路径,这里是 项目文件夹/include
    • -g:源文件路径,项目文件夹/src/所有cpp文件
    • -o:编译好的二进制路径,这里是 项目文件夹/bin/项目文件夹名称

设置好,按 Ctrl + Shift + b,选择上面 task 的label 即可编译,termial 输出结果是:

1
2
3
4
> Executing task: /usr/bin/g++ -I /workspaces/my_docker_projects/FileCopier/include -g /workspaces/my_docker_projects/FileCopier/src/*.cpp -o /workspaces/my_docker_projects/FileCopier/bin/FileCopier <


Terminal will be reused by tasks, press any key to close it

查看 bin 文件夹:

1
2
root:/workspaces/my_docker_projects/FileCopier# ls bin/
FileCopier

可以看到已经成功编译生成了相应的二进制文件

运行

同样,运行项目实际上也是一条 shell 指令,我们可以创建 task 来配置,这里创建如下:

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
{
"version": "2.0.0",
"tasks": [
{
"label": "C/C++: g++ build active project",
//...
},

{
"type": "shell",
"label": "C/C++: run active project",
"command": "bin/${workspaceFolderBasename}",
"args": [
"${workspaceFolder}/data/input_text.txt",
"${workspaceFolder}/data/output_text.txt"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": "build"
}
]
}

跟编译类似,command 中设为对应的 shell 指令,这里是二进制文件路径,arg 则输入项目对应的参数,这里是输入文件路径和输出文件路径,运行方式同样 Ctrl + Shift + b:选择对应label,这里应该是 C/C++: run active project,输出结果如下:

1
2
3
4
5
6
> Executing task: bin/FileCopier /workspaces/my_docker_projects/FileCopier/data/input_text.txt /workspaces/my_docker_projects/FileCopier/data/output_text.txt <

Hello World!
File copied!

Terminal will be reused by tasks, press any key to close it.

上面 Hello World 是输入文本文件里的内容,File copied 是程序中输出表示复制完成,比较一下两个文件:

1
2
root:/workspaces/my_docker_projects/FileCopier# diff data/input_text.txt data/output_text.txt
root:/workspaces/my_docker_projects/FileCopier#

输出无区别,表示程序正确运行。

编译和运行

目前我们已经通过两个 task 分别实现了编译和运行。有没有办法同时执行编译和运行呢?答案是同样可以通过 task 来实现,我们创建以下 task:

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
{
"version": "2.0.0",
"tasks": [
{
"label": "C/C++: g++ build active project",
//...
},

{
"label": "C/C++: run active project",
//...
},

{
"type": "shell",
"label": "C/C++: g++ build & run active project",
"command": "echo",
"args": [
"Project built and run!"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"dependsOn": [
"C/C++: g++ build active project",
"C/C++: run active project"
],
"group": "build"
}
]
}

以上 task 大部分和之前两个没什么区别,唯一在于 dependsOn 这个参数制定了在 task 执行 command 需要依赖于什么 tasks, 这里我们放入前两个 task 的label。因此执行这个 task 就可以相当于同时执行编译+运行了,由于两个 tasks 会用两个 terminal,运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在 1.C++: g++ build & run active project terminal 中
# 第一个 task 运行
> Executing task: /usr/bin/g++ -I /workspaces/my_docker_projects/FileCopier/include -g /workspaces/my_docker_projects/FileCopier/src/*.cpp -o /workspaces/my_docker_projects/FileCopier/bin/FileCopier <


Terminal will be reused by tasks, press any key to close it.

# 第三个 task 运行
> Executing task: echo 'Project built and run!' <

Project built and run!

Terminal will be reused by tasks, press any key to close it.

# 在 2.C++:Task- C/C++: run active project terminal 中
# 第二个 task 运行
> Executing task: bin/FileCopier /workspaces/my_docker_projects/FileCopier/data/input_text.txt /workspaces/my_docker_projects/FileCopier/data/output_text.txt <

Hello World!
File copied!

Terminal will be reused by tasks, press any key to close it.

针对简单单文件的设置

有些情况下,我们实际上并不需要针对一个项目编译(没有头文件,源代码也只有一份,C语言课程作业或者leetcode解答等情况..)这个时候实际上用生成的默认 tasks.json 的模板即可,大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "g++ build active file",
"command": "/usr/bin/g++",
"args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}"],
"options": {
"cwd": "/usr/bin"
},
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

其余的运行以及编译 + 运行的task可以参考上面的部分,但是要注意的是,如果是针对 active file 的编译(如上 task 有 ${file}等等),记得一定要在打开你想编译源文件下执行对应的 task !否则会显示错误文件类型之类的错误,这一点和上面我的设置是不一样的,如果是参考我以上三个task的设置的话,可以注意到中间没有用到涉及 filename 的变量,所以不需要打开对应源文件,甚至不打开任何文件都可以,但是要注意我使用了 ${workspaceFolder},所以需要 vscode 中打开的文件夹是我们的项目文件夹的根目录。

GDB 调试

首先需要确保电脑中已经安装了 gdb,然后通过 vscode 窗口中 Run -> add configuration。会自动在当前打开的文件夹下的 .vscode 中生成一个 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
27
28
29
30
31
32
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/${workspaceFolderBasename}",
"args": [
"${workspaceFolder}/data/input_text.txt",
"${workspaceFolder}/data/output_text.txt"
],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++ build active project",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}

这里我基本没有做太多的修改(对 gdb 还不是很熟悉哈哈)。主要看的参数有以下几个:

  • "program":要调试的二进制文件路径
  • "args":项目输入参数
  • "stopAtEntry":要不要在开始进行调试时暂停
  • "preLaunchTask":在进行调试之前需要进行的 task,(通常是编译项目),这里我设为之前创建的编译 task。

然后按 F5 或者侧边栏中 Run 都可以进入调试,调试界面如下:

左侧 variable 可以观察当前局部变量和全局变量,watch 设定观测点,call stack 查看程序调用栈。添加断点的方法是是行号左侧单击一下,右键编辑断点可以增加条件,其他单步运行等操作在上方。

结语

目前为止,这篇笔记总结了 vscode 中配置简单 c++ 项目的编译和调试相关开发环境。本来还想总结下多文件(相对大型)项目的编译,时间有限只能放在下一篇了。有什么意见或者建议欢迎交流!