CMake教程(4):添加库

2022-04-06  本文已影响0人  MemetGhini

在之前的基础上本文中将讲解如何把代码以库的形式引到工程中。在子目录中写一个简单的数学库,里面实现一个平方运算方法。本文中用到的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名会根据STATICSHARED成为name.a或name.so。
这里的STATICSHARED可不设置,通过全局的 BUILD_SHARED_LIBSFALSETRUE 来指定。最后就是源代码目录了。所以此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> ...])

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})

对源码也做同样的改造,用宏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
上一篇下一篇

猜你喜欢

热点阅读