Cmake的使用_cmake干啥用的通俗点

(37) 2024-08-14 11:01:01

vscode

  • Cmake简介
  • Cmake语法
  • 重要指令和CMake常用变量
    • 重要指令
    • CMake常用变量
  • CMake编译工程
  • CMake代码实践
  • CMake完整项目
  • 调试步骤launch.json和tasks.json

Cmake简介

简介

  • CMake是一个跨平台的安装编译工具,可以用简单的语句来描述所有平台的安装(编译过程)。

  • CMake可以说已经成为大部分C++开源项目标配

传统的C++文件不通系统对应不通的编译
Cmake的使用_cmake干啥用的通俗点 (https://mushiming.com/)  第1张
比如 当添加一个文件的时候需要对不通操作系统中的工程进行修改
Cmake的使用_cmake干啥用的通俗点 (https://mushiming.com/)  第2张
有了Cmake以后,只需要编写一个CmakeLists文件就可以对应将一个C++工程不通操作系统
Cmake的使用_cmake干啥用的通俗点 (https://mushiming.com/)  第3张
当新添加一个文件的时候只需要更改对应的CmakeLists即可
Cmake的使用_cmake干啥用的通俗点 (https://mushiming.com/)  第4张

Cmake语法

基本语法格式:指令(参数 1 参数 2…)

  • 参数使用括弧括起
  • 参数之间使用空格或分号分开

指令是大小写无关的,参数和变量是大小写相关的

set(HELLO hello.cpp) add_executable(hello main.cpp hello.cpp) ADD_EXECUTABLE(hello main.cpp ${HELLO}) 

变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名

重要指令和CMake常用变量

重要指令

cmake_minimum_required - 指定CMake的最小版本要求

# CMake最小版本要求为2.8.3 cmake_minimum_required(VERSION 2.8.3) 
  • 语法:cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])

project - 定义工程名称,并可指定工程支持的语言

# 指定工程名为HELLOWORLD project(HELLOWORLD) 
  • 语法:project(projectname [CXX] [C] [Java])

set - 显式的定义变量

# 定义SRC变量,其值为main.cpp hello.cpp set(SRC sayhello.cpp hello.cpp) 
  • 语法:set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

include_directories - 向工程添加多个特定的头文件搜索路径 —>相当于指定g++编译器的-I参数

# 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径 include_directories(/usr/include/myincludefolder ./include) 
  • 语法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

link_directories - 向工程添加多个特定的库文件搜索路径 —>相当于指定g++编译器的-L参数

# 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径 link_directories(/usr/lib/mylibfolder ./lib) 
  • 语法:link_directories(dir1 dir2 …)

add_library - 生成库文件

# 通过变量 SRC 生成 libhello.so 共享库 add_library(hello SHARED ${SRC}) 
  • 语法:add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)

add_compile_options - 添加编译参数

1# 添加编译参数 -Wall -std=c++11 2add_compile_options(-Wall -std=c++11 -O2) 
  • 语法:add_compile_options(

add_executable - 生成可执行文件

# 编译main.cpp生成可执行文件main add_executable(main main.cpp) 
  • 语法:add_executable(exename source1 source2 … sourceN)

target_link_libraries - 为 target 添加需要链接的共享库 —>相同于指定g++编译器-l参数,用在add_executable之后(link_library用在add_excutable之前)

# 将hello动态库文件链接到可执行文件main target_link_libraries(main hello) 
  • 语法:target_link_libraries(target library1library2…)

add_subdirectory - 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置

# 添加src子目录,src中需有一个CMakeLists.txt add_subdirectory(src) 
  • 语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

aux_source_directory - 发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表

# 定义SRC变量,其值为当前目录下所有的源代码文件 aux_source_directory(. SRC) # 编译SRC变量所代表的源代码文件,生成main可执行文件 add_executable(main ${SRC}) 
  • 语法:aux_source_directory(dir VARIABLE)

CMake常用变量

CMAKE_C_FLAGS gcc编译选项
CMAKE_CXX_FLAGS g++编译选项

# 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 

CMAKE_BUILD_TYPE 编译类型(Debug, Release)

# 设定编译类型为debug,调试时需要选择debug set(CMAKE_BUILD_TYPE Debug) # 设定编译类型为release,发布时需要选择release set(CMAKE_BUILD_TYPE Release) 

CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
__BINARY_DIR

这三个变量指代的内容是一致的。 如果是 in source build,指的就是工程顶层目录。 如果是 out-of-source 编译,指的是工程编译发生的目录。 PROJECT_BINARY_DIR 跟其他指令稍有区别,不过现在,你可以理解为他们是一致的。 

CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
__SOURCE_DIR

这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。 也就是在 in source build时,他跟 CMAKE_BINARY_DIR 等变量一致。 PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。 

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中;

编译流程

在 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 

CMake代码实践

针对第五章写的两个小项目来写对应的CMakeLists.txt

最小CMake工程

# Set the minimum version of CMake that can be used cmake_minimum_required(VERSION 3.0) # Set the project name project (HELLO) # Add an executable add_executable(hello_cmake main.cpp) 

多目录工程 - 直接编译

cmake_minimum_required(VERSION 3.0) project(HELLOWORLD) #对应 -Iinclude include_directories(include) #main_cmake 是输出文件名 add_executable(main_cmake main.cpp src/swap.cpp) 

多目录工程 - 生成库编译

# Set the minimum version of CMake that can be used cmake_minimum_required(VERSION 3.0) #project name  project(SWAP_LIBRARY) #add compile options add_compile_options("-Wall -std=c++11") #set CMAKE_BUILD_TYPE set( CMAKE_BUILD_TYPE Debug ) # set output binary path  set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) ############################################################ # Create a library ############################################################ #Generate the static library from the library sources add_library( swap_library STATIC src/Swap.cpp ) target_include_directories( swap_lib PUBLIC ${PROJECT_SOURCE_DIR}/include ) ############################################################ # Create an executable ############################################################ # Add an executable with the above sources add_executable( swap_01 main.cpp ) # link the new swap_01 target with the swap_lib target target_link_libraries( swap_01 swap_liby ) 

CMake完整项目

. │ CMakeLists.txt │ main.cpp │ myexe.exe │ ├─build ├─include │ Gun.h │ Solider.h │ └─src Gun.cpp Solider.cpp 

main.cpp

#include "Gun.h" #include "Solider.h" void test(){ 
    Solider solider("xusanduo"); solider.addGun(new Gun("AK47")); solider.addBulletToGun(20); solider.fire(); } int main(){ 
    test(); return 0; } 

Gun.h

#pragma once #include<string> class Gun{ 
    public: Gun(std::string type){ 
    this->_bullet_count = 0; this->_type = type; } void addBullet(int bullet_num); bool shoot(); private: int _bullet_count; std::string _type; }; 

Solider.h

#pragma once #include<string> #include "Gun.h" class Solider{ 
    public: Solider(std::string name); void addBulletToGun(int num); bool fire(); void addGun(Gun *ptr_gun); ~Solider(); private: std::string _name; Gun * _ptr_gun; }; 

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 << "there is no bullet !" << endl; return false; } this->_bullet_count -= 1; cout << "shoot successfully" << endl; return true; } 

Solider.cpp

#include "Solider.h" Solider::Solider(std::string name){ 
    this->_name = name; this->_ptr_gun = nullptr; } void Solider::addGun(Gun *ptr_gun){ 
    this->_ptr_gun = ptr_gun; } void Solider::addBulletToGun(int num){ 
    this->_ptr_gun->addBullet(num); } bool Solider::fire(){ 
    this->_ptr_gun->shoot(); return true; } Solider::~Solider(){ 
    if(this->_ptr_gun == nullptr){ 
    return; } delete this->_ptr_gun; this->_ptr_gun = nullptr; } 
  1. 命令行编译
g++ .\main.cpp .\src\Gun.cpp .\src\Solider.cpp -Iinclude -o myexe 
  1. cmake编译
    CMakeLists.txt文件
cmake_minimum_required(VERSION 3.0) project(SOLIDER) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(CMAKE_BUILD_TYPE Debug) include_directories(include) #或者include_directories(${CMAKE_SOURCE_DIR}/include) add_executable(myexe main.cpp src/Gun.cpp src/Solider.cpp) 

编译

cd build cmake .. make 

当更改文件后 直接用make命令构建即可 make会只编译修改的文件

调试步骤launch.json和tasks.json

{ 
    "version": "0.2.0", "configurations": [ { 
    "name": "(gdb) 启动", "type": "cppdbg", "request": "launch", "program": "输入程序名称,例如 ${workspaceFolder}/a.exe", //可执行文件 "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", //进入当前工作目录 "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "/path/to/gdb", //gdb路径 "setupCommands": [ { 
    "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "build" //调试之前执行的任务 一般是生成exe文件 } ] } 

tasks.json

{ 
    "version": "2.0.0", "options": { 
    "cwd": "${workspaceFolder}/build" #进入build目录 }, "tasks": [ { 
    "type": "shell", #第一个task "label": "cmake", "command": "cmake", "args": [ ".." ] }, { 
    "label": "make", #第二个task "group": { 
    "kind": "build", "isDefault": true }, "command": "mingw32-make.exe", #win10下是mingw32.make linux下是make "args": [ ] }, { 
    "label": "Build", #task任务名 和launch.json中的preLaunchTask对应 "dependsOrder": "sequence", #顺序执行task任务 "dependsOn":[ "cmake", "make" ] } ] } 
THE END

发表回复