VSCode C++ 多文件编译
一、前言
这篇文章是windows系统下的VSCode的C++项目(C应该也差不多)的多文件编译的教程(未使用CMake),笔者也才刚刚学会方法,如果有什么错误或者问题还请指出。这个教程是循序渐进的,所以有基础的或者只是想知道launch.json和tasks.json怎么配置的可以直接看最后面的配置代码,但是如果是想知道为什么要这么配置的建议全文看完并跟着做一遍。
二、准备工作
1、下载VSCode(废话),官网链接
2、下载MinGW编译器,官网链接
这个网站国内可能进不去,但是网上有很多资源和安装教程,或者去b站搜索一下教程,然后一定要把环境变量配好,这个并不是很难,所以就不细说了。
2、安装插件
打开VSCode,点击左侧的扩展选项:
安装简体中文插件(英语好的可以不安装):搜索chinese即可
安装C/C++插件:搜索C/C++即可,这个插件建议不要下载最新版本,如果已经安装了最新版本,可以点击卸载按钮,在下拉框中选择安装之前的版本,我是1.8.4版本。新的版本在后面生成launch.json时可能不会给出预制模板。
三、正式开始
1、创建工程
在电脑中合适的位置创建一个文件夹(假设文件夹名为Test)作为项目文件夹,然后在VSCode中点击左上角文件->打开文件夹,打开刚才创建的文件夹,创建新的文件,创建方式有以下两种(其实不止两种,只是这两种比较方便):
- 在左侧项目文件夹处点击
- 在左侧项目文件夹空白(虽然是黑的)处双击:
- 方法2的空白处右键后选择新建文件夹。
创建一个名为main.cpp的cpp文件,写一个最简单的helloworld程序:
main.cpp:
1 |
|
2、控制台编译(非常重要,建议一定要跟着做一遍才能理解后面在干什么)
1、单文件编译
点击终端->新建终端,然后下面就会出现一个终端窗口,他会直接进入到工程目录下:
在终端中键入g++ .\main.cpp
回车,g++
意思是使用g++编译器,如果是C项目则使用gcc编译器,后面的.\main.cpp
是要编译的文件(.\
是当前文件夹的意思),编译完后Test目录下就会出现一个名为a.exe的可执行文件,这时候在终端中键入.\a.exe
回车,就会执行这个文件,若终端中显示Hello World!就说明编译没有问题。
然后在终端中键入g++ -g .\main.cpp -o main.exe
回车,这时候会发现Test目录下会出现一个main.exe,此时在终端中键入.\main.exe
回车,那么终端中依旧会显示Hello World!,那么a.exe和main.exe有什么区别呢?
我们可以在终端中键入ls
回车查看这两个文件的大小:
我们发现,main.exe比a.exe大了一些,那么我们来解释一下g++ .\main.cpp
和g++ -g.\main.cpp -o main.exe
,
g++是使用g++编译器编译(C++文件),-g是指编译出的可执行文件中包含调试信息(所以main.exe会比a.exe大)这样这个文件就可以被调试了(调试的事情之后再说),-o是指定生成的可执行文件的名称。
2、多文件编译
在项目文件夹中再创建hello.cpp和hello.h两个文件,并在hello.h中声明一个名为printHello的函数:
hello.h:
1 | void printHello(); |
并在hello.cpp中实现这个函数,在main.cpp中调用这个函数:
hello.cpp:
1 |
|
main.cpp:
1 |
|
我们先将之前生成的a.exe和main.exe删除了(项目中选中文件右键删除即可,若显示无法删除则说明你的程序正在运行),这时我们在终端中键入g++ -g .\main.cpp -o main.exe
回车,此时我们发现项目中并没有生成main.exe,并且终端报错:
这是因为main.cpp调用的方法是hello.cpp的,但是我们只编译了main.cpp,故而报错printHello方法未定义。
另外,如果在上面的main.cpp中若
#include "hello.h"
下方显示有波浪线(一般情况下不会有这个错误,因为头文件是在项目文件夹中的),并报错找不到头文件,则可以按下ctrl + shift + p
在弹出的搜索框中键入C/C++ Edit Configurations
,选择C/C++ 编辑配置(JSON),如果没有安装中文插件的选择C/C++ edit configration(JSON) 这时项目中会出现一个名为.vscode的文件夹,并且里面有一个名为c_cpp_properties.json的文件,打开后如下显示:
其他的不同没什么关系,也不一定要和我配的一样,主要是"includePath"字段,
"${workspaceFolder}/**"
表示包含你这个项目文件夹中的所有文件(/**
表示递归包含),如果你的头文件在别的文件夹中(不在项目文件夹中,也不在项目文件夹的子文件夹中),就需要把那个文件夹的地址写进这里,或者你可以直接把那个文件夹拷贝到项目文件夹中。但其实这个只是影响编写代码的智能补全,并不影响之后的编译。
既然如此,那是不是只要把hello.cpp和main.cpp都编译了就行了呢?我们在终端中键入g++ -g .\main.cpp .\hello.cpp -o main.exe
回车,这时项目文件夹中出现了main.exe,且终端也没有报错,说明编译成功,键入.\main.exe
终端中也显示出了Hello World!。
那么也就是说多文件其实也可以编译成功,但是如果我们每一次编译都要这样输入的话太麻烦了,而且这种编译的方法不就是传统的在控制台中编译的方法吗?这甚至和vscode没有什么关系(只是我们借用了vscode的内部终端而已),我们直接按win + r
键入cmd
,直接在cmd中也可以这样编译,那么vscode有没有什么好的方法可以让我们更快的编译?这时就需要用到launch.json和tasks.json了。
3、vscode编译
1、配置launch.json和tasks.json
点击左侧调试按钮:然后点击创建launch.json文件。
选择C++(GDB/LLDB)
再选择g++.exe - 生成和调试活动文件
如果在选择了C++(GDB/LLDB)后就没有这一步选择的话那么可能的因为C/C++插件的版本问题,因为笔者之前安装最新版本时出现过这个问题,但如果你安装的是旧的版本还有这个问题,那么我就不知道是怎么原因了(因为我没有遇到过,当然不知道怎么处理)但是也不要慌,这个问题只是会让你的launch.json文件没有预制的模板,你可以去网上找一个模板,或者按照我下面给出的代码配置。
此时会进入编译过程,然后显示调试失败:
直接×掉这个提示框(如果点击了打开"launch.json"则会出现之前的选择配置的选择框,按esc关掉选择框就行了)。此时项目中会有.vscode文件夹(可能你之前就有了,反正无论如何现在有这个文件夹就行)。里面会有launch.json和tasks.json两个文件(之前配置过includePath的话还会有c_cpp_properties.json),其中launch.json是调试的配置,tasks.json是编译的配置。我们先打开launch.json,正常情况下会是下面这个情况(当然不会有我这么详细的注释):
launch.json
1 | { |
如果你的launch.json只有两行:
1 | { |
那么就是我上面说的没有预制模板,可能最新版本是C/C++插件的问题,但是你可以直接把我的这个配置复制过去,然后把"miDebuggerPath"
的value设置成你安装的MinGW里的gdb的地址(或者你选用别的调试器,那么"MIMode"
的value也改成你选的调试器),并且你的"preLaunchTask"
的value要和你的tasks.json里的"label"
的value对应。
然后我们再打开tasks.json:
tasks.json
1 | { |
在这两个文件中最重要的就是
- launch.json的
"program"
要和tasks.json的"args"
的"-o"所指定生成的可执行文件的名字要一致,不然找不到要调试的文件。 - launch.json的
"preLaunchTask"
要和tasks.json的"label"
对应,不然调试前无法编译。
我们先将之前控制台编译生成的main.exe删除,然后根据之前控制台编译的情况,我们将tasks.json中的"args"
作如下修改:
1 | "args": [ |
然后将launch.json的"program"
设置成 "${workspaceFolder}\\main.exe",
(与tasks.json的"-o"
参数对应):
1 | "program": "${workspaceFolder}\\main.exe", |
我们点击上面的运行->启动调试:(或者按快捷键F5)。
这时下方的终端中的C/C++:g++.exe生成活动文件窗口会显示生成成功完成:
然后点击调试控制台,里面会显示Hello World!说明编译正确,并且程序成功运行。
如果你的launch.json中的
"externalConsole"
设置成true,则表示调试时在控制台中运行程序,那么你点击调试后会有一个窗口一闪而过,因为main函数执行后就退出了,可以在main函数的return 0;之前加一句getchar();
(注意导入头文件)来避免这个情况。点击调试后是下面这个情况。
这种方式来编译其实就相当于在控制台编译中键入g++ -g .\main.cpp .\hello.cpp -o main.exe
,那么如果有很多文件岂不是很麻烦?要把每一个要编译的cpp文件都写进tasks.json?其实可以将tasks.json的"args"
进行如下设置:
1 | "args": [ |
"${workspaceFolder}\\*.cpp",
就表示将工作目录(也就是项目的目录)下的所有cpp文件都编译,此时再启动调试,依旧成功编译,成功调试。
那么如果我的头文件和cpp文件都是在不同目录下的呢?例如,在项目目录下创建两个文件夹(和创建文件的方式差不多,在Test下的空白区右键创建),分别叫heads和sources,然后将hello.h放到heads文件夹中,将main.cpp和hello.cpp放到sources文件夹中。
此时启动调试会发现终端中的C/C++:g++.exe生成活动文件窗口报告编译错误:
但是调试控制台(或者cmd窗口)依旧显示了Hello World!。
编译错误是因为你的cpp文件已经不是直接放在工作目录(项目目录)下了(虽然sources目录依旧在工作目录下),那么就没有编译到cpp文件,从而报错。
而调试控制台(或cmd窗口)中仍然显示Hello World!是因为我们只是规定调试前要做编译的工作,但是至于编译是否成功并不关心,甚至没有编译都行(例如将launch.json的"preLaunchTask": "C/C++: g++.exe 生成活动文件"
注释掉),只要launch.json的"program": "${workspaceFolder}\\main.exe",
的可执行文件是存在的并且可执行文件中有调试信息即可。编译失败只是没有重新生成新的main.exe,但是原来的main.exe依旧存在并且可以调试。
那么我们可以将tasks.json的"args"
进行如下设置:
1 | "args": [ |
再启动调试,那么终端中的C/C++:g++.exe生成活动文件窗口就会显示生成成功:
而此时的main.exe是新生成的main.exe(可以删除了main.exe之后再启动调试试试看)。
对于"-g"
后面的"${workspaceFolder}\\sources\\*.cpp"
应该不需要再解释了吧,而"-I"
是之前控制台编译所没有讲到的,其表示的是需要包含的头文件的文件夹,也就是"${workspaceFolder}\\heads"
。我们可以在终端处试试控制台调试,新建终端,在终端中键入g++ -g .\sources\main.cpp .\sources\hello.cpp -I .\heads -o main.exe
回车,没有报错就是编译成功(可以把main.exe删了再试试有没有重新生成main.exe),此时在终端中键入.\main.exe
回车则显示Hello World!。
如果你的项目中有别的地方的头文件那么也需要把他加入到"-I"
后面
由此,多文件编译就差不多配置好了,有一些建议:
如果在上述操作中发现main.exe无法删除,那么是因为main.exe正在运行,得结束运行才行。
我们可以把
"-o"
指定的生成路径改成"${workspaceFolder}\\bin\\main.exe"
,同样的launch.json的"program"
后的路径也要改成"${workspaceFolder}\\bin\\main.exe"
。并且在项目目录下建立bin文件夹(不然会报错),这样生成的可执行文件就在bin目录下了。launch.json中的
"cwd"
和tasks.json的"options"
下的"cwd"
(可能你的tasks.json里没有这个东西,可以加上)设置成"${workspaceFolder}"
。
最后贴上我最终设置的launch.json和tasks.json:
launch.json:
1 | { |
tasks.json
1 | { |
做别的项目时只要相应的进行一些小调整就行。