CMake教程读书笔记-第二步,添加一个库

第二步,添加一个库

相关代码:2_addlib

现在,我们向项目中添加一个自己实现的库(即 MathFunctions)。这个库包含一个我们自己实现的、计算一个数平方根的函数(即 mysqrt),可执行文件可以使用这个库函数来替代编译时提供的标准平方根库函数(即 sqrt)。

添加一个库到项目中,会涉及到:增加模块开关(注意cmake的变量和代码的宏似乎可以不同名)、为项目添加逻辑模块、包含接口文件、添加编译子目录、添加链接选项等,下面会详细举例说明。

自定义库的 MathFunctions/CMakeLists.txt

这里,我们将库放置到 MathFunctions 子目录中。然后在其 CMakeLists.txt 中(注意,是 MathFunctionsCMakeLists )加入这样一行:

add_library(MathFunctions mysqrt.cxx)

MathFunctions/mysqrt.cxx

源文件 mysqrt.cxx 定义了一个叫做 mysqrt 的函数,这个函数功能和之前编译提供的 sqrt 类似。

#include <stdio.h>
double mysqrt(int n)
{
        fprintf(stderr, "custom square root, not implementated currently.\n");
        return 0;
}

MathFunctions/MathFunctions.h

头文件定义在源文件目录中,稍后会添加到包含路径。内容如下:

#ifndef __math_functions_h__
#define __math_functions_h__
double mysqrt(int n);
#endif

顶级 CMakeLists.txt

为了能够使用新实现的库,我们在顶级 CMakeLists.txt 中添加 add_subdirectory 调用,这样这个库就能够被编译到。

同时我们也添加了另外一个包含目录(即 ${PROJECT_SOURCE_DIR}/MathFunctions),以便声明了函数的 MathFunctions/mysqrt.h 头文件能够被找到。

最后添加新库链接到 executable 中(即 target_link_libraries ),这样顶级 CMakeLists.txt 文件的最后几行看起来是这个样子:

...skip above lines...
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions) 
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)

MathFunctions 设置为可选

接下来,我们看看如何让 MathFunctions 库变得可选。这对于大型库或者依赖第三方代码的库,很有必要。

首先,在顶级 CMakeLists.txt 文件中添加一个选项:

# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON)

这会交互显示一个用户可以改变的 CMake 选项(在使用 ccmake 生成 Makefile ,可以看到一个图形交互界面; 使用 cmake -i 可以根据交互提示设定选项),选项的默认值为 ON 。 这个设置将会保存在缓存中,这样用户不用在每次为这个项目运行 CMake 的时候必须选择设置它们。

下面为使 MathFunctions 库的编译和链接依照选项对应的特定条件进行,我们将顶级 CMakeLists.txt 文件的的末尾修改成这个样子:

# add the MathFunctions library?
#
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})

这里,使用 USE_MYMATH 的设置来确定 MathFunctions 是否应当被编译和使用。这里需要注意,为了收集按特定条件将被链接到可执行文件的可选库,我们使用了一个变量(这里是指 EXTRA_LIBS )。这也是用来维护具有许多选项组件的大项目的常用方法。

tutorial.cxx

相应的,我们直接修改代码如下:

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif

int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n", argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }

  double inputValue = atof(argv[1]);

#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
#else
  double outputValue = sqrt(inputValue);
#endif

  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

TutorialConfig.h.in

在前面代码中,我们使用了 USE_MYMATH ,但是这个宏在源代码的角度上,却不是直接就存在的。

通过向 TutorialConfig.h.in 加入下面行,可以让 CMake 借助 TutorialConfig.h.in 配置文件,让我们可以在代码中使用 USE_MYMATH 宏。

#cmakedefine USE_MYMATH

详细过程

下面通过时间回顾一下过程。

  1. 原始文件目录结构

    $ls -p
    tree.cmakelog  tree.cleanlog  tree.makelog  tree.origin  tutorial/
    
    $tree . >tree.origin
    
    $cat tree.origin
    .
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    └── tutorial
        ├── CMakeLists.txt
        ├── MathFunctions
        │   ├── CMakeLists.txt
        │   ├── MathFunctions.h
        │   └── mysqrt.cpp
        ├── TutorialConfig.h.in
        └── tutorial.cpp
    
    2 directories, 10 files
    

    这里, tree.cmakelog, 与 tree.cleanlog, tree.makelog, tree.origin 等都是用于记录输出日志的文件。

  2. cmake -i 通过选项的交互选择生成 Makefile

    $cmake -i tutorial/
    Would you like to see advanced options? [No]:
    Please wait while cmake processes CMakeLists.txt files....
    
    Variable Name: CMAKE_BUILD_TYPE
    Description: Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
    Current Value: 
    New Value (Enter to keep current value): 
    
    Variable Name: CMAKE_INSTALL_PREFIX
    Description: Install path prefix, prepended onto install directories.
    Current Value: /usr/local
    New Value (Enter to keep current value): 
    
    Variable Name: USE_MYMATH
    Description: Use tutorial provided math implementation
    Current Value: ON
    New Value (Enter to keep current value): 
    
    Please wait while cmake processes CMakeLists.txt files....
    
    CMake complete, run make to build project.
    
    $tree . >tree.cmakelog
    $cat tree.cmakelog
    .
    ├── CMakeCache.txt
    ├── CMakeFiles
    │   ├── CMakeCCompiler.cmake
    │   ├── cmake.check_cache
    │   ├── CMakeCXXCompiler.cmake
    │   ├── CMakeDetermineCompilerABI_C.bin
    │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   ├── CMakeDirectoryInformation.cmake
    │   ├── CMakeOutput.log
    │   ├── CMakeSystem.cmake
    │   ├── CMakeTmp
    │   │   └── CMakeFiles
    │   │       └── cmTryCompileExec.dir
    │   ├── CompilerIdC
    │   │   ├── a.out
    │   │   └── CMakeCCompilerId.c
    │   ├── CompilerIdCXX
    │   │   ├── a.out
    │   │   └── CMakeCXXCompilerId.cpp
    │   ├── Makefile2
    │   ├── Makefile.cmake
    │   ├── progress.marks
    │   ├── TargetDirectories.txt
    │   └── Tutorial.dir
    │       ├── build.make
    │       ├── cmake_clean.cmake
    │       ├── DependInfo.cmake
    │       ├── depend.make
    │       ├── flags.make
    │       ├── link.txt
    │       └── progress.make
    ├── cmake_install.cmake
    ├── Makefile
    ├── MathFunctions
    │   ├── CMakeFiles
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── MathFunctions.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── cmake_clean_target.cmake
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   └── progress.make
    │   │   └── progress.marks
    │   ├── cmake_install.cmake
    │   └── Makefile
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    ├── tutorial
    │   ├── CMakeLists.txt
    │   ├── MathFunctions
    │   │   ├── CMakeLists.txt
    │   │   ├── MathFunctions.h
    │   │   └── mysqrt.cpp
    │   ├── TutorialConfig.h.in
    │   └── tutorial.cpp
    └── TutorialConfig.h
    
    12 directories, 49 files
    

    这里,也可通过 ccmake 图形方式交互生成对应选项的 Makefile, 或者 cmake -DUSE_MYMATH=ON 。另外,直接运行 cmake 并没有像 CMakeLists.txt 那样默认使用 USE_MYMATH=ON , 但是,再次运行 cmake 则会发现启用了该选项。 这个option命令和你本地是否存在编译缓存的关系很大。

    源码目录 tutorial 仍旧不变,头文件 MathFunctions.h 也仍旧在源码目录中。

  3. make 编译

    $make
    Scanning dependencies of target MathFunctions
    [ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cpp.o
    Linking CXX static library libMathFunctions.a
    [ 50%] Built target MathFunctions
    Scanning dependencies of target Tutorial
    [100%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cpp.o
    Linking CXX executable Tutorial
    [100%] Built target Tutorial
    
    $tree . >tree.makelog
    $cat tree.makelog
    .
    ├── CMakeCache.txt
    ├── CMakeFiles
    │   ├── CMakeCCompiler.cmake
    │   ├── cmake.check_cache
    │   ├── CMakeCXXCompiler.cmake
    │   ├── CMakeDetermineCompilerABI_C.bin
    │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   ├── CMakeDirectoryInformation.cmake
    │   ├── CMakeOutput.log
    │   ├── CMakeSystem.cmake
    │   ├── CMakeTmp
    │   │   └── CMakeFiles
    │   │       └── cmTryCompileExec.dir
    │   ├── CompilerIdC
    │   │   ├── a.out
    │   │   └── CMakeCCompilerId.c
    │   ├── CompilerIdCXX
    │   │   ├── a.out
    │   │   └── CMakeCXXCompilerId.cpp
    │   ├── Makefile2
    │   ├── Makefile.cmake
    │   ├── progress.marks
    │   ├── TargetDirectories.txt
    │   └── Tutorial.dir
    │       ├── build.make
    │       ├── cmake_clean.cmake
    │       ├── CXX.includecache
    │       ├── DependInfo.cmake
    │       ├── depend.internal
    │       ├── depend.make
    │       ├── flags.make
    │       ├── link.txt
    │       ├── progress.make
    │       └── tutorial.cpp.o
    ├── cmake_install.cmake
    ├── Makefile
    ├── MathFunctions
    │   ├── CMakeFiles
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── MathFunctions.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── cmake_clean_target.cmake
    │   │   │   ├── CXX.includecache
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.internal
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   ├── mysqrt.cpp.o
    │   │   │   └── progress.make
    │   │   └── progress.marks
    │   ├── cmake_install.cmake
    │   ├── libMathFunctions.a
    │   └── Makefile
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    ├── tutorial
    │   ├── CMakeLists.txt
    │   ├── MathFunctions
    │   │   ├── CMakeLists.txt
    │   │   ├── MathFunctions.h
    │   │   └── mysqrt.cpp
    │   ├── TutorialConfig.h.in
    │   └── tutorial.cpp
    ├── Tutorial
    └── TutorialConfig.h
    
    12 directories, 57 files
    

更多信息

通常过程

代码如下

$ tree .
.
└── tutorial
    ├── CMakeLists.txt
    ├── MathFunctions
    │   ├── CMakeLists.txt
    │   ├── MathFunctions.h
    │   └── mysqrt.cpp
    ├── TutorialConfig.h.in
    └── tutorial.cpp

2 directories, 6 files

$ mkdir build_on && build_on
$ cmake -DUSE_MYMATH=ON ../tutorial
$ make
$ ./Tutorial 
./Tutorial Version 1.0
Usage: ./Tutorial number
$ ./Tutorial 25
custom square root.
The square root of 25 is 0

关于添加选项

必须在 configure_file 之前

注意:添加的选项必须在 configure_file 配置前面,如下:

......
# should we use our own math functions?                                                                                                                  option (USE_MYMATH "Use tutorial provided math implementation" ON) 

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
    ) 
......

否则,option将不起作用,比如:

......
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
    ) 

# should we use our own math functions?                                                                                                                  option (USE_MYMATH "Use tutorial provided math implementation" ON) 
......

关于 xxxconfig.h.in 中的 #cmakedefine#define, 和 CMakefileLists.txt 中的 add_definitions()

根据 [[https://cmake.org/pipermail/cmake/2011-March/043464.html][#cmakedefine vs #define]]CMAKE 中 add_definitions的用法 可知,

  • config.h.in 中用 #cmakedefine 定义的宏 VAR ,会根据 CMakeLists.txt 中是否有 VAR 变量,在 config.h 中进行 #define VAR#undefine VAR
  • config.h.in 中用 #define 定义的宏 VAR ,使用 @VAR@ 引用 CMakeLists.txtVAR 变量的值,会直接在 config.h#define VAR
  • CMakeLists.txt 中使用 add_definitions(-DVAR), 其实是直接对编译器添加 -DVAR 选项(如果不关心规范cmake可借此加入其它编译选项而非仅仅 -D ),导致代码有 #define VAR 的效果。

其它的方法

除了前面的方法,实际上,更方便添加选项的方法还有:

添加如下内容到 CMakeLists.txt

project(Tutorial)
     
option(USE_MYMATH "Use tutorial provided math implementation" OFF)
if (USE_MYMATH)
add_definitions(-DTEST_DEBUG)
endif()

源代码宏

#ifdef USE_MYMATH
......
#endif

编译打开选项

$cmake -DUSE_MYMATH=ON tutorial
$cmake --build tutorial

这样不用写 TutorialConfig.h.in 文件了。 opt 对应的是变量,其值里面的 ON/OFF 控制的是同名宏的定义与否,而非宏值。

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

推荐阅读更多精彩内容