CMAKE学习笔记

[TOC]

1 CMakeLists.txt基础模板

1.1 最小项目

CMakeLists.txt

1
2
3
4
5
6
# 指定使用 CMake 的最低版本号
cmake_minimum_required (VERSION 2.6)
# 指定项目名称
project (Tutorial)
# 添加可执行文件
add_executable(Tutorial tutorial.cxx)

tutorial.cxx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}

// convert input to double
const double inputValue = atof(argv[1]);

// calculate square root
const double outputValue = sqrt(inputValue);
std::cout << "The square root of " << inputValue
<< " is " << outputValue
<< std::endl;
return 0;
}

1.2 构建、编译和运行

安装CMake、MinGW64并配置环境变量。终端进入到CMakeLists.txt目录,输入:

1
2
3
4
5
6
7
8
mkdir build
cd build
# 构建
cmake -G"MinGW Makefiles" ..
# 编译链接
cmake --build .
# 运行
.\Tutorial.exe 25
  • cmake -G<编译器名称> <CMakeLists.txt路径>
  • cmake –build <编译生成文件存放路径>

2 CMakeLists.txt优化

2.1 set 与 PROJECT_NAME

1
2
3
4
5
6
7
8
cmake_minimum_required(VERSION 2.6)
project(Tutorial)

## SET(<变量名> <变量值>)
SET(SRC_LIST tutorial.cxx)

# ${变量名}: 获取变量的值
add_executable(${PROJECT_NAME} ${SRC_LIST})

2.2 添加版本号和配置头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cmake_minimum_required (VERSION 2.6)
# 指定项目名及其版本号
project(Tutorial VERSION 1.0)
# 版本号也可以通过下面方式指定
## project(Tutorial)
## set (Tutorial_VERSION_MAJOR 1)
## set (Tutorial_VERSION_MINOR 0)
SET(SRC_LIST tutorial.cxx)

# 配置一个头文件,通过它向源代码中传递一些CMake设置。TutorialConfig.h.in需要我们编写,TutorialConfig.h自动生成
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" # TutorialConfig.h.in在源文件目录下
"${PROJECT_BINARY_DIR}/TutorialConfig.h" # TutorialConfig.h在生成的二进制文件树目录下
)
# 将二进制文件树添加到包含文件的搜索路径中,这样我们可以找到TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
# 添加可执行文件
add_executable(${PROJECT_NAME} ${SRC_LIST})

TutorialConfig.h.in

1
2
3
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

自动生成的TutorialConfig.h

1
2
3
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0

修改tutorial.cxx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#include <iostream>

int main (int argc, char *argv[]) {
if (argc < 2) {
// report version
std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
fprintf(stdout,"The square root of %g is %g\n", inputValue, outputValue);
return 0;
}

运行结果如下:

1
2
3
4
PS D:\CMAKE_Learn\build> .\Tutorial.exe
D:\CMAKE_Learn\build\Tutorial.exe Version 1.0
Usage: D:\CMAKE_Learn\build\Tutorial.exe number
PS D:\CMAKE_Learn\build>

2.3 指定 C++ 标准

1
2
3
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

3 添加库

一般我们将库的源码放在项目源文件目录下的MathFunctions子目录中,并在该子目录(该目录下包含头文件MathFunctions.h和相关源文件mysqrt.cxx)下新建一个CMakeLists.txt,添加如下内容:

1
2
# 添加一个叫 MathFunctions 的库文件
add_library(MathFunctions mysqrt.cxx)

此时项目文件结构如下:

1
2
3
4
5
6
7
8
9
CMAKE_Learn/
build/
MathFunctions/
CMakeLists.txt
MathFunctions.h
mysqrt.cxx
CMakeLists.txt
tutorial.cxx
TutorialConfig.h.in

再在顶层目录下的CMakeLists.txt文件中添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
# 向当前工程添加存放源文件的子目录,指定库所在的子目录
add_subdirectory(MathFunctions)
#生成可执行文件
add_executable(${PROJECT_NAME} tutorial.cpp)
# 指定将MathFunctions库链接到项目Tutorial的可执行文件
target_link_libraries(${PROJECT_NAME} PUBLIC MathFunctions)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(${PROJECT_NAME} PUBLIC
${PROJECT_BINARY_DIR} # CMake生成的TutorialConfig.h存放目录
${PROJECT_SOURCE_DIR}/MathFunctions # 库的头文件存放目录
)

CMake中target_xxx关键字指定编译给定目标时相关内容,在上面命令中即指定Tutorial这个项目编译过程中包含的头文件目录和库文件目录。

4 将库设置为可选项

第一步是向顶级 CMakeLists.txt 文件添加一个选项。

1
option(USE_MYMATH "Use tutorial provided math implementation" ON)

option 表示提供用户可以选择的选项。命令格式为:option(<variable> "description [initial value])USE_MYMATH 选项的缺省值为 ON

下一步创建if语句,

1
2
3
4
5
6
7
8
9
10
11
12
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES ${PROJECT_SOURCE_DIR}/MathFunctions)
endif()

add_executable(${PROJECT_NAME} tutorial.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC ${EXTRA_LIBS})
target_include_directories(${PROJECT_NAME} PUBLIC
${PROJECT_BINARY_DIR}
${EXTRA_INCLUDES}
)

APPEND表示将元素MathFunctions追加到列表EXTRA_LIBS中,将元素 ${PROJECT_SOURCE_DIR}/MathFunctions 追加到列表EXTRA_INCLUDES中。EXTRA_LIBS 存储 MathFunctions 库,EXTRA_INCLUDES 存储 MathFunctions 头文件。因此在target_link_librariestarget_include_directories中用${EXTRA_LIBS}${EXTRA_INCLUDES}分别获取库文件和头文件目录。

当然也可以通过TutorialConfig.h.in配置文件实现上述效果,修改tutorial.cxx如下:

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
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"

#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif

int main (int argc, char *argv[]) {
if (argc < 2) {
fprintf(stdout,"%s Version %d.%d\n", argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
double inputValue = atof(argv[1]);

#ifdef USE_MYMATH
double outputValue = mysqrt(inputValue);
#else
double outputValue = sqrt(inputValue);
#endif

fprintf(stdout,"The square root of %g is %g\n", inputValue, outputValue);
return 0;
}

TutorialConfig.h.in中添加一行,添加宏USE_MYMATH

1
#cmakedefine USE_MYMATH

在构建项目时可以选择性地使用库:

1
2
# 在构建项目时不使用库
cmake -DUSE_MYMATH=OFF ..

5 添加库的使用要求

库的使用要求有三个:

  • INTERFACE:表示用户需要,开发者不需要;
  • PRIVATE:表示用户不需要,开发者需要;
  • PUBLIC:表示用户和开发者都需要。

6 动态库和静态库构建

1
2
3
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)

添加一个名为<name>的库文件,该库文件将会根据调用的命令里列出的源文件来创建。STATIC为静态库,SHARED为动态库。

静态库和动态库的区别

  • 静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”;
  • 静态库在编译链接阶段会直接整合到目标程序中,编译链接成功的可执行文件可独立运行;
  • 动态库在编译时不会放到链接的目标程序中,而是在运行阶段加载,即可执行文件无法单独运行。

7 包含第三方库

1
2
3
find_package(<package> [version] [EXACT] [QUIET]
[[REQUIRED|COMPONENTS] [components...]]
[NO_POLICY_SCOPE])

查找并加载第三方库。该命令会设置<package>_FOUND变量,用来指示要找的包是否被找到了。REQUIRED选项表示如果报没有找到的话,cmake的过程会终止,并输出警告信息。[version]参数为第三方库的最低版本。COMPONENTS选项后面可以列出要查找的库列表(components list)。

参考

[1] https://zhuanlan.zhihu.com/p/500002865

[2] CMake Tutorial — CMake 3.25.1 Documentation

[3] https://blog.csdn.net/qq_44074143/article/details/123244847