添加库和链接库
在cmake中,如果要使用库,需要两步:添加库和链接库
- 添加库:将源代码编译成库文件的形式。
- 链接库:在编译可执行文件的过程中,link库文件。
在一个工程中,不可能把所有的源文件都写在一个目录下,通常是把不同功能的源文件放在不同的目录下,最外层只放置
main.cpp
、CMakeLists.txt
还有一些其他配置文件。- cmake如何使用不同目录下面的源文件呢?
- 将不同目录下面的源文件编译成库(添加库),在上层的CMakeLists.txt中链接这个库,就可以在上层源文件中调用子目录中的源文件。
- cmake如何使用不同目录下面的源文件呢?
我们的文件结构如下所示:
├── CMakeLists.txt ├── MathFunctions │ ├── CMakeLists.txt │ ├── MathFunctions.cpp │ ├── MathFunctions.h │ ├── mysqrt.cpp │ └── mysqrt.h ├── TestConfig.h.in └── test.cpp
1
2
3
4
5
6
7
8
9很显然,我们要在
test.cpp
中调用MathFunctions
目录下面的源文件中的函数,因此我们需要把该目录下面的源文件编译成库文件。- MathFunctions目录给出了一个典型库文件的写法,对外展示一个MathFunctions.cpp和MathFunctions.h文件,如果要扩充这个库(比如增加加法函数,我们叫做扩充这个库的细节),只需要增加myadd.cpp、myadd.h,并且声明在MathFunctions.cpp和MathFunctions.h文件即可。
MathFunctions.h:
- cpp
#ifndef __MATHFUNCTIONS_H__ #define __MATHFUNCTIONS_H__ #include "mysqrt.h" namespace mathfunctions { double sqrt(double x); } // namespace mathfunctions #endif
1
2
3
4
5
6
7
8
9
10
MathFunctions.cpp:
- cpp
#include "MathFunctions.h" double mathfunctions::sqrt(double x) { return mathfunctions::details::mysqrt(x); }
1
2
3
4
5
6
mysqrt.h:
- cpp
#ifndef __MYSQRT_H__ #define __MYSQRT_H__ namespace mathfunctions { namespace details { double mysqrt(double x); } // namespace details } // namespace mathfunctions #endif
1
2
3
4
5
6
7
8
9
10
11
12
mysqrt.cpp:
- cpp
#include "mysqrt.h" #include <iostream> double mathfunctions::details::mysqrt(double x) { if (x <= 0) { return 0; } double result = x; // do ten iterations for (int i = 0; i < 10; ++i) { if (result <= 0) { result = 0.1; } double delta = x - (result * result); result = result + 0.5 * delta / result; } return result; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
对于上述源文件和头文件,我们的想法是编译成一个库:MathFunctions。
有两种思路:
- 掺杂编译:
- 将所有的源文件混在一起编译,最终形成一个库。
- 分开编译:
- 将mysqrt.cpp编译成一个库:MathMysqrt,然后将MathMysqrt以及MathFunctions.cpp编译成库:MathFunctions。
- 掺杂编译:
这里我们选择分开编译:
首先,构建MathMysqrt库:
- cmake
add_library(MathMysqrt mysqrt.cpp) #添加MathMysqrt库,也就是编译这个源文件,生成库文件
1
2
接着,我们构建MathFunctions库:
- cmake
add_library(MathFunctions MathFunctions.cpp) #添加MathFunctions库,也就是编译这个源文件,生成库文件 add_library(MathMysqrt mysqrt.cpp) target_link_libraries(MathFunctions PRIVATE MathMysqrt) #编译MathFunctions的过程中,链接MathMysqrt库文件,这个链接过程设置为私有
1
2
3
4
5
6
7
最后,在上层CMakeLists.txt文件中调用MathFunctions库文件:
这里,我们要给上层调用者一个库的接口,通常,库所对应的头文件就是库的接口,即MathFunctions.h。
- cmake
cmake_minimum_required(VERSION 3.10) project(Test VERSION 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) configure_file(TestConfig.h.in TestConfig.h) add_subdirectory(MathFunctions) add_executable(Test test.cpp) target_link_libraries(Test PUBLIC MathFunctions) target_include_directories(Test PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions" )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 关于
add_subdirectory(MathFunctions)
:- 给出要调用的库的路径,实际上会执行该路径下的CMakeLists.txt,也就是编译出要使用的库文件。
关于
target_link_libraries(Test PUBLIC MathFunctions)
:- 在构建Test可执行文件的时候,公开链接MathFunctions这个库。
关于
target_include_directories
,要找到MathFunctions库对外的接口头文件。
test.cpp:
- cpp
#include <iostream> #include "TestConfig.h" #include "MathFunctions.h" int main(int argc, char *argv[]) { if (argc == 1) { std::cout << "Hello World" << std::endl; } else if (argc == 2) { std::string argv_1 = argv[1]; if (argv_1 == "--version") { std::cout << " Version " << Test_VERSION_MAJOR << "." << Test_VERSION_MINOR << std::endl; } else { double x = std::stod(argv_1); std::cout << argv_1 << " sqrt is: " << mathfunctions::sqrt(x) << std::endl; } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
构建信息:
- shell
[ 16%] Building CXX object MathFunctions/CMakeFiles/MathMysqrt.dir/mysqrt.cpp.o [ 33%] Linking CXX static library libMathMysqrt.a [ 33%] Built target MathMysqrt [ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.cpp.o [ 66%] Linking CXX static library libMathFunctions.a [ 66%] Built target MathFunctions Consolidate compiler generated dependencies of target Test [ 83%] Building CXX object CMakeFiles/Test.dir/test.cpp.o [100%] Linking CXX executable Test [100%] Built target Test
1
2
3
4
5
6
7
8
9
10- 可以看到,在构建Test可执行文件之前,先构建了MathFunctions库,在这之前还构建了MathMysqrt库。
执行结果:
./Test 10
- shell
10 sqrt is: 3.16228
1
如果我们有权限选择MathFunctions中的sqrt函数调用MathMysqrt中的mysqrt或者标准库中的sqrt,那么我们就需要在MathFunctions中以及MathFunctions对应的CMakeLists.txt中都设置条件,以符合条件编译。
- 当我们需要调用MathMysqrt中的mysqrt时,就需要编译MathMysqrt这个库文件。
- 当我们需要调用标准库中的sqrt时,就不需要编译MathMysqrt这个库文件。
CMakeLists.txt:
- cmake
add_library(MathFunctions MathFunctions.cpp) option(USE_MYMATH "Use my sqrt functions" ON) if (USE_MYMATH) target_compile_definitions(MathFunctions PRIVATE "USE MYMATH") add_library(MathMysqrt mysqrt.cpp) target_link_libraries(MathFunctions PRIVATE MathMysqrt) endif()
1
2
3
4
5
6
7
8
9
10 关于option:
- 使用option命令创建了一个变量(通常叫做编译定义)USE_MYMATH,这个变量被配置为ON。
关于
target_compile_definitions
:- 将USE_MYMATH这个编译定义传递到MathFunctions库中(相当于**#define声明宏**)。
如果USE_MYMATH这个编译定义被设置,那么将会编译MathMysqrt库,同时MathFunctions库由MathMysqrt库构建。
MathFunctions.cpp:
- cpp
#include "MathFunctions.h" double mathfunctions::sqrt(double x) { #ifdef USE_MYMATH return mathfunctions::details::mysqrt(x); #else return std::sqrt(x); #endif }
1
2
3
4
5
6
7
8
9
10
MathFunctions.h:
- cpp
#ifndef __MATHFUNCTIONS_H__ #define __MATHFUNCTIONS_H__ #ifdef USE_MYMATH #include "mysqrt.h" #else #include <cmath> #endif namespace mathfunctions { double sqrt(double x); } // namespace mathfunctions #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
开启USE_MYMATH编译构建:
- shell
[ 33%] Built target MathMysqrt Consolidate compiler generated dependencies of target MathFunctions [ 66%] Built target MathFunctions Consolidate compiler generated dependencies of target Test [100%] Built target Test
1
2
3
4
5- 可以发现,此时编译了MathMysqrt库文件。
关闭USE_MYMATH编译构建:
- shell
Consolidate compiler generated dependencies of target MathFunctions [ 50%] Built target MathFunctions Consolidate compiler generated dependencies of target Test [100%] Built target Test
1
2
3
4- 可以发现,此时没有编译MathMysqrt库文件。