CMake 入门 (CMake Tutorial)

For English version you can check it from
CMake.

查看源码

环境: Ubuntu 16.04 64bit
CMake版本: 3.5.1

0. 构建

构建项目的流程:

$ cd build
$ cmake ..
$ make
$ make install          # if you want to install it 

修改配置后重新构建:

$ cd build 
$ rm -rf ./*
$ make

1. 快速开始

对于一些简单的项目,三行CMakeLists.txt就可以完成工作了。这个CMakeLists.txt是这样的:

cmake_minimum_required(VERSION 2.6)
project(Tutorial)
add_executable(Tutorial tutorial.cxx)

第一行规定了需要的最低CMake版本是2.6;第二行告诉我们项目名称为Tutorial;第三行列出生产可执行文件所需要的依赖关系。

在CMake中可以使用小写,大写或者大小写混合使用来编写CMakeLists.txt文件。

tutorial.cxx是一个很简单的C++程序,它可以根据命令行参数来计算平方根。第一版是这样的:

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

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

    double inputValue = atof(argv[1]);
    double outputValue =  sqrt(inputValue);

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

    return 0;
}

构建项目

由于在CMake构建过程中会生成很多中间文件,所以在顶层目录下新建一个build文件夹来存放产生的中间文件。

$ cd build
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jim/Workspaces/CMake-tutorial/build

可以看到在build目录中生成了一个Makefile文件,接下里可以直接使用make指令生成可执行文件。

使用CMake构建项目基本上都是这个流程,后面构建项目的方法也是如此。

添加一个版本号和配置头文件

我们要使用的第一个CMake功能就是为我们的项目添加一个版本号。当然可以直接在源代码中配置,但是在CMakeLists.txt中配置更加灵活。为了添加一个版本号,我们需要修改CMakeLists.txt文件:

cmake_minimum_required(VERSION 2.6)
project(Tutorial)
# The version number.
set(Tutorial_VERSION_MAJOR 1)
set(Tutorial_VERSION_MINOR 0)

# 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"
    )

# Add the binary tree to the search path for include files
# to the source code
include_directories("${PROJECT_BINARY_DIR}")

# Add the executable
add_executable(Tutorial tutorial.cxx)

在CMake中set指令用来定义变量,上面这个文件中定义了两个变量Tutorial_VERSION_MAJORTutorial_VERSION_MINOR

PROJECT_SOURCE_DIRPROJECT_BINARY_DIR是CMake中预先定义的两个变量,前者表示的是顶层CMakeLists.txt所在目录,后者是执行cmake的目录,对我们而言就是build目录。

configure_file命令根据第一个参数所指的文件在构建目录中生成一个头文件,由于我们需要使用这个头文件,所以要用include_directories命令包含这个目录。

我们需要创建一个TutorialConfig.h.in文件来构建头文件:

// The configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

我们需要修改一下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"

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]);
    double outputValue =  sqrt(inputValue);

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

    return 0;
}

2. 链接到库

我们将在项目中添加一个包含计算平方根功能的库。最终生成的可执行文件可以使用这个库而不是使用标准数学库中的函数。

我们会在项目中创建一个MathFunctions的子目录来存放我们库的源代码,这个子目录中应该有一个这样的CMakeLists.txt文件:

add_library(MathFunctions mysqrt.cxx)

这条命令添加了一个叫MathFunctions的库,这个库依赖mysqrt.cxx这个源文件。默认会生成静态库,如果要使用动态库的话需要添加SHARED属性:

# Use shared library
add_library(MathFunctions SHARED mysqrt.cxx)

# Use static library
add_library(MathFunctions STATIC mysqrt.cxx)

这里的SHARED和STATIC必须要使用大写。之后就会在构建目录下生成MathFunctions文件夹,其中包含了需要的动态库libMathFunctions.so或者静态库libMathFunctions.a

我们需要在MathFunctions目录下创建一个mysqrt.cxx文件,它需要包含一个mysqrt函数去实现求平方根的功能:

#include "MathFunctions.h"
#include <math.h>

double mysqrt(double inputValue)
{
    return sqrt(inputValue);
}

接着还需要创建MathFunctions.h这个头文件:

#ifndef MATHFUNCTIONS_H
#define MATHFUNCTIONS_H

double mysqrt(double inputValue);

#endif

我们需要在顶层的CMakeLists.txt文件中使用add_subdirectory来使库得到构建,我们还需包含一下这个目录,以便MathFunctions.h能够被搜索到:

cmake_minimum_required(VERSION 2.6)
project(Tutorial)
# The version number.
set(Tutorial_VERSION_MAJOR 1)
set(Tutorial_VERSION_MINOR 0)

# 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"
    )

# Add the binary tree to the search path for include files
# to the source code
include_directories("${PROJECT_BINARY_DIR}")
include_directories("${PROJECT_SOURCE_DIR}/MathFunctions")

add_subdirectory(MathFunctions)

# Add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial MathFunctions)

target_link_libraries是用来给项目链接库用的,第一个参数必须使用add_executable指令,第二个参数是要连接到的库。

选择是否使用库

可以在CMake中定义是否要使用库:

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

option命令可以定义一些选项,默认是OFF。

我们还需要修改一下顶层的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})

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 of %g is %g\n", 
        inputValue, outputValue);

    return 0;
}

最后我们只需要在TutorialConfig.h.in配置头文件中添加相应的宏定义:

#cmakedefine USE_MYMATH

并且吧configure_file命令移动到宏USE_MYMATH定义下面,否则将在定义宏之前生成头文件。

当我们设置USE_MYMATH宏为ON时,可以看到生成的头文件中定义了这个宏,并且能看到我们的可执行文件链接到了库:

$ ldd Tutorial
    linux-vdso.so.1 =>  (0x00007fff039eb000)
    libMathFunctions.so => /home/jim/Workspaces/CMake-tutorial/build/MathFunctions/libMathFunctions.so (0x00007f2e8a86e000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2e8a4a4000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2e8a19b000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f2e8aa70000)

3. 安装和测试

安装

我们可以给项目添加一下安装规则,这些规则目前都比较直白,如果要安装MathFunctions库,我们需要在MathFunctions目录下的CMakeLists.txt中添加一些安装规则:

install(TARGETS MathFunctions DESTINATION bin)
install(FILES MathFunctions.h DESTINATION include)

其中TARGETS用来安装可执行文件,FILES用来安装一些文件,比如头文件。
DESTINATION是安装的根目录,默认是/usr/local,当然可以制定CMAKE_INSTALL_PREFIX来指定安装根目录。

bin和include是根目录下的子目录。

对于项目生成的可执行文件和头文件,我们需要在顶层的CMakeLists.txt中添加规则:

# Add the install targets
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
    DESTINATION include)

构建完成,生成可执行文件之后就可以使用make install安装了:

$ cd build 
$ cmake ..
$ make
$ sudo make install 
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/Tutorial
-- Set runtime path of "/usr/local/bin/Tutorial" to ""
-- Installing: /usr/local/include/TutorialConfig.h
-- Installing: /usr/local/bin/libMathFunctions.so
-- Installing: /usr/local/include/MathFunctions.h

可以看到文件安装到了相应的目录下。

在构建是使用CMAKE_INSTALL_PREFIX指定安装根目录:

$ mkdir install
$ cd build 
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/Workspaces/CMake-tutorial/install
$ make
$ sudo make install 

测试

对于测试功能仅简要提及,如果想要深入了解,可以自行检索。
CMake中可以使用CTest来测试我们的代码,我们将在顶层的CMakeLists.txt文件中,添加几行测试代码来检验我们程序的正确性和完整性:

# Add some tests for our project
include(CTest)

# Does the application run?
add_test(TutorialRun Tutorial 25)

# Does it sqrt of 25?
add_test(TutorialComp25 Tutorial 25)
set_tests_properties(TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")

# Does it handle negative numbers?
add_test(TutorialNegative Tutorial -25)
set_tests_properties(TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0")

# Does it handle small numbers?
add_test(TutorialSmall Tutorial 0.0001)
set_tests_properties(TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")

# Does the usage message work?
add_test(TutorialUsage Tutorial)
set_tests_properties(TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")

当构建编译完成后,使用ctest可以查看测试结果:

$ ctest
Test project /home/jim/Workspaces/CMake-tutorial/build
    Start 1: TutorialRun
1/5 Test #1: TutorialRun ......................   Passed    0.00 sec
    Start 2: TutorialComp25
2/5 Test #2: TutorialComp25 ...................   Passed    0.00 sec
    Start 3: TutorialNegative
3/5 Test #3: TutorialNegative .................***Failed  Required regular expression not found.Regex=[-25 is 0
]  0.00 sec
    Start 4: TutorialSmall
4/5 Test #4: TutorialSmall ....................   Passed    0.00 sec
    Start 5: TutorialUsage
5/5 Test #5: TutorialUsage ....................   Passed    0.00 sec

80% tests passed, 1 tests failed out of 5

Total Test time (real) =   0.02 sec

The following tests FAILED:
      3 - TutorialNegative (Failed)
Errors while running CTest

如果想要测试一系列的输入量,可以定义一个宏来完成:

# Define a macro to simplify adding tests, then use it
macro(do_test arg result)
  add_test(TutorialComp${arg} Tutorial ${arg})
  set_tests_properties(TutorialComp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro(do_test)
 
# Do a bunch of result based tests
do_test(25 "25 is 5")
do_test(-25 "-25 is 0")

4. 平台相关性

添加一些和平台相关的特性,比如我们添加一些依赖于平台是否能提供log和exp功能的代码。当然,几乎所有的平台都支持这个特性。首先在顶层CMakeLists.txt中使用check_function_exists宏来检查是否提供这些功能:

# Does this system provide the log and exp functions?
include(CheckFunctionExists)
check_function_exists(log HAVE_LOG)
check_function_exists(exp HAVE_EXP)

然后,修改一下TutorialConfig.h.in配置文件,定义一下宏。当平台支持这些特性时,自动生成相应的宏定义:

// If we have both log and exp then use them
#if defined (HAVE_LOG) && defined (HAVE_EXP)
    result = exp(log(x)*0.5)
#else
//otherwise use an intertive approach
#endif

5. 使用CMake运行程序

这一章,我们将用CMake在构建过程中调用一个程序,生成一个平方根表文件。首先在MathFunctions目录中创建一个用来生成这个表的文件MakeTable.cxx:

// A simple program that builds a sqrt table
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[])
{
    int i;
    double result;

    // Make sure we have enough arguments
    if (argc < 2) {
        return 1;
    }

    // Open the output file
    FILE *fout = fopen(argv[1], "w");
    if (!fout) {
        return 1;
    }

    // Create a source file with a table of square roots
    fprintf(fout, "double sqrtTable[] = {\n");
    for (i = 0; i < 10; ++i) {
        result = sqrt(static_cast<double>(i));
        fprintf(fout, "%g,\n", result);
    }

    // Close the table with a zero
    fprintf(fout, "0};\n");
    fclose(fout);
    return 0;
}

这个程序根据命令行参数来确定生成文件的名字。

接下来要在MathFunctions下的CMakeLists.txt中添加相应的命令来生成平方根表文件:

# First we add the excutable that generates the table
add_executable(MakeTable MakeTable.cxx)

# Add the command to generate the source
add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    DEPENDS MakeTable
    )

# Add the binary tree directory to the search path for
# include files
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# Add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h)

在这个文件中,首先根据MakeTable.cxx生成可执行文件MakeTable,然后调用这个程序,在目标目录下生成名为Table.h的文件。

构建编译完成之后就能在build/MathFunctions目录下看到生成的文件。

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

推荐阅读更多精彩内容

  • CMake学习 本篇分享一下有关CMake的一些学习心得以及相关使用。 本文目录如下: [1、CMake介绍] [...
    AlphaGL阅读 12,219评论 11 79
  • 文章翻译自:CMake Tutorial 第一步 | 第二步 | 第三步 | 第四步 | 第五步 | 第六步 | ...
    汪坤阅读 13,965评论 1 23
  • 注:首发地址 1. 前言 当在做 Android NDK 开发时,如果不熟悉用 CMake 来构建,读不懂 CMa...
    cfanr阅读 24,231评论 1 53
  • 向您的项目添加 C 和 C++ 代码 本文内容 下载 NDK 和构建工具 创建支持 C/C++ 的新项目 构建和运...
    会飞的大象_阅读 3,749评论 0 3
  • 今天,我高中的好兄弟分享了一首歌给我,是周董的歌——《等你下课》。 我们以前在学校经常一起学周董的歌,唱周董的歌,...
    下课抽支烟阅读 193评论 0 0