推荐B站视频:1.1 Cmake构建项目的流程_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1xa4y1R7vT?p=1&vd_source=a934d7fc6f47698a29dac90a922ba5a3
>>目录
1)CMake初体验
- CMake构建流程
- Windows下使用CMake构建项目
- Linux下使用CMake构建项目
2)CMake语法
- 概述以及如何打印
- 变量操作set、list
- 流程控制
- 函数
- 作用域
- 宏
3)CMake构建项目的四种方式
- 直接写入源码路径的方式
- 调用cmake脚本的方式
- CMakeLists嵌套
- Object Libraries
4)CMake与静态库和动态库
- 如何生成静态库和动态库
- CMake如何调用静态库与动态库
5)CMake与源文件交互
实例展示
6)CMake条件编译
实例展示
第一章 CMake初体验
第一节:CMake是什么,CMake构建项目的流程
- CMake是一个开源的、跨平台的自动化构建工具,通过CMake我们可以轻松地
管理我们的项目,注意:
- CMake 并不是包管理工具!
- CMake 并不只支持C/C++
- 掌握CMake是学习C++必经之路
CMake的优点和缺点
- 优点:
- 操作透明而细腻
- 它专注于现代C++现代化,专注于支持C++现代编译器和工具链
- 真正的跨平台,支持比如Windows、Linux、MacOS,Cygwin(Linux移植到Windows)等
- 支持生成几乎所有主流IDE的项目
- 缺点:
- 它还在成长
- CMake它是一门语言,你需要学习
CMake严重阻碍了C++的发展吗???
CMake没有阻碍C++的发展,相反CMake拯救了C++!相比其他语言的构建工具,CMake自然要复杂的多,但这不是CMake的问题,相比其他的编程语言,C/C++更关注底层和构建其他的编程语言,C/C++更关注底层和构建其他的编程语言,因此CMake面临的问题和其他编程语言是完全不同的!而且正因为CMake的出现,C++才终于完成了项目跨平台!
C/C++源文件是怎么生成可执行程序
>>toolchain流程
- 预处理(-E参数 宏替换等)
- 编译 gcc/msvc/clang(-S参数)
- 汇编(-C参数 linux生成.o文件、windows生成.obj文件)
- 连接(将多个二进制文件连接生成一个可执行的文件)
CMake与Makefile、Make的关系
- Makefile并不是跨平台,CMake会根据编译器的类型来决定是否生成Makefile,大多数情况下CMake会生成Makefile
- 大型项目不推荐大家手动编写Makefile
- CMake工具(类似批处理工具)是通过调用makefile文件中的命令实现编译和链接的
CMake 命令行执行流程
1.编写CMakeLists.txt文件,下面是最基本的配置
- cmake_minimum_required(VERSION 3.20) #最小版本
- project(Hello)#项目名
- add_executable(Hello main.cpp)#生成可执行文件
2.cmake -B build(默认MSVC) / cmake -B build -G "MinGW Makefiles"(选择MinGW)
- 创建一个build并在此目录下生成makefile或其他文件
3.cmake --build build
- 生成项目
第二节:Windows下用CMake构建项目
- Windows下的CMake安装
- 官网下载CMake
- https://cmake.org/download/
Windows下的build system generator
- 默认MSVC(vs2022与vs2019)
- 可以安装 MinGW(gcc与clang)
- cmake参数:cmake -G <generator-name> -T <toolset-spec> -A <platform-name><path-to-source>
- 通过指定-G "MinGW Makefiles"来指定cmake使用gcc
通过例子来
- 给大家演示一下在windows下用cmake构建项目
第三节:Linux下使用CMake构建项目
- Linux下安装CMake
- sudo apt-get install cmake
- 源码安装
- 推荐源码安装,感受一下C++的编译速度
>>源码安装
sudo apt-get install build-essentialsudo wget https://cmake.org/files/v3.28/cmake-3.28.0.tar.gztar -zxvf cmake-3.28.0.tar.gzcd cmake-3.28.0./configuresudo makesudo make installcmake --version 检查是否安装成功
通过例子来
- 如何在linux下使用CMake
第二章 CMake语法
第一节:CMake Language概述
- CMake项目是基于CMakeLists.txt构建的,在CMakeLists.txt中(或者是*.cmake)我们用到的是CMake Language
- CMake Language的语法非常像一些命令式编程语言
- 执行从源树(CMakeLists.txt)的根文件开始
CMake命令行工具是由五个可执行文件构成
cmakectestcpackcmake-guiccmake
如果不通过CMakeLists.txt,运行CMake
- cmake -P *.cmake
- 以上用法很少在项目中用到,但适合学习CMake语法
- first.cmake
cmake_minimum_required(VERSION 3.28.0)
message("hello")
message(hello)
message("asfdfas
jiko")
message([[asasdsa
fdgh]])# 获取CMAKE中的信息
# ${}
message(${CMAKE_VERSION})
执行结果:
cmd cmake 3.28.1 7ms
asasdsa
fdgh
3.28.1
第二节:变量操作 set、list
- CMake中的变量分为两种
- CMake提供
- 自定义
- CMake变量的命名区分大小写
- CMake中的变量在存储时都是字符串
- CMake获取变量:${变量名}
- 变量的基础操作是set()与unset().但你也可以用list或是string操作变量
Set方法
- set(<variable><value>...[PARENT_SCOPE])
- set可以给一个变量设置多个值
- 变量内部存储时使用";"分割,但显示时只进行连接处理
List方法
- list(APPEND <list> [<element>...]) 列表添加元素
- list(REMOVE_ITEM <list> <value> [value...]) 列表删除元素
- ist(LENGTH <list> <output variable>) 获取列表元素个数
- list(FIND <list> <value> <out-var>) 在列表中查找元素返回索引
- list(INSERT <list> <index> <element>...) 在index位置插入
- list(REVERSE <list>) 反转list
- list(SORT <list>[...]) 排序list
list.cmake
cmake_minimum_required(VERSION 3.28.0)# 两种方式来常见Var
set(LISTVALUE a1 a2 a3)
message(${LISTVALUE})List(APPEND port p1 p2 p3)
message(${port})# 获取长度
list(LENGTH LISTVALUE len)
message(${len})list(FIND LISTVALUE "a2" index)
message(${index})list(REMOVE_ITEM port p1)
message(${port})list(APPEND LISTVALUE a5)
message(${LISTVALUE})list(INSERT LISTVALUE 3 a4)
message(${LISTVALUE})list(REVERSE LISTVALUE)
message(${LISTVALUE})list(SORT LISTVALUE)
message(${LISTVALUE})
执行结果:
cmd cmake 3.28.1 48ms
╭─ 15:56:45 | 27 Jan, Saturday | in D: Work cmake
╰─❯ cmake -P list.cmake
a1a2a3
1
p2p3
a1a2a3a5
a1a2a3a4a5
a5a4a3a2a1
a1a2a3a4a5
第三节:流程控制
- if条件流程控制
- loop循环流程控制
- break
- continue
条件IF
if(<condition>)<commands>elseif(<condition>)<commands>else() <commands>endif()
通过例子来
- CMake中的流程控制中的条件语句的用法
if.cmake
set(VARBOOL TRUE)
if(NOT VARBOOL)message(TRUE)
else()message(FALSE)
endif()if(1 LESS 2) message("1 LESS 2")
endif()if("ok" LESS 234) message("OK is less")
endif()if(2 EQUAL "2") message("2 EQUAL 2")
endif()
执行结果:
cmd cmake 3.28.1 51ms
╭─ 16:04:00 | 27 Jan, Saturday | in D: Work cmake
╰─❯ cmake -P if.cmake
FALSE
1 LESS 2
2 EQUAL 2
循环LOOP
- Forforeach(<loop_var> RANGE <max>)<commands>endforeach()foreach(<loop_var>RANGE<min><max>[<step>])foreach(<loop_variable>IN[LISTS<lists>][ITEMS<items>])- whilewhile(<condition>)<commands>endwhile()
例子演示:
for.cmake
cmake_minimum_required(VERSION 3.28.0)foreach(VAR RANGE 3) message(${VAR})
endforeach()
message("===============")set(MY_LIST 1 2 3)foreach(VAR IN LISTS MY_LIST ITEMS 4 f) message(${VAR})
endforeach()message("==================")
# zip
set(L1 one two three four five)
set(L2 1 2 3 4 5)foreach(num IN ZIP_LISTS L1 L2) message("word = ${num_0}, num = ${num_1}")
endforeach()
执行过程:
cmd cmake 3.28.1 52ms
╭─ 16:09:16 | 27 Jan, Saturday | in D: Work cmake
╰─❯ cmake -P for.cmake
0
1
2
3
===============
1
2
3
4
f
==================
word = one, num = 1
word = two, num = 2
word = three, num = 3
word = four, num = 4
word = five, num = 5
第四节:函数
- 定义函数的语法
function(<name>[<argument>...])<commands>endfunction()
演示:
func.cmake
cmake_minimum_required(VERSION 3.28.0)function(MyFunc FirstArg) message("MyFunc Name: ${CMAKE_CURRENT_FUNCTION}")message("First Argument: ${FirstArg}") # 第一个参数set(FirstArg "New Value") # 修改第一个参数的值message("First Argument: ${FirstArg}") # 输出修改后的值message("ARGV0 ${ARGV0}")message("ARGV1 ${ARGV1}")message("ARGV2 ${ARGV2}")
endfunction() set(FirstArg "first value")
MyFunc(${FirstArg} "value")
message("First Argument: ${FirstArg}") # 输出修改后的值
执行结果:
cmd cmake 3.28.1 51ms
╭─ 16:11:47 | 27 Jan, Saturday | in D: Work cmake
╰─❯ cmake -P func.cmake
MyFunc Name: MyFunc
First Argument: first value
First Argument: New Value
ARGV0 first value
ARGV1 value
ARGV2
First Argument: first value
第五节:Scope作用域
CMake 有两种作用域
- Function scope 函数作用域
- Directory scope 当从add_subdirectory()命令执行嵌套目录中的CMakeLists.txt列表文件时,注意父CMakeLists.txt其中的变量可以被子CMakeLists.txt使用
CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(scope)function(OutFunc)message("-> Out: ${Var}") set(Var 2) InFunc()message("<- Out: ${Var}")
endfunction()function(InFunc)message("-> In: ${Var}")set(Var 3)message("<- In: ${Var}")
endfunction()set(Var 1)
message("-> Global: ${Var}")
OutFunc()
message("<- Global: ${Var}")
执行命令:cmake -B build -G "MinGW Makefiles",查看执行结果
cmd cmake 3.28.1 77ms
╭─ 16:16:09 | 27 Jan, Saturday | in D: Work cmake scope
╰─❯ cmake -B build -G "MinGW Makefiles"
-- The C compiler identification is GNU 13.2.0
-- The CXX compiler identification is GNU 13.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-> Global: 1
-> Out: 1
-> In: 2
<- In: 3
<- Out: 2
<- Global: 1
-- Configuring done (2.4s)
-- Generating done (0.0s)
-- Build files have been written to: D:/Work/cmake/scope/build
第六节:宏
- CMake中的宏
macro(<name>[<argument>...])<commands>endmacro()
注意:尽量不要写宏,只要会读就好
演示:
cmake_minimum_required(VERSION 3.28.0)macro(Test myVar)set(myVar "new value") #创建了一个新的myVar变量message("argument : " ${myVar})
endmacro()set(myVar "First value")
message("myVar: ${myVar}")
Test("value")
message("myVar: ${myVar}")
执行结果:
cmd cmake 3.28.1 7ms
╭─ 16:20:42 | 27 Jan, Saturday | in D: Work cmake
╰─❯ cmake -P macro.cmake
myVar: First value
argument : value
myVar: new value
第三章 CMake 构建项目的四种方式
第一节:直接写入源码路径的方式
- add_excutable中直接写入相对路径
- 在源码中引入头文件时候需要写相对路径
第二节:调用子目录cmake脚本的方法
- include方法可以引入子目录中的cmake后缀的配置文件
- 将配置加入add_executable中
animal文件夹下有dog.h,cat.h,dog.cpp,cat.cpp,animal.cmake
- dog.h
#pragma once
#include <string>
class Dog{
public:std::string barking();
};
- dog.cpp
#include "dog.h"std::string Dog::barking()
{return "dog wang wang wang";
}
- cat.h
#pragma once
#include <string>
class Cat{
public:std::string barking();
};
- cat.cpp
#include "cat.h"std::string Cat::barking()
{return "cat miao miao miao";
}
- animal.cmake
set(animal_source animal/dog.cpp animal/cat.cpp)
- main.cpp
#include <iostream>
#include "animal/dog.h"
#include "animal/cat.h"
using namespace std;
int main(int argc,char const* argv[]) {Dog dog;std::cout<<dog.barking()<<std::endl;Cat cat;std::cout<<cat.barking()<<std::endl;return 0;
}
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(Dog)
include(animal/animal.cmake)
message(${animal_source})
add_executable(app main.cpp ${animal_source})
执行结果:
PS D:\Work\cmake\build> ."D:/Work/cmake/build/app.exe"
dog wang wang wang
cat miao miao miao
PS D:\Work\cmake\build>
第三节:CMakeLists嵌套
- target_include_directories 头文件目录的声明
- target_link_libraries 链接库
- add_subdirectory 添加子目录
- add_library 生成库文件
- 默认 STATIC library
animal文件夹下有dog.h,cat.h,dog.cpp,cat.cpp,CMakeLists.txt
- CMakeLists.txt
add_library(AnimalLib cat.cpp dog.cpp)
与animal文件夹同目录下的CMakeLists.txt
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(animalproject)
add_subdirectory(animal)
add_executable(app main.cpp)target_link_libraries(app PUBLIC AnimalLib)
target_include_directories(app PUBLIC "${PROJECT_SOURCE_DIR}/animal")
第四节:Object Libraries
- add_library OBJECT
- Object Library 是一个特殊的库类型,它将目标文件编译成一个库,但是不会生成最终的链接文件。这意味着你可以在后续的add_library()或add_executable()命令中,将Object Library作为源文件进行链接,从而生成最终的可执行文件或库文件
- 将target_include_directories()移入到子CMakeLists.txt中
(1)方式一
animal文件夹中的CMakeLists.txt
- CMakeLists.txt
add_library(AnimalLib OBJECT cat.cpp dog.cpp)
target_include_directories(AnimalLib PUBLIC .)
和animal文件夹同目录的CMakeLists.txt
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(Dog)
add_subdirectory(animal)
add_executable(app main.cpp)target_link_libraries(app PUBLIC AnimalLib)
(2)方式二,比较细粒度的分开cat和dog
animal文件夹中的CMakeLists.txt
- CMakeLists.txt
add_library(cat OBJECT cat.cpp)
target_include_directories(cat PUBLIC .)add_library(dog OBJECT dog.cpp)
target_include_directories(dog PUBLIC .)
和animal文件夹同目录的CMakeLists.txt
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(Dog)
add_subdirectory(animal)
add_executable(app main.cpp)target_link_libraries(app PUBLIC cat dog)
第四章 CMake与库
第一节:CMake 如何生动动态库/静态库
- 静态库
- 在连接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到
- 可执行文件中。因此对应的链接方式称为静态链接。
- 对函数库的链接是在编译时完成的!
- 动态库
- 动态库不是在编译时被链接到目标代码中,而是运行时才被载入
- 静态库对空间的浪费是巨大的!
- 命名
- 动态库的命名
- lib<name>.so/dll
- 静态库的命名
- lib<name>.a/lib
- 动态库的命名
- 命令
- file()常用于搜索源文件
- add_library(animal STATIC ${SRC})生成静态库
- add_library(animal SHARED ${SRC})生成动态库
- ${LIBRARY_OUTPUT_PATH}导出目录
通过例子来
- 使用cmake来生成静态库和动态库
创建include和src文件夹,include文件夹存放cat.h,dog.h,src文件夹存放cat.cpp,dog.cpp
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(Dog)file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)# 静态库
# set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/staticLib)
# add_library(animal STATIC ${SRC})# 动态库
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/sharedLib)
add_library(animal SHARED ${SRC})
第二节:CMake 如何调用静态库与动态库
静态库调用流程
- 引入头文件
- 链接静态库
- 生成可执行二进制文件
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(Animal CXX)include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/staticLib)
link_libraries(animal)
add_executable(app main.cpp)
动态库调用流程
- 引入头文件
- 声明库目录
- 生成可执行二进制文件
- 链接动态库
cmake_minimum_required(VERSION 3.28.0)
project(Animal CXX)#动态
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/sharedLib)
add_executable(app main.cpp)
target_link_libraries(app PUBLIC animal)
第五章 CMake与源文件的交互
animal文件夹下有dog.h,cat.h,dog.cpp,cat.cpp,CMakeLists.txt
- CMakeLists.txt
add_library(AnimalLib cat.cpp dog.cpp)
与animal文件夹同目录下的CMakeLists.txt
- CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(animalproject)# 设置C++的标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)configure_file(config.h.in config.h)add_subdirectory(animal)
add_executable(app main.cpp)target_link_libraries(app PUBLIC AnimalLib)
target_include_directories(app PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/animal")
- config.h.in
#define CMAKE_CXX_STANDARD 11
- main.cpp
#include <iostream>
#include "dog.h"
#include "cat.h"
#include "config.h"
using namespace std;
int main(int argc,char const* argv[]) {Dog dog;std::cout<<dog.barking()<<std::endl;Cat cat;std::cout<<cat.barking()<<std::endl;std::cout<<CMAKE_CXX_STANDARD<<std::endl;return 0;
}
第六章 CMake条件编译
animal文件夹下有dog.h,cat.h,dog.cpp,cat.cpp,cattwo.cpp,cattwo.h,CMakeLists.txt
- cattwo.h
#include <string>
namespace cattwo {std::string two();
}
- cattwo.cpp
#include "cattwo.h"
std::string cattwo::two() {return "two two mimi";
}
- cat.cpp
#include "cat.h"
#ifdef USE_CATTWO#include "cattwo.h"
#endif
std::string Cat::barking()
{
#ifdef USE_CATTWOreturn cattwo::two();
#elsereturn "cat miao miao miao";
#endif
}
- CMakeLists.txt
option(USE_CATTWO "Use cat two" ON)
# option(USE_CATTWO "Use cat two" OFF)
if(USE_CATTWO)set(SRC cat.cpp dog.cpp cattwo.cpp)
else()set(SRC cat.cpp dog.cpp)
endif()add_library(AnimalLib ${SRC})
if(USE_CATTWO)target_compile_definitions(AnimalLib PRIVATE "USE_CATTWO")
endif()
与animal文件夹同目录下的CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0)
project(animalproject)
add_subdirectory(animal)
add_executable(app main.cpp)target_link_libraries(app PUBLIC AnimalLib)
target_include_directories(app PUBLIC "${PROJECT_SOURCE_DIR}/animal")