CMake教程(4):添加库
在之前的基础上本文中将讲解如何把代码以库的形式引到工程中。在子目录中写一个简单的数学库,里面实现一个平方运算方法。本文中用到的CMake函数尽量只讲解本文中涉及到的部分。后面会专门会详细介绍平时常用的函数。
工程结构
先贴出组织的工程结构:
.
├── CMakeLists.txt ->根目录CMake配置
├── MathFunctions ->数学库目录
│ ├── CMakeLists.txt ->数学库CMake配置
│ ├── MathFunctions.h ->数学库头文件
│ └── MySquare.cpp ->数学库实现文件
├── Tutorial.cpp ->可执行文件源码
├── TutorialConfig.h ->工程配置头文件
└── build ->构建编译目录
实现数学库
首先,实现咱们简单的数学库MathFunctions
。数学库的头文件 MathFunctions.h
目前只声明一个函数。内容为:
double mySquare(double inputValue);
数学库实现文件MySquare.cpp
,把上面的函数实现一遍。内容为:
double mySquare(double inputValue)
{
return inputValue * inputValue;
}
就这样数学库的代码写好了。接下来就是怎么把这个组织为一个库的呢?
CMake组织库
需要用上CMake的add_library
方法。
add_library
作用是用指定文件给工程添加一个库。包括如下几种:
- 普通库
- 对象库
- 接口库
- 导入库
- 别名库
其参数如下:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
本文中先介绍普通库
。其中name
属性必须全局唯一。生成的library名会根据STATIC
或SHARED
成为name.a或name.so。
这里的STATIC
和SHARED
可不设置,通过全局的 BUILD_SHARED_LIBS
的 FALSE
或 TRUE
来指定。最后就是源代码目录了。所以此CMake文件内容为:
add_library(MathFunctions MySquare.cpp)
编写执行代码
库写好之后接下来就是把它引到咱们的可执行文件中。需要借助于CMake的add_subdirectory
函数。其作用为为工程添加一个子目录去编译。add_subdirectory
指定源文件和源CMakeLists.txt
文件的目录。所以根目录的CMakeLists.txt文件内容为:
cmake_minimum_required(VERSION 3.10)
project(Tutorial)
add_subdirectory(MathFunctions)
add_executable(Tutorial Tutorial.cpp)
#函数为把库连接到可执行文件中
target_link_libraries(Tutorial PUBLIC MathFunctions)
#指定头文件搜索目录
target_include_directories(Tutorial PUBLIC MathFunctions)
这两个函数后面文章中专门拿出篇幅来讲,暂时了解一下作用就行。
接下来写可执行文件代码,引相关头文件来使用它来验证了。可想而知其内容为:
#include <iostream>
#include <MathFunctions.h>
int main() {
double inputValue = 5.0;
double outputValue = mySquare(inputValue);
std::cout << "result: " << outputValue << std::endl;
return 0;
}
构建和编译项目:
> cmake ..
> make
> ./Tutorial
执行结果为:
> result: 25
符合项目预期。
如何让这个库变成可选?
此时需要了解一下cmake命令option
命令。并为根目录的cmake添加:
option(USE_MYMATH "Use code provided math implementation" ON)
然后按照这个选项的值来让编译器编译连接此库。为此我们把根目录的CMakelists.txt改造为。
if (USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()
用if检查选项值,在if块内用add_subdirectory把子目录添加进来。
用list命令来在列表中保存需要连接的库和需要导入的头文件。cmake中的list
时以;
分割的字符串。
list(APPEND <list> [<element> ...])
-
APPEND
为修改命令,为list中添加元素。 -
<list>
为list的变量名,如果当前作用域中不存在这个变量名则作为空list为其添加 - [<element> ...]为list添加的元素
ps:也可以用set来创建list,例如:set(var a b c d) var的值为a;b;c;d;然后照样可以用list的命令来处理var
之后在详细介绍此两个命令。目前简单理解就行。并且这种动态控制的方法是较为常见的形式。
add_executable(Tutorial Tutorial.cpp)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
- target_link_libraries是把我们的库连接到可执行文件
- target_include_directories是为可执行文件
Tutorial
添加头文件搜索目录,否则会找不到我们的头文件
对源码也做同样的改造,用宏USE_MYMATH来决定用哪一个平方函数:
#include <iostream>
#include <TutorialConfig.h>
#ifdef USE_MYMATH
#include "MathFunctions.h"
#else
#include <cmath>
#endif
int main()
{
double inputValue = 5.0;
#ifdef USE_MYMATH
double outputValue = mySquare(inputValue);
std::cout << "mySquare func called!" << std::endl;
#else
double outputValue = pow(inputValue, 2);
std::cout << "pow func called!" << std::endl;
#endif
std::cout << "result: " << outputValue << std::endl;
return 0;
}
由于我们在源码中用到了USE_MYMATH
,所以我们可以在TutorialConfig.h
中添加#cmakedefine USE_MYMATH
来让CMake为我们定义USE_MYMATH
宏。
configure_file
之前介绍过是复制一份到指定目录,并替换里面的变量。 #cmakedefine var
是如果var在cmake中有设定会被替换为#define VAR
否则/* #undef VAR */
相当于什么都不做。
ps:当然也可以不用TutorialConfig.h
这种形式来让cmake为我们定义USE_MYMATH,直接add_definitions(-DUSE_MYMATH)
来为源码库编译添加一个宏
接下来就编译看看是否符合预期。进入build目录
cmake .. -DUSE_MYMATH=OFF
或者
cmake .. -DUSE_MYMATH=ON