还是第一次取这么标题党的题目。。。
这是之前写的一篇关于使用VS Code配置C/C++和OpenCV的博客,但是最近遇到一点关于VS Code配置文件的问题,有点懵,翻找博客时联想到当时配置时也只是按照教程机械操作,对VS Code原理机制方面不了解,于是趁着帮同学再次配置OpenCV的契机,打算重构本博客,写一篇关于VS Code各种配置文件的原理和机制,授人与渔!
本文的重点将放在VS Code的原理和机制上,但是考虑直接讲原理方面会有些凌乱,于是打算从使用VS Code配置C/C++以及配置OpenCV的角度展开,讲解对各个配置文件的理解。在看下面的内容之前,你首先要有一点最基本的编译知识:
前情提要:由于VS Code更新太快,所以如果版本不同可能相关的界面和操作会有所区别,本文基于的版本为1.73.1,如果发现界面不同,不要惊慌,万变不离其宗,关键是掌握它的原理。
这里简要介绍一下MinGW的安装教程。
安装好MinGW之后,还需要将其路径添加到环境变量。操作简图如下。其中路径那里选择自己电脑上安装的路径即可。
配置完之后,可以在VS Code的终端验证一下:
gcc --version #验证gcc
g++ --version #验证g++
gdb --version #验证gdb
如果不报找不到命令的错误,那表明配置成功了。如果报错,可以考虑重启终端(即重启VS Code),理论上来说差不多也够了,但保不齐会出什么bug,所以如果后面遇到啥问题,考虑一下重启电脑。
这里需要注意,由于早期VS Code中的C++插件在右上角没有运行按钮,所以导致早期的教程都建议安装一个Code Runner插件,但是以目前的版本来看,其C++插件已经做得足够好用,不再需要安装Code Runner插件。
VS Code这边,只需要安装一个插件即可,即C/C++。直接在扩展中搜索c++即可,一般第一个就是。
安装好插件后,如果此时新建一个c或cpp文件,就可以发现已经有代码高亮了,还可以发现右上角多了一个三角形,可以用来运行和调试代码。此时随便建立一个cpp文件,然后点击运行,会出现一个悬浮窗让你选择使用那个编译器去运行代码。
一般选择g++或者gcc,差别不是特别大(更细节的咱也不懂。。。)
点击之后就可以发现,在你工作区的.vscode文件夹下会自动生成一个tasks.json文件,如下图所示。
于是,就来到了本文的一个重点:读懂tasks.json文件
首先需要明确,这个文件是干什么的,如果愿意啃官方文档的可以看看这个链接。用直白的话来说,我认为,它的作用就是设置你安装的编译工具来对代码进行编译,也就是设置一个task
。这样,当你设置好之后,下次需要编译时,只需要点击一个按钮即可,不再需要一条一条地敲命令。
再来看看里面的各个属性。可以发现label
项和command
项就是你刚刚选择的编译器对应的信息。label
的作用只是标注这个task,相当于一个ID,可以随便修改。command
相当于点击运行按钮时执行的指令。
除了这两个参数外,还需要重点关注另外两个项:args
和options
,其中options
中的cwd
项表示current work direction
,即当前工作路径,这个是直接关系到你在代码里面读写文件时的相对路径中的./
,非常重要!而其对应的值${fileDirname}
可以理解为VS Code预定义的“宏”,就是表示文件所在文件夹的路径。除了这个之外,VS Code还提供了其他的“宏”供用户更改,如下图所示。【图片来自官方链接】
这些变量在VS Code中编辑时会有代码提示,使用方便了不少。
最后是args
属性,这个是要输入到命令行中的,紧跟在command
项后面,因此要和你设置的命令匹配。以g++为例的话,随意打开一个终端,输入g++ --help
就能得到它能加的参数,如下图所示。
至于VS Code直接生成的默认tasks文件,其中-g
表示启动调试,-o
后面接的内容表示输出的exe文件。
配置好tasks之后,惊奇地发现,现在好像就可以进行运行和调试了,不再需要配置其他内容,但是如果翻以往的教程可以发现,此时应该是还得配置launch.json
文件才对,这个估计就是VS Code更新的结果了。。。
但是,如果不配置launch
文件,在VS Code左边那里就没办法看到使用的调试器是啥(强迫症表示受不了),而且还不能配置调试。所以最好还是再配置一下launch文件,方法如下。
另外,需要注意,早期的VS Code生成的默认launch文件是包含一个
preLaunchTask
属性,目前好像没有了,大胆猜测有可能是tasks一般情况下只有一个,所以默认执行了,如果一定要加的话,记得这一项要和tasks.json中的label
项一致。
通过这种方式配置好的c++编译和调试功能可以发现是不能运行更不能调试(这里注意区分运行和调试哦~)中文文件的【路径和文件都不能有中文】,其本质原因我认为是调试器gdb输入的exe文件不支持中文,否则会报错没有这个文件或者非法参数。因为在tasks文件中args属性中加了-g,但是实测发现即使注释掉-g也不好使,具体没太研究明白,有待后续探索。
但是参考以往的教程可以发现,有一些还会装code runner插件,但其实code runner本质上只是帮你打命令而已,具体可以去看code runner设置中的executorMap
,比如,cpp项的代码是这样的:
"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
&&
相当于是命令的分界线,可以发现code runner首先是cd到你文件所在文件夹(保证了cwd),然后调用g++,编译你编写的文件,然后输出(-o)一个与代码文件同名但无后缀的exe文件,最后再运行这个exe文件。由于g++能够编译中文名文件,所以如果使用code runner,运行代码时基本不会出现因为文件名或路径是中文的报错。
而上面的配置相当于是使用VS Code自带的cpp插件实现的,虽然也是调用MinGW套装,但是运行和调试似乎是密不可分的?具体不太懂。总之现在的问题在于不能运行和调试文件名或路径中含有中文的代码文件。通过参考这个链接,我找到了一种“骚方法”能够实现英文文件夹下的英文文件运行调试正常,中文文件能运行,但调试会有异常(就别调了);而中文文件夹下既不能调试,甚至运行都不行。 总的来说,如果有过往中文文件,只需要把它放在英文文件夹下即可运行(调试就别想了,除非改为英文名),操作也不是很复杂,而且还可以少一个code runner插件,赢!
下面是操作方法。本质就是把生成的exe文件放到一个英文路径下,记得同时改tasks和launch文件就行。
这样操作还有一个好处就是不管运行多少文件,都只会有一个exe文件,干净了不少,再赢!
另外,不知道算不算不用code runner额外的好处,就是code runner似乎和最新的PowerShell7不兼容,如果要打印中文,老是会有乱码出现,而且用chcp还不能改。。。但是用c++插件运行和调试似乎都没有中文乱码的烦恼(如果有,建议把代码文件存为utf-8)
其实有了配置C++的基础,再配置OpenCV就简单了不少。这里假定你已经配置好了一个C++环境(就是上面的tasks和launch都配置好了)
打开OpenCV官网可以发现,在它的Release下面有不同的选项可以下载,如下图所示。
如果是用Windows的话,可以选择Windows和Sources,它们的差别在于一个只有源码,一个除源码外,还有一个build文件夹。
那问题来了,既然它有build文件夹,为什么我们还要再次编译呢?emmm,关于这个的理解,我认为是编译器的不同,已有的build文件是Windows上(VS)常用的编译器(MSVC)编译得到的,所以它内部有VC14和VC15文件夹,所以如果是VS配置OpenCV,就不需要编译,只需要添加编译好的文件对应的路径即可,但是咱们VS Code配置用的是MinGW,所以还得编译一下。
那配置C++时能不用MinGW吗?是个好问题,找到一个VS Code配置MSVC的教程,有兴趣的可以参考一下。链接
总的来说,如果只需要配置VS Code,就只下源码就行了,如果是还想配置VS,那就下载Windows版本。
下载完源码后,并不能直接使用,还需要对源码进行编译。
OpenCV配置最麻烦的就是编译这块,因为一旦编译错误,似乎很难找到问题所在。所以,如果有现成的编译好的文件,比如别人电脑上的文件,是可以直接复制过来使用的(实测有效!),但是要保证MinGW版本之间没差太多,而且尽量文件夹不要有空格!!!
OpenCV源码一般都是使用CMake进行编译。首先要下载CMake:https://cmake.org/download/,安装好之后一定记得把bin文件夹添加到环境变量,然后打开cmake-gui。
进行一些简单的配置:
这里需要注意的是,放编译文件(Makefile)的文件夹可以自定义,但是建议和OpenCV放在一起(方便找)。
这一步一般不会出错,如果出错,据说可以通过勾选和取消勾选中间的红色区域选项(具体不太懂,建议定点搜索),再次Configure并Generate,速度一般会比第一次快不少。
配置好之后,接下来就是编译过程了,也是最容易出错的过程。其实就只有两步:
cd .... #先cd到放编译文件的文件夹下
minGW32-make -j 4 #4表示线程数,如果嫌不够可以继续加
minGW32-make install #等上一个指令运行到100%,再运行该指令即可
错误记录
error: ‘sleep_for’ is not a member of ‘std::this_thread‘
:参考这个教程,是在代码gapi_async_test.cpp中添加一个头文件#include
,亲测确实有效! 编译好OpenCV后,会得到一个放编译文件的文件夹,也是需要添加到环境变量中。以我电脑上的版本为例,我把编译好的文件放在了x64/MinGW
文件夹下,然后添加环境变量时,只需要添加该文件夹下的bin文件夹即可。
另外,如果要配置VS版本的OpenCV,记得把VC14和VC15的bin路径也添加到环境变量(本教程中用不到)。同理,配置完环境变量后,记得重启终端(重启VS Code)
前面提到,如果有现成的配置好的C++环境,这一步将非常简单,相当于只需要把OpenCV相关库文件添加进去即可。
tasks.json
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe 生成活动文件",
"command": "C:\\Program Files\\MinGW_x64\\bin\\g++.exe",
"args": [ //发现没有,在原C++基础上只需要改这个属性即可
"${file}",
"-I",
"C:/Program Files/Opencv 4.5.1/build/include", //路径记得确认一下
"-I",
"C:/Program Files/Opencv 4.5.1/build/include/opencv2", //路径记得确认一下
"-L",
"C:/Program Files/Opencv 4.5.1/build/x64/MinGW/lib",//路径记得确认一下
"-l",
"libopencv_core451",
"-l",
"libopencv_imgproc451",
"-l",
"libopencv_imgcodecs451",
"-l",
"libopencv_video451",
"-l",
"libopencv_ml451",
"-l",
"libopencv_highgui451",
"-l",
"libopencv_objdetect451",
"-l",
"libopencv_flann451",
"-l",
"libopencv_imgcodecs451",
"-l",
"libopencv_photo451",
"-l",
"libopencv_videoio451",
"-o",
"${fileDirname}/${fileBasenameNoExtension}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}
这里简单解释一下吧,-I
表示包含目录,即#include
;-L
表示库目录,即需要包含进去的lib
文件夹;-l
表示link
,即编译时需要链接的库。【似乎和VS配置OpenCV对上了?】至于需要链接哪些库,lib文件夹下的都加上就好了(.a
后缀表示静态链接库)
launch.json
没啥要改的,保持原样即可。
c_cpp_properties.json
这个文件是设置C++插件的,理论上来说其实可以不需要管这个文件,但是如果不改的话,代码里面include那里会报错找不到文件,虽然不影响运行,但是呢,一方面是不方便查看源码(头文件定位不过去),另一方面就是画波浪线受不了呀~~
一般设置这个有两种方式:
一种是修改C++插件的设置。如下图所示。
但是亲测发现,虽然能打开头文件,但是仍然会有波浪线,强迫症表示受不了。。。
于是,建议采用下面的方式:
一般会打开一个C++插件的设置,然后添加进去OpenCV的路径即可。或者直接打开命令面板(Ctrl + Shift + P
),输入c++,得到下图。
总之,最后本质上就是在修改c_cpp_properties.json
文件,也不需要改很多,只需要添加两个路径即可,如下图所示。
到此,include画波浪线的问题就解决了,OpenCV也配置完了。
总结一下,不管是配置C++还是OpenCV或者其他什么框架,最关键的就是理解各个部分的作用是什么,代码运行的逻辑是什么,各个插件的定位是什么。VS Code是一个很好的工具,但是也得花一点时间研究一下它的使用方式。
考虑到以后还得继续使用VS Code,这部分的内容还是继续保留吧,以后遇到什么问题也会继续更新。
外部控制台运行
coderunner作为vs code的“官方运行代码的插件,可以说功能很强大了,但是也有一部分问题,其中一个就是运行代码只能在内部终端运行,无法在外部控制台运行,但是调试还是可以在外部控制台输入的,这个可以通过设置调整。
无意间找到一个教程,发现其实在外部控制台运行也是可以的,只需要改变一下coderunner的设置即可,直接看教程链接吧。但是经过我的测试,发现其实这个教程也有一定的问题,比如会闪退(必须要在最后加上system(“PAUSE”) 才行)
设置code runner允许中文名,允许空格
之所以会存在这个问题,是因为code runner中提供的预定义的“宏”有一些没有引号,有一些又有,傻傻分不清楚【所以建议不要用】
复制代码如下:
"c": "cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt", //原始代码
//"c": "g++ $fullFileName -o $fileNameWithoutExt.exe && start $fileNameWithoutExt.exe",//更改后可以使得.c文件运行在外部窗口
//"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt", //原始代码
"cpp": "cd $dir && g++ -std=c++17 \"$fileName\" -o \"run\" && .\\\"run\"", //更改后可以使文件夹中只有一个run.exe执行文件,且可以避免文件名空格问题
//"cpp": "g++ $fullFileName -o $fileNameWithoutExt.exe && start $fileNameWithoutExt.exe", //更改后可以使cpp文件在外部终端运行
//"cpp": "cd $dir && g++ $fullFileName -o run && start run",//外部终端,同时运行可以只有一个run.exe执行文件
link