被引用:【B站视频教程笔记】基于VSCode和CMake实现C/C++开发 | Linux篇(gcc/g++)(安装、配置、使用详细教程)(VSCode教程)(CMake教程)(精!)
另外,这是github上的cmake cookbook的主页:CMake Cookbook
文章目录- CMake的使用
- 语法特性
- CMake重要指令
- cmake_minimum_required(指定CMake最小版本)
- project(定义工程名称)
- set(定义变量)
- include_directories(头文件搜索路径)
- target_include_directories(为特定项添加头文件搜索路径)(可能带PRIVATE, PUBLIC, INTERFACE等参数)
- link_directories(库搜索路径)
- add_library(生成库文件)
- add_compile_options(添加编译参数)
- add_executable(生成可执行文件)
- link_libraries(因为依赖关系没有 target_link_libraries 那么清晰,官方建议使用新的 target_link_libraries)
- target_link_libraries(为target添加需要链接的库文件)
- add_subdirectory(添加存放源文件子目录)(子目录中必须有CMakeLists.txt文件)
- aux_source_directory(临时构建源文件列表)(跟add_executable搭配使用?)
- find_package 找库,引入依赖库,从外部项目查找和加载设置,通过`if(库名_FOUND)`能判断是否找到
- add_definitions(代码外部控制,外部控制代码的开启和关闭,不对源码造成伤害?)
- CMake常用变量
- CMAKE_C_FLAGS gcc编译选项
- CMAKE_CXX_FLAGS g++编译选项
- CMAKE_BUILD_TYPE 编译类型(Debug,Release)
- CMAKE_BINARY_DIR、PROJECT_BINARY_DIR、`_BINARY_DIR`
- CMAKE_SOURCE_DIR、PROJECT_SOURCE_DIR、`_SOURCE_DIR`
- CMAKE_C_COMPILER(指定C编译器)
- CMAKE_CXX_COMPILER(指定C++编译器)
- EXECUTABLE_OUTPUT_PATH(可执行文件输出的存放路径)
- LIBRARY_OUTPUT_PATH(库文件输出的存放路径)
- CMake编译工程
- 编译流程
- 两种构建方式
- 内部构建(in-source build):不推荐使用
- 外部构建(out-of-source build):推荐使用
- 【实战】外部构建示例
- 【CMake代码实践】(士兵与枪)
- CMake调试
- 配置launch.json
- 运行调试
- 配置launch.json中的preLaunchTask,同时配置task.json(做自动化调试)
- 自动化调试测试
- 总结
- 附录:cmake 常用变量和常用环境变量查表手册---整理 .
用CMake,比你写makefile方便的多
比如增加了一个bar.cpp文件,我们只需要修改CMakeLists.txt文件就可以了
参见:CMake 中的 include_directories 和 target_include_directories 有什么区别?
link_directories(库搜索路径) add_library(生成库文件) add_compile_options(添加编译参数) add_executable(生成可执行文件) link_libraries(因为依赖关系没有 target_link_libraries 那么清晰,官方建议使用新的 target_link_libraries)link_libraries 与 target_link_libraries 的区别:
target_link_libraries 要在 add_executable 之后
link_libraries 要在 add_executable 之前
参考文章:cmake 的link_libraries和target_link_libraries
target_link_libraries(为target添加需要链接的库文件)
写法示例:
比如(以下写法(包括备注中的)都可以): TARGET_link_LIBRARIES(myProject hello),连接libhello.so库 TARGET_link_LIBRARIES(myProject libhello.a) TARGET_link_LIBRARIES(myProject libhello.so) 再如: TARGET_link_LIBRARIES(myProject libeng.so) #这些库名写法都可以。 TARGET_link_LIBRARIES(myProject eng) TARGET_link_LIBRARIES(myProject -leng)
参考文章:target_link_libraries 和link_libraries区别
add_subdirectory(添加存放源文件子目录)(子目录中必须有CMakeLists.txt文件) aux_source_directory(临时构建源文件列表)(跟add_executable搭配使用?)
查找当前目录下的所有源文件并保存在SRC变量中(不会递归子文件夹)
Module模式与Config模式 通过上文我们了解了通过Cmake引入依赖库的基本用法。知其然也要知其所以然,find_package对我们来说是一个黑盒子,那么它是具体通过什么方式来查找到我们依赖的库文件的路径的呢。 到这里我们就不得不聊到find_package的两种模式,一种是Module模式,也就是我们引入curl库的方式。另一种叫做Config模式,也就是引入glog库的模式。下面我们来详细介绍着两种方式的运行机制。 在Module模式中,cmake需要找到一个叫做Find.cmake的文件。这个文件负责找到库所在的路径,为我们的项目引入头文件路径和库文件路径。 cmake搜索这个文件的路径有两个,一个是上文提到的cmake安装目录下的share/cmake- /Modules目录,另一个使我们指定的CMAKE_MODULE_PATH的所在目录。 如果Module模式搜索失败,没有找到对应的Find .cmake文件,则转入Config模式进行搜索。它主要通过 Config.cmake or -config.cmake这两个文件来引入我们需要的库。 以我们刚刚安装的glog库为例,在我们安装之后,它在/usr/local/lib/cmake/glog/目录下生成了glog-config.cmake文件,而/usr/local/lib/cmake/ /正是find_package函数的搜索路径之一。 (find_package的搜索路径是一系列的集合,而且在linux,windows,mac上都会有所区别,需要的可以参考官方文档find_package) 由以上的例子可以看到,对于原生支持Cmake编译和安装的库通常会安装Config模式的配置文件到对应目录,这个配置文件直接配置了头文件库文件的路径以及各种cmake变量供find_package使用。 而对于非由cmake编译的项目,我们通常会编写一个Find .cmake,通过脚本来获取头文件、库文件等信息。通常,原生支持cmake的项目库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
示例:
find_package(GLOG)
add_executable(glogtest glogtest.cc)
if(GLOG_FOUND)
# 由于glog在连接时将头文件直接链接到了库里面,所以这里不用显示调用target_include_directories
target_link_libraries(glogtest glog::glog)
else(GLOG_FOUND)
message(FATAL_ERROR ”GLOG library not found”)
endif(GLOG_FOUND)
add_definitions(代码外部控制,外部控制代码的开启和关闭,不对源码造成伤害?)
参见:CMakeLists中的add_definitions()函数
CMake常用变量 CMAKE_C_FLAGS gcc编译选项 CMAKE_CXX_FLAGS g++编译选项(只会在原先的编译状态下追加或修改)
呀,老总不让看这个,让先看rtsp了,那就先看rtsp吧
基于VSCode和CMake实现C/C++开发 | Linux篇
20210923,继续!
CMAKE_C_COMPILER(指定C编译器) CMAKE_CXX_COMPILER(指定C++编译器) EXECUTABLE_OUTPUT_PATH(可执行文件输出的存放路径) LIBRARY_OUTPUT_PATH(库文件输出的存放路径) CMake编译工程CMake目录结构:项目主目录存在一个CMakeLists.txt文件
两种方式设置编译规则:
- 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory
添加子目录即可; - 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的
CMakeLists.txt中;
(CMake可以帮助我们生成makefile)
在 linux 平台下使用 CMake 构建C/C++工程的流程如下:
- 手动编写 CMakeLists.txt。
- 执行命令 cmake PATH 生成 Makefile ( PATH 是顶层CMakeLists.txt 所在的目录 )。
- 执行命令 make 进行编译。
# important tips . # 表示当前目录 ./ # 表示当前目录 .. # 表示上级目录 ../ # 表示上级目录两种构建方式 内部构建(in-source build):不推荐使用
内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源
文件放在一起会显得杂乱无章。
## 内部构建 # 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件 cmake . # 执行make命令,生成target make外部构建(out-of-source build):推荐使用
将编译输出文件与源文件放到不同目录中
## 外部构建 # 1. 在当前目录下,创建build文件夹 mkdir build # 2. 进入到build文件夹 cd build # 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件 cmake .. # 4. 执行make命令,生成target make【实战】外部构建示例
yg@ubuntu:~/arnold_test/20210922_swap_test$ tree
.
├── build
├── CMakeLists.txt
├── include
│ └── swap.h
├── main.cpp
└── src
└── swap.cpp
3 directories, 4 files
main.cpp
#include#include "swap.h" using namespace std; int main() { int a = 1; int b = 2; cout << "交换前:a = " << a << ", " << "b = " << b << endl; swap(a, b); cout << "交换后:a = " << a << ", " << "b = " << b << endl; cout << "success!" << endl; return 0; }
swap.cpp
#include "swap.h"
void swap(int &a, int &b)
{
int temp;
temp = a;
a = b;
b =temp;
}
swap.h
#pragma once extern void swap(int &a, int &b);
CMakeLists.txt
cmake_minimum_required(VERSION 3.5.1)
project(SWAP)
include_directories(include)
# 不能写在add_executable后面,宏定义要写前面
aux_source_directory(src SRC)
add_executable(main_cmake main.cpp ${SRC})
运行cmake指令生成makefile文件,并执行make指令:
yg@ubuntu:~/arnold_test/20210922_swap_test$ cd build yg@ubuntu:~/arnold_test/20210922_swap_test/build$ cmake .. -- The C compiler identification is GNU 5.4.0 -- The CXX compiler identification is GNU 5.4.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/yg/arnold_test/20210922_swap_test/build yg@ubuntu:~/arnold_test/20210922_swap_test/build$ make Scanning dependencies of target main_cmake [ 33%] Building CXX object CMakeFiles/main_cmake.dir/main.cpp.o [ 66%] Building CXX object CMakeFiles/main_cmake.dir/src/swap.cpp.o [100%] linking CXX executable main_cmake [100%] Built target main_cmake yg@ubuntu:~/arnold_test/20210922_swap_test/build$ ls CMakeCache.txt CMakeFiles cmake_install.cmake main_cmake Makefile yg@ubuntu:~/arnold_test/20210922_swap_test/build$ ./main_cmake 交换前:a = 1, b = 2 交换后:a = 2, b = 1 success! yg@ubuntu:~/arnold_test/20210922_swap_test/build$【CMake代码实践】(士兵与枪)
一个士兵对象,一个枪对象,士兵对象里有枪属性,并且有装填子弹和发射子弹方法,枪对象里也有装填子弹和发射子弹方法
yg@ubuntu:~/arnold_test/20210923_vscode_soldier_gun$ tree
.
├── build
├── CMakeLists.txt
├── include
│ ├── Gun.h
│ └── Soldier.h
├── main.cpp
└── src
├── Gun.cpp
└── Soldier.cpp
3 directories, 6 files
main.cpp
#include "Gun.h"
#include "Soldier.h"
using namespace std;
void test()
{
Soldier sanduo("xusanduo");
sanduo.addGun(new Gun("AK47"));
sanduo.addBulletToGun(20);
sanduo.fire();
}
int main()
{
test();
return 0;
}
Soldier.cpp
#include "Soldier.h"
using namespace std;
Soldier::Soldier(string name)
{
this->_name = name;
this->_ptr_gun = nullptr;
}
void Soldier::addGun(Gun *ptr_gun){
this->_ptr_gun = ptr_gun;
}
void Soldier::addBulletToGun(int num)
{
this->_ptr_gun->addBullet(num);
}
bool Soldier::fire()
{
return(this->_ptr_gun->shoot());
}
Soldier::~Soldier()
{
if (this->_ptr_gun==nullptr)
{
return;
}
delete this->_ptr_gun;
this->_ptr_gun=nullptr;
}
Gun.cpp
#include "Gun.h"
#include "iostream"
using namespace std;
void Gun::addBullet(int bullet_num)
{
this->_bullet_count += bullet_num;
}
bool Gun::shoot()
{
if(this->_bullet_count<=0)
{
cout << "没子弹了!" << endl;
return false;
}
this->_bullet_count -= 1;
cout << "成功发射一枚子弹!" << endl;
return true;
}
Soldier.h
#pragma once #include#include "Gun.h" using namespace std; class Soldier { private: string _name; Gun *_ptr_gun; public: Soldier(string name); ~Soldier(); void addGun(Gun *ptr_gun); void addBulletToGun(int num); bool fire(); };
Gun.h
#pragma once #includeusing namespace std; class Gun { public: Gun(string type){ this->_bullet_count = 0; this->_type = type; } void addBullet(int bullet_num); bool shoot(); private: int _bullet_count; string _type; };
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(SOLDIERFIRE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -std=c++11")
include_directories(${CMAKE_SOURCE_DIR}/include)
aux_source_directory(src SRC)
add_executable(my_cmake_exe main.cpp ${SRC})
编译运行结果:
yg@ubuntu:~/arnold_test/20210923_vscode_soldier_gun/build$ cmake .. -- Configuring done -- Generating done -- Build files have been written to: /home/yg/arnold_test/20210923_vscode_soldier_gun/build yg@ubuntu:~/arnold_test/20210923_vscode_soldier_gun/build$ makeScanning dependencies of target my_cmake_exe [ 25%] Building CXX object CMakeFiles/my_cmake_exe.dir/main.cpp.o [ 50%] linking CXX executable my_cmake_exe [100%] Built target my_cmake_exe yg@ubuntu:~/arnold_test/20210923_vscode_soldier_gun/build$ ./my_cmake_exe 成功发射一枚子弹!CMake调试 配置launch.json
点击调试按钮,点击launch.json文件
点击C++(GDB/LLDB),如果有C++-运行和调试活动文件,就继续点(我这里没有)
然后弹出launch.json文件
然后修改这个地方为我们上次生成的可执行文件
然后我们打开main.cpp文件,在16行那打个断点,然后按F5调试运行
然后我们可以按F11单步调试,这跟Visual Studio调试的快捷键是一样的
但是发现调试遇到点故障,,不同的地方乱进
我们得修改下CMakeLists.txt文件并重新编译
删除编译的 -g(生成带调试信息的可执行文件)和-O2(编译代码优化)选项,因为这可能会影响我们的调试文件,同时设置CMAKE_BUILD_TYPE为Debug
然后再次运行调试,可以正常调试了,但感觉这调试功能远不如Visual Studio方便啊!
在launch.json中,添加这两行代码(虽然不知道干啥用的!)
然后点击上方终端-配置默认生成任务
然后点击使用模板创建tasks.json文件
随便点一个others
然后把里面都删掉
在里面填入以下信息:
{
"version": "2.0.0",
"options": {
"cwd": "${workspaceFolder}/build" // "cwd"表示当前工作目录:current working directory;workspaceFolder表示工作空间文件夹
},
"tasks": [
{
"type": "shell",
"label": "cmake", // 该任务的标签是:"cmake"
"command": "cmake", // cmake任务生成makefile文件
"args": [ // cmake命令后所跟的参数,".."表示在父目录
".."
]
},
{
"label": "make", // 该任务的标签是:"label",根据makefile文件进行编译
"group": {
"kind": "build", // 当前任务所属的组是build组
"isDefault": true
},
"command": "make", // 在Windows下实际执行的命令是:mingw32-make
"args": [ // mingw32-make命令后面所跟的参数
]
},
{
"label": "Build", //这个名为"Build"的task是launch.json执行前所预先执行的任务
"dependsOrder": "sequence",//按列出的顺序执行任务依赖项
"dependsOn":[ // 并且这个任务又依赖"cmake"和"make"这两个任务
"cmake",
"make"
]
}
]
}
然后在launch.json的"preLaunchTask"处填入Build
然后就可以做自动化调试了
自动化调试测试我们在main.cpp的17行加入一行打印信息,然后直接按F5调试程序,发现程序自动执行了cmake …和make指令,并开启调试,在终端输出了我们添加的打印信息
视频课程完结!
因为我做的项目比较大,有点担心VScode的性能不够好,有点想转向QT creator或者C-Lion,涉及到交叉编译器的设置还不是很懂,准备先熟悉一下vscode的code runner插件
附录:cmake 常用变量和常用环境变量查表手册—整理 .cmake 常用变量和常用环境变量查表手册—整理 .



