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名会根据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> ...])
  • 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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容