0%

CMake教程(二):创建一个简单的静态库

CMake Help
系列基于CMake Tutorial
本文基于Step 2: Adding a Library
Rather than placing all of the source files in one directory, we can organize our project with one or more subdirectories. In this case, we will create a subdirectory specifically for our library. Here, we can add a new CMakeLists.txt file and one or more source files. In the top level CMakeLists.txt file, we will use the add_subdirectory() command to add the subdirectory to the build.
Once the library is created, it is connected to our executable target with target_include_directories() and target_link_libraries().

创建生成一个静态库

考虑,我们需要一个数学库的开方运算。自建一个数学库。目录结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── CMakeLists.txt
├── MyProject.h.in
├── include
│ └── hello.h
├── libs
│ └── mathFunctions
│ ├── CMakeLists.txt
│ ├── mathfunctions.h
│ └── mysqrt.cpp
└── src
├── hello.cpp
└── main.cpp

mathfunctions.h声明方法,写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef _MATHFUNCTIONS_H_
#define _MATHFUNCTIONS_H_

namespace mymath {

/**
* @brief 一个简单的根号求解
*
* @param x 非负整数
* @return int x的算数平方根 舍去小数部分
*/
int mySqrt(int x);

} // namespace math

#endif // _MATHFUNCTIONS_H_

mysqrt.cpp实现开方运算这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "mathfunctions.h"

int mymath::mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ( static_cast<long long>(mid * mid) <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}

编辑libs/mathFunctions/CMakeList.txt文件。

我们要告知CMake创建一个库目标,向其添加文件。

1
2
# 为静态库MathFunctions库添加文件mysqrt.cpp
add_library(MathFunctions STATIC mysqrt.cpp)

这里我们定义了一个库MathFunctions,向其添加mysqrt.cpp文件。

在程序中链接静态库

我们返回MyProject项目的目录。修改CMakeList.txt文件。

为了使用我们自定义的MathFunctions库,我们需要告知CMake这个库的位置在哪。
MathFunctions定义在libs/mathFunctions/CMakeList.txt中。

1
2
# 添加子目录libs/mathFunctions并构建
add_subdirectory(libs/mathFunctions)

而后我们在告知CMake在生成可执行文件target1前要将库MathFunctions链接进来。

1
2
# 为target1链接库MathFunctions
target_link_libraries(target1 PUBLIC MathFunctions)

最后我们需要告知库MathFunctions的头文件在哪。

1
2
3
4
5
target_include_directories(target1 PUBLIC
"${PROJECT_BINARY_DIR}"
"include"
"libs/mathFunctions"
)

最后的CMakelists.txt文件为

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
29
30
31
32
33
34
35
36
37
38
39
40
41
# CMake最低版本要求 VERSION >= 3.25.0
cmake_minimum_required(VERSION 3.25.0)

# 设定项目名 以及版本号 Semantic Versioning
# 语义化版本 参考https://semver.org/
project(MyProject VERSION 1.0.0)

# 生成项目配置头文件
configure_file("MyProject.h.in"
"MyProject.h")

# 设置c++标准为C++17标准
set(CMAKE_CXX_STANDARD 17)
# 设置指定的C++编译器版本是必须的,如果不设置,或者为OFF,则指定版本不可用时,会使用上一版本。
set(CMAKE_CXX_STANDARD_REQUIRED on)
# 生成一个compile_commands.json文件,和基于clang的工具一起使用
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# 添加子目录libs/mathFunctions并构建
add_subdirectory(libs/mathFunctions)

# 搜索项目目录/src下的所有的源文件并赋值给变量sources
aux_source_directory(${PROJECT_SOURCE_DIR}/src sources)

# 为MyProject添加一个可执行目标target1
add_executable(target1)

# 为target1链接库MathFunctions
target_link_libraries(target1 PUBLIC MathFunctions)

# 指定目标target1包含的头文件路径
# PUBLIC表示当前目标target1可以使用这些头文件 如果外部目标依赖target1,也可以使用这些头文件
target_include_directories(target1 PUBLIC
"${PROJECT_BINARY_DIR}"
"include"
"libs/mathFunctions"
)

# Specifies sources to use when building a target and/or its dependents.
# 添加变量sources中的源文件给target1
target_sources(target1 PUBLIC ${sources})

修改./src/main.cpp文件,调用自定义库中的库函数。

1
2
3
4
5
6
7
8
9
#include "hello.h"

#include <mathfunctions.h>
#include <iostream>

int main() {
printHelloWorld();
std::cout << "100的算数平方根为" << mymath::mySqrt(100) << "\n";
}

执行命令,构建target1并运行。

1
cmake -B build && cmake --build build && ./build/target1

Hello World!
100的算数平方根为10

Exercise 2 - Making Our Library Optional

Now let us make the MathFunctions library optional. While for the tutorial there really isn’t any need to do so, for larger projects this is a common occurrence.

CMake can do this using the option() command. This gives users a variable which they can change when configuring their cmake build. This setting will be stored in the cache so that the user does not need to set the value each time they run CMake on a build directory.

我们为自定义的库创建一个宏定义USE_MYMATH去控制是否导入这个库。

修改CMakeLists.txt文件,添加

1
2
3
4
5
6
7
8
9
10
11
# 添加一个变量选项 用户能选择是否开启或关闭 ON为true OFF为false
option(USE_MYMATH "Use tutorial provided math implementation" ON)

if(USE_MYMATH)
# 添加子文件夹libs/mathFunctions
add_subdirectory(libs/mathFunctions)
# 把库MathFunctions添加入EXTRA_LIBS列表中去
list(APPEND EXTRA_LIBS MathFunctions)
# 添加库MathFunctions的头文件到变量EXTRA_INCLUDES
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/libs/mathFunctions")
endif()

修改

1
2
3
4
5
6
7
8
9
# 为target1链接库
target_link_libraries(target1 PUBLIC ${EXTRA_LIBS})

# 指定目标target1包含的头文件路径
# PUBLIC表示当前目标target1可以使用这些头文件 如果外部目标依赖target1,也可以使用这些头文件
target_include_directories(target1 PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)

最后的CMakeLists.txt文件为

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# CMake最低版本要求 VERSION >= 3.25.0
cmake_minimum_required(VERSION 3.25.0)

# 设定项目名 以及版本号 Semantic Versioning
# 语义化版本 参考https://semver.org/
project(MyProject VERSION 1.0.0)

# 生成版本号头文件
configure_file("MyProject.h.in"
"MyProject.h")

# 设置c++标准为C++17标准
set(CMAKE_CXX_STANDARD 17)
# 设置指定的C++编译器版本是必须的,如果不设置,或者为OFF,则指定版本不可用时,会使用上一版本。
set(CMAKE_CXX_STANDARD_REQUIRED on)
# 生成一个compile_commands.json文件,和基于clang的工具一起使用
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# 添加一个变量选项 用户能选择是否开启或关闭 ON为true OFF为false
option(USE_MYMATH "Use tutorial provided math implementation" ON)

if(USE_MYMATH)
# 添加子文件夹libs/mathFunctions
add_subdirectory(libs/mathFunctions)
# 把库MathFunctions添加入EXTRA_LIBS列表中去
list(APPEND EXTRA_LIBS MathFunctions)
# 添加库MathFunctions的头文件到变量EXTRA_INCLUDES
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/libs/mathFunctions")
endif()

# 添加提供查找头文件的目录
# 当前CMakeList.txt中的所有目标以及所有在其调用点之后添加的子目录中的所有目标将具有此头文件搜索路径
include_directories(${PROJECT_SOURCE_DIR}/include)

# 搜索项目目录src下的所有的源文件并赋值给变量sources
aux_source_directory(${PROJECT_SOURCE_DIR}/src sources)

# 为MyProject添加一个可执行目标target1
add_executable(target1)

# 为target1链接库
target_link_libraries(target1 PUBLIC ${EXTRA_LIBS})

# 指定目标target1包含的头文件路径
# PUBLIC表示当前目标target1可以使用这些头文件 如果外部目标依赖target1,也可以使用这些头文件
target_include_directories(target1 PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)

# Specifies sources to use when building a target and/or its dependents.
# 添加变量sources中的源文件给target1
target_sources(target1 PUBLIC ${sources})

USE_MYMATH加入配置文件MyProject.h.in

1
2
3
4
#define MyProject_VERSION_MAJOR @MyProject_VERSION_MAJOR@
#define MyProject_VERSION_MINOR @MyProject_VERSION_MINOR@
#define MyProject_VERSION_PATCH @MyProject_VERSION_PATCH@
#cmakedefine USE_MYMATH

在项目中使用USE_MYMATH控制,修改src/main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "MyProject.h"
#include "hello.h"

#ifdef USE_MYMATH
#include <mathfunctions.h>
#endif

#include <cmath>
#include <iostream>

int main() {
printHelloWorld();
#ifdef USE_MYMATH
std::cout << "100的算数平方根为" << mymath::mySqrt(100) << "\n";
#else
std::cout << "使用标准库函数。100的算数平方根为" << sqrt(100) << "\n";
#endif
}