VScode的两个json文件 (tasks.json、launch.json)针对C++构建任务

你能学到

  • 一些g++编译指令
  • tasks.json、launch.json在C++构建任务中的常用的键值对的含义

    前言

    本文仅讲解 tasks.json、launch.json 文件常用的地方,以 VScode 自动生成的两个 json 文件为例。

    json是一种数据交换格式,不了解的自行百度。其实也没有太大必要了解很深,在本文中你只需知道它是用来存储配置信息的即可。

    因为此格式便于计算机解析,大家都约定使用而已。换言之,只要你NB,设计的新格式合理,好用,没准大家都使用你的,那不大赚一笔?打住,想money想疯了-_-

    在说这两个json文件之前,有必要先说说g++的编译指令。为什么呢?

    tasks.json 顾名思义是用来定义任务的。使用 VScode 一键运行,通常执行的就是此文件中定义的任务。而现在我们的主要任务是构建 C++项目,编译出可执行文件 .exe。

    用 VScode 来写代码,主要的无非就几个要求:

    • 编译C++项目文件
    • 调试程序
    • 代码错误提示

      等,这些功能可由 GCC工具链 中的 g++、gcc、gdb 提供。

      换言之,你只要下载了 GCC工具链(或者其他C++编译器),即便不用 VScode 也可以写 C++项目,然后使用相关指令调用 g++、gdb,进行编译、调试等(就像有人开玩笑说,大神都在用记事本写代码)。但是这样是很繁琐的,下面演示 g++ 相关的编译指令:


      g++指令

      本文重点在两个json文件,所以只是简单的讲解部分指令*(因为会用到,如果你已经会了自行跳过)* ,想要了解更多的自行百度。

      常用的参数

      • -g : 生成 debug 版本程序,包含可供 gdb 使用的调试信息

        如果不加此参数,那么无法生成能被调试的程序

        • -o : 指定输出文件的路径
        • -I : 指定头文件包含目录,用于搜索头文件
          单文件编译
          • 初始状态
            • 输出指令调用 g++,编译 1.cpp,生成 1.exe
              • 运行 1.exe,成功
                多文件编译
                • 项目结构如下

                  add.h 中为 add() 函数的声明语句,add.cpp 为其定义语句,现在在 main.cpp 中调用 add() 函数

                  • 输入指令

                    因为 main.cpp 中导入的 add.h 不在当前目录,无法识别,因此需要使用 -I 参数指明其所在目录 (省略则默认为当前目录,比如下图中当前目录为 D:\10.cmake\demo\htf\text)


                    如果没有指明 add.h 所在目录,则会报错:


                    • 运行,没有问题

                      tasks.json

                      作者的另外一篇文章(VScode配置C++环境)中说了自动生成 tasks.json 的方法,其内容如下:

                      • type

                        任务的类型。

                        VScode 内置任务类型为 shell、process。cppbuild 是为 C\C++ 插件提供的。(使用此类型,以便更好地与 C\C++ 插件集成,使用其功能)

                        • tasks

                          其后跟的 [ ] 中为对应的值,说明了任务是什么,怎么做。

                          既然是 tasks,那么也就是说任务可以有多个,在 [ ] 中,每个任务的相关信息在 { } 中,以 “,” 隔开。比如上述 tasks.json 只有一个任务 (红框中)

                          • label

                            既然可以有多个任务,那总该有唯一标识一个任务的键值,label 就是起此作用,接收一个 string。


                            • command

                              接收一个 string,用于在 shell 中 执行。比如之前用到的 g++

                            • args

                              接收一个 string数组,用于给 command 传递参数。像之前的 g++,书写调用指令需要指定参数。

                              • 比如 g++ 1.cpp 指令,g++ 对应 command,而 g++ 后跟的参数对应 args
                              • ${file} :是一种固定用法,表示取变量file的值。

                                file 是 VScode 已经定义好了的变量(就像 C++ 的预定义宏一样),表示当前打开的文件的路径,会在执行任务时自动替换。

                                ${变量} 表示取变量的值。

                                想了解其他的预定义变量你可查看 VScode官方文档

                              当我们一键运行时,会发现终端出现如下情况(重点在 && 的后面跟的)

                              很显然,上述 tasks.json 的主要任务是 一键运行后,自动在终端执行 command + args

                              由于 g++ 所在路径已经加入环境变量,因此你可以将 指令 “C:\msys64\ucrt64\bin\g++.exe” 简单写为 “g++”

                              -fdiagnostics-color=always 表示 g++ 在输出错误信息时采用有颜色的字体输出。比如之前的没有写 -I 的那张图片的报错信息,有的单词被标红


                              • options

                                在执行任务时使用的命令选项,可省略

                                上述tasks.json中的 **cwd 用于指明任务的执行目录 **,如果省略是默认是项目的根目录。

                                • 因为 g++指令 中处理的是路径,存在相对路径,所以要指定一个合适的目录,保证之后的相对路径正确,绝对路径那就无所谓了。
                                • ${fileDirname} 表示 取当前打开文件(file)的路径所在目录(dirname)
                                • detail

                                  接收一个 string,用于描述任务详情,相当于注释

                                  其他键值对目前用不到,不在过多说明。


                                  所以其实重要的地方就两个:command、args。针对编译C++程序而言,二者提供了调用g++的指令 (这就是为什么要先说 g++ 指令的原因) 。下面来分析:

                                  • file:当前打开的文件对应的路径。
                                  • fileDirname (file dirname) :当前打开的文件对应的目录。
                                  • fileBasenameNoExtension:
                                    • Basename:路径中最后不包含文件路径分隔符的部分 (比如 ./file.h 为 file.h,C:\dir\file 为 file)
                                    • NoExtension:没有扩展名

                                      合起来也就是 当前打开文件的无扩展名的文件名。

                                      所以,上述的 command + args = ‘g++ -g 当前打开的文件 -o 当前打开的文件.exe’,也就是说它只能处理单文件编译的情况。那么多文件的项目怎么办呢?无非还是修改 args,举例:

                                      本人比较喜欢的一种格式,bin 目录用来装程序,inc 为头文件,src 为源文件。

                                      如果用之前自动生成的tasks.json显然不合适,因为它只适合单文件编译,现在会是多文件,可如下进行修改

                                      workspaceFolder:工作区目录 (在 VScode 中打开的目录,比如上图为 根目录TEXT)

                                      • 第一条红线:TEXT 目录下的 src 目录下的所有以 .cpp 结尾的文件 (*.cpp,通配符匹配路径)
                                      • 第二条红线:由于前一个是 -o 参数,所以指 输出文件为 TEXT 目录下的 bin 目录下的 main.exe

                                        一个程序只对应一个.exe,在编译单文件的 json 文件中,我们使用的是当前打开的文件的文件名,而现在是直接指定程序名称为 main.exe

                                        • 第三条红线:由于前一个是 -I 参数,所以指 头文件包含目录 为 TEXT 目录下的 inc 目录
                                        • 第四条红线:因为上述从 args 的描述可知,此指令应该在 TEXT 目录下执行,才能确保路径正确

                                          现在一键构建 (Ctrl + Shift + B)

                                          运行程序,成功


                                          launch.json

                                          有必要注意的就三个

                                          • program:对应所生成的程序的路径,因为你要调试的程序就是它
                                          • externalConsole:默认情况下使用的是 VScode 内置的控制台。设为 true,那么将启用新的控制台
                                            1. false
                                              1. true
                                              • miDebuggerPath:调试器路径,以便 VScode 调用 gdb

                                                最后

                                                可以看到,VScode 自动生成的 tasks.json 只适用于单文件编译,但是在一个 C++ 项目中,大多采用头文件与源文件分离的情况,因为这样方便管理项目。

                                                C++ 头文件与源文件写法不明白的自行百度

                                                在写的过程中你会发现一个重复性的工作:需要两次写声明函数,并进行一些增删,比如:

                                                // 头文件
                                                namespace std {void fic(const string& s = "");
                                                } 
                                                // 源文件
                                                namespace std {void fic(const string& s) {// 删除默认参数
                                                		// ...
                                                	}	
                                                }
                                                

                                                声明少时没多大感觉,但较多时就比较麻烦,这里推荐一个作者自己写的命令行小工具(我称为 header_to_file),能够 读取头文件中的声明语句,自动生成定义语句,并输出到源文件中,避免重复性工作,有兴趣的前往 **header_to_file **了解。


                                                文章参考VScode官方文档,如有错误,欢迎指正。

                                                如果本文对你有用,欢迎点赞,或者分享给有需要的人(注明出处即可),谢谢。