该笔记是对CMake的初级入门,基于b站up主原子之音相关视频
# Cmake
# 第一章 认识Cmake
# 1.1 Cmake构建项目的流程
C++生成toolchain文件的流程
- 预处理(
-E
参数 宏替换等) - 编译
gcc/msvc/clang
(-S参数) - 汇编(
-C
参数linux
生成.o
文件、windows
生成.obj
文件) - 连接(将多个二进制文件连接生成一个可执行的文件)
Cmake命令行执行的流程
1.编写CMakeLists.txt
文件,下面是最基本的配置
cmake_minimum_required(VERSION 3.20)
#最小版本project(Hello)
#项目名add_executable(Hello hello.cpp)
#由源文件生成一个可执行的程序
2.cmake -B build
- 创建一个
build
并在此目录下生成makefile
或其他文件
3.cmake --build build
- 生成项目
# 1.2 Windows下使用Cmake
- 默认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
使用cmake步骤
创建
.cpp
文件创建
CMakeLists.txt
文件在文件中分别输入
cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(Hello hello.cpp)
2
3
4.在命令行中分别输入
cmake -B `name`
cmake build `name`
2
# 1.3 Linux下使用Cmake
源码安装
sudo apt-get install build-essential
sudo wget https://cmake.org/files/v3.28/cmake-3.28.0.tar.gz
tar -zxvf cmake-3.28.0.tar.gz
cd cmake-3.28.0
./configure
sudo make
sudo make install
cmake --version 检查是否安装成功
2
3
4
5
6
7
8
其余和在Window下操作相同,但是需要注意的是在Linux
下操作没有自动保存,要手动保存才能运行程序
# 第二章 Cmake的基础语法
# 2.1Cmake语法 message
使用cmake -P *.cmake
可以不通过CMakeLists.txt
来运行CMake
,便于语法的学习
在文件夹下创立.cmake
文件(注意需要手动保存)
#在first.cmake文件下进行操作
message("hello")
message(hello)
#两种方式均可以执行
#加了""可以实现多行打印
message("hel
lo")
#多行打印的第二种方式
message([[hel
lo]])
#获取cmake中的信息
message(${CMAKE_VERSION})
#在终端中输入 cmake -P first.cmake
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.2Cmake语法set和list
CMake
中的变量分为两种 CMake提供 自定义
CMake
变量的命名区分大小写CMake
中的变量在存储时都是字符串CMake
获取变量:变量名变量的基础操作是
set()
与unset()
,但你也可以用list
或是string
操作变量
set
set(<variable> <value>... [PARENT SCOPE])
set
可以给一个变量设置多个值- 变量内部存储时使用”
;
”分割,但显示时只进行连接处理
## set(变量 变量值)
set(Var1 aaa)
# 在设置set变量时可以存在空格,但是在调用的过程中需要加上转移字符`\`
set("My Var" aaa)
set([[My Var]] aaa)
message(${My\ Var})
# 在cmake中允许变量的多次定义。多次定义时,新的一次定义时对上一次定义的覆盖
# set设置多个值,有两种定义的方式
set(LISTVALUE a1 a2)
set(LISTVALUE a1;a2)
message(${LISTVALUE})
# 打印系统变量,如$PATH
message($ENV{PATH})
# 设置环境变量
set(ENV{CXX} "g++")
message($ENV{CXX})
#unset
unset(ENV{CXX})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
list
list(APPEND <list> [<element>...])
列表添加元素list(REMOVE TEM <list> <value> [value...])
列表删除元素list(LENGTH <list> <output variable>)
获取列表元素个数list(FIND <list> <value> <out-var>)
在列表中查找元素返回索引list(INSERT <list> <index> [<element>...])
在index
位置插入list(REVERSE <list>)
反转list
list(SORT <list> [...])
排序list
# 两种方式来创建Var
set(LISTVALUE a1 a2 a3)
message(${LISTVALUE})
list(APPEND port p1 p2 p3)
message(${port})
# 获取长度
list(LENGTH LISTVALUE len)
message(${len})
# 结果:3
# 寻找位置
list(FIND LISTVALUE a2 index)
message(${index})
# 结果:1
# 删除变量
list(REMOVE_ITEM port p1)
message(${port})
# 结果:p2p3
# 增加变量
list(APPEND LISTVALUE a5)
message(${LSITVALUE})
list(INSERT LISTVALUE 3 a4)
message(${LSITVALUE})
# 结果:a1a2a3a5
# 结果:a1a2a3a4a5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 2.3CMake流程控制
if(<condition>)
<commands>
elseif(<condition>)
<commands>
else()
<commands>
endif()
2
3
4
5
6
7
set(VARBOOL TRUE)
if(VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
if(NOT VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
# OR与"||"的作用相同
if(NOT VARBOOL OR VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
# AND与"&&"的作用相同
if(NOT VARBOOL AND VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
# 在cmake中LESS是"<"的意思
if(1 LESS 2)
message("1 LESS 2")
endif()
# 在cmake中EQUAL是"=="的意思
# 在cmake中2和"2"是相等的
if(2 EQUAL "2")
message("EQUAL")
endif()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
For
foreach(<loop_var> RANGE <max>)
<commands>
endforeach()
foreach(<loop_var> RANGE <min> <max>[<step>])
foreach(<loop_variable> IN [LISTS <lists>] [ITEMS <items>])
2
3
4
5
6
7
While
while(<condition>)
<commands>
endwhile()
2
3
4
foreach(VAR RANG 3)
message(${VAR})
endforeach()
#结果: 0
# 1
# 2
# 3
set(MY_LIST 1 2 3)
foreach(VAR IN LISTS MY_LIST ITEMS 4 f)
message(${VAR})
endforeach()
#结果: 0
# 1
# 2
# 3
# 4
# f
# zip
set(Ll one two three four)
set(L2 1 2 3 4 5)
foreach(num IN ZIP_LISTS L1 L2)
message("word = ${num_0}, num = ${num_1}")
endforeach()
#结果:
#word = one,num = 1
#word = two,num = 2
#word = three,num = 3
#word = four,num = 4
#word = ,num = 5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在cmake
中continue
和break
也能和其他语言一样正常使用。
# 2.4CMake语法 函数
定义函数的语法
function(<name>[<argument>...])
<commands>
endfunction()
2
3
cmake_minimum_required(VERSION 3.20.0)
# 在cmake函数中MyFunc为函数名,FirstArg是形参,在cmake中允许多个参数的传入。
function(MyFunc FirstArg)
# CMAKE_CURRENT_FUNCTION是cmake给的一个内置变量,用来表示函数的名称
message("MyFunc Name: ${CMAKE_CURRENT_FUNCTION}")
# 打印传的第一个参数
message("FirstArg ${FirstArg}")
set(FirstArg "New value")
message("FirstArg again:${FirstArg}")
#ARGV+数字 为cmake内置变量,用来表示传参进来的第几个变量
message("ARGV0 ${ARGV0}")
message("ARGV1 ${ARGV1}")
message("ARGV2 ${ARGV2}")
endfunction(MYFunc FirstArg)
set(FirstArg "first value")
MyFunc(${FirstArg} "value")
#可以看出在函数中对变量进行赋值对其本身并没有影响,及在函数中对变量的改变只在函数的作用域中生效
message("FirstArg ${FirstArg}")
# MyFunc Name: MyFunc
# FirstArg first value
# FirstArg again:New value
# ARGV0 first value
# ARGV1 value
# ARGV2
# FirstArg first value
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 2.5CMake作用域
CMake 有两种作用域
Function scope
函数作用域Directory scope
当从add subdirectory()
命令执行嵌套目录中的CMakeLists.txt
列表文件时,注意父CMakeLists.txt
其中的变量可以被子CMakeLists.txt
使用
# 函数的作用域只在函数体内,函数参数传值是值传递。
cmake_minimum_required(VERSION 3.20.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}")
# -> Global:1
# -> Out: 1
# -> In:2
# <- In:3
# <- Out:2
# <- Global:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 2.6CMake宏
CMake
中的宏
macro(<name>[<argument>...])
<commands>
endmacro()
2
3
注意:尽量不要写宏,只要会读就好
注意:尽量不要写宏,只要会读就好
macro(Test myVar)
set(myVar "new value") # 创建了一个新的myVar变量
message("argument: ${myVar}")
endmacro()
set(myVar "Fist value")
message("myVar: ${myVar}")
# 宏相当于把宏那部分的代码黏贴到这里
Test("value")
message("myVar: ${myVar}")
# myVar: Fist value
# argument: value
# myVar: new value
2
3
4
5
6
7
8
9
10
11
12
13
14
# 第三章 CMake构建项目的四张方式
# 3.1直接写入源码的方式
add executable
中直接写入相对路径- 在源码中引入头文件时需要写相对路径
//dog.h
#pragma once
#include <string>
class Dog{
public:
std::string barking();
};
//dog.cpp
#include "dog.h"
std::string Dog::barking()
{
return "YH wang wang";
}
//main.cpp
#include <iostream>
#include "animal/dog.h"//直接写入的源码的方式要求在头文件引入时写上相对路径
int main(int argc, char const *argv[])
{
Dog yh;
std::cout << yh.barking() << std::endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 在CMakeLists.txt文件中
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX) # CXX就是cpp
add_executable(Animal main.cpp animal/dog.cpp)
2
3
4
# 3.2调用子目录中的cmake脚本
include
方法可以引入子目录中的cmake
后缀的配置文件- 将配置加入
add executable
中
//cat.h
#pragma once
#include <string>
class Cat{
public:
std::string barking();
};
//cat.cpp
#include "cat.h"
std::string Cat::barking()
{
return "WKM miao miao";
}
//main.cpp
#include <iostream>
#include "animal/dog.h"
#include "animal/cat.h"
int main(int argc, char const *argv[])
{
Dog yh;
Cat wkh;
std::cout << yh.barking() << std::endl;
std::cout << wkh.barking() << std::endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# animal.cmake
set(animal_scources animal/dog.cpp animal/cat.cpp) #定义了一个名为animal_scources的变量
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
include(animal/animal.cmake) # 注意在使用的过程要先进行声明
add_executable(Animal main.cpp ${animal_scources})
2
3
4
5
6
7
8
9
# 3.3CmakeLists嵌套
target_include_directories
头文件目录的声明target_link_libraries
连接库文件add_subdirectory
添加子目录add_library
生成库文件 默认STATIC library
# animal/CMakeLists.txt
add_library(AnimalLib cat.cpp dog.cpp)
#CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
# 这个命令将在animal目录下查找并执行Animal库的CMakeLists.txt文件
add_subdirectory(animal)
# 定义了一个名为Animal的可执行文件,并指定了它的源文件为main.cpp
add_executable(Animal main.cpp)
# 将Animal可执行文件与AnimalLib库链接,使得Animal可执行文件可以使用AnimalLib库中定义的功能。
target_link_libraries(Animal PUBLIC AnimalLib)
# 将Animal可执行文件的包含目录设置为"${PROJECT_SOURCE_DIR}/animal",这样Animal可执行文件就可以包含Animal库的头文件。
target_include_directories(Animal PUBLIC "${PROJECT_SOURCE_DIR}/animal")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.4Object Library
add_library OBJECT
Object Library
是一个特殊的库类型,它将目标文件编译成一个库,但不会生成最终的链接文件。这意味着你可以在后续的add library
或add executable()
命令中,将Object Library
作为源文件进行链接,从而生成最终的可执行文件或库文件。- 将
target_include_directories
移入到子CMakeLists
中
# animal/CMakeLists.txt
add_library(AnimalLib OBJECT cat.cpp dog.cpp)
target_include_directories(AnimalLib PUBLIC .)
#CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
# 这个命令将在animal目录下查找并执行Animal库的CMakeLists.txt文件
add_subdirectory(animal)
# 定义了一个名为Animal的可执行文件,并指定了它的源文件为main.cpp
add_executable(Animal main.cpp)
# 将Animal可执行文件与AnimalLib库链接,使得Animal可执行文件可以使用AnimalLib库中定义的功能。
target_link_libraries(Animal PUBLIC AnimalLib)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第四章 CMake和库
# 4.1CMake生成静态库和动态库
静态库 在连接阶段,会将汇编生成的目标文件
.o
与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态连接。对函数库的连接是在编译时完成的!动态库 动态库不是在编译时被连接到目标代码中,而是运行时是才被载入静态库对空间的浪费是巨大的!
动态库的命名
lib<name>.so/dll
静态库的命名
lib<name>.a/lib
命令
file()
常用于搜索源文件add library(animal STATIC SSRC))
生成静态库add library(animal SHARED SSRC)
生成动态库SKLIBRARY_OUTPUT PATHI
导出目录
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 静态
# set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/a)
# add_library(animal STATIC ${SRC})
# 动态
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/so)
add_library(animal SHARED ${SRC})
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2CMake链接动态和静态库
- 静态库调用流程 1.引入头文件 2连接静态库 3.生成可执行二进制文件
- 动态库调用流程 1.引入头文件 2.声明库目录 3.生成可执行二进制文件 4.连接动态库
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
# 静态
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/a)
link_libraries(animal)
add_executable(app main.cpp)
#动态
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/so)
add_executable(app main.cpp)
target_link_libraries(app PUBLIC animal)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 第五章 CMake与源文件交互
需要先创建一个交互文件.h.in
文件,在交互文件中可以设定变量、宏等
#define CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD}
在CMakeLists.txt
中需要声明变量
cmake_minimum_required(VERSION 3.20.0)
project(Animal CXX)
# 设置c++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED T)
configure_file(config.h.in config.h)
add_subdirectory(animal)
add_executable(Animal main.cpp)
target_link_libraries(Animal PUBLIC AnimalLib)
target_include_directories(Animal PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/animal")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在main文件中引用
#include <iostream>
#include "dog.h"
#include "cat.h"
#include "config.h"
int main(int argc, char const *argv[])
{
Dog yh;
Cat wkh;
std::cout << yh.barking() << std::endl;
std::cout << wkh.barking() << std::endl;
std::cout << CMAKE_CXX_STANDARD << std::endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 第六章 CMake条件编译
通过不同传入参数编译不同的文件
- 用
option
定义变量 - 在子
CMakeLists.txt
中根据变量ON
还是OFF
来修改SRC
(源文件)以及target_compile_definitions
- 修改源文件根据变量选择代码
- 执行命令时
-D<变量>=ON/OFF
来进行条件编译
说明 PUBLIC INTERFACE 和PRIVATE的区别
- PUBLIC 本目标需要用,依赖这个目标的其他目标也需要
- INTERFACE 本目标不需要,依赖本目标的其他目标需要
- PRIVATE 本目标需要,依赖这个目标的其他目标不需要
//cattwo.h
#include "cattwo.h"
std::string cattwo::two()
{
return "two two mimi";
}
//cattwo.cpp
#include <string>
namespace cattwo
{
std::string two();
}
//cat.cpp
#include "cat.h"
#ifdef USE_CATTWO
#include "cattwo.h"
#endif
std::string Cat::barking()
{
#ifdef USE_CATTWO
return cattwo::two();
#else
return "cat mimi";
#endif
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# animal/CMakeLists
option(USE_CATTWO "Use cat two" ON)
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()
2
3
4
5
6
7
8
9
10
11
12
13
14
15