C++ - cmake学习笔记

  • cmake 内置命令是不区分大小写的
  • 但是注意区分变量的大小写

aux_source_directory

  • 在目录中查找所有源文件
    1
    aux_source_directory(<dir> <variable>)

例子

  • 工程格式

    1
    2
    3
    4
    5
    6
    7
    ./Demo2
    |
    +--- main.cc
    |
    +--- MathFunctions.cc
    |
    +--- MathFunctions.h
  • 写法1

    1
    2
    3
    4
    5
    6
    7
    8
    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)

    # 项目信息
    project (Demo2)

    # 指定生成目标
    add_executable(Demo main.cc MathFunctions.cc)
  • 写法2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)

    # 项目信息
    project (Demo2)

    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)

    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})

打印变量

  • status 表示这是一般的打印信息
    1
    2
    set(USER_KEY, "Hello World")
    message( STATUS "this var key = ${USER_KEY}.")

Set 的用法

Set赋值给一般变量(normal variables)

  • 什么是一般变量,一般变量和代码中变量相似,仅在自身所在作用域起作用,除非后面使用PARENT_SCOPE。

  • 每一个新的目录或者函数都会创建新的作用域,当在新的作用域创建一般变量且后面加上PARENT_SCOPE,该变量可以在父目录或者调用新函数的函数上起作用。

  • 举个栗子:

    1
    2
    set(FOO  “x”)。         //FOO作用域为当前作用域.
    set(FOO "x" PARENT_SCOPE)   //FOO作用域跳上一级.

Set赋值给缓存变量(cache variables)

  • 什么是缓存变量,缓存变量可以理解为当第一次运行cmake时,这些变量缓存到一份文件中(即编译目录下的CMakeCache.txt)。当再次运行cmake时,这些变量会直接使用缓存值,可以利用ccmake或者cmake-gui等工具来重新赋值。缓存变量在整个cmake运行过程中都可以起作用。

  • 当使用CACHE时,且缓存(cache)中没有该变量时,变量被创建并存入缓存(cache)中,如果原缓存(cache)中有该变量,也不会改变原缓存中该变量的值,除非后面使用FORCE。

  • 举个栗子:

    1
    2
    3
    set(FOO, "x" CACHE <type>)
    //原缓存中没有FOO则将FOO赋值为x且存入cache中。
    //原缓存中有FOO则不做任何改变,即便原cache中FOO存的不是x。
1
2
3
4
set(FOO, "x" CACHE <type><docstring> FORCE)    
//即便原cache中存在FOO也会创建另一个FOO,官方文档原话(If FORCE is specified, the value of the cache variable
//is set, even if the variable is already in the cache.This should normally be avoided, as it will
//remove any changes to the cache variable’s value by the user.),小弟笨拙没有搞懂。
  • 使用CACHE的同时,要设定<type><docstring><type>可以理解为所存入变量类型,<docstring>为变量的描述。

  • <type>分为以下几种类型:

    1
    2
    3
    4
    5
    FILEPATH = File chooser dialog.
    PATH = Directory chooser dialog.
    STRING = Arbitrary string.
    BOOL = Boolean ON/OFF checkbox.
    INTERNAL = No GUI entry (used for persistent variables).
  • 其中INTERNAL将变量为内部变量,即cmake-gui不会向用户显示这类变量,而其它类型的缓存变量用户都可以通cmake-gui按照特定的类型改变。

例子:

  • 如果编译的时候没有带 CMAKE_BUILD_TYPE 参数的话,则会默认为 Release
    1
    2
    3
    4
    5
    6
    7
    8
    #---------------------------------------------------------------------------------------
    # Set default build type to release
    #---------------------------------------------------------------------------------------
    if (UNIX)
    if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
    endif ()
    endif ()

注意

  • CACHE 与 PARENT_SCOPE 不能一起使用

  • 同一名称(例FOO)的一般变量和缓存变量可以同时存在,但在调用该变量时(${FOO})会在先取一般变量的值,一般变量中没有再取缓存变量的值。

    1
    2
    3
    4
    set(FOO “x”)        //设置一般变量FOO,不会触及cache,但是会隐藏cache中的FOO。

    set(FOO “x” CACHE ...)  //忽视相同名称的一般变量,在cache中检查FOO是否存在,
                    //如果不存在则存入cache中,存在也不会对cache中FOO重新赋值。
  • 当改变cache中的变量时,同名的一般变量会被删除。一般不建议使用相同名称的一般变量和缓存变量。

  • 然而在有些工程中可以很好的借助这一交互例如:

    • 一个工程利用ADD_SUBDIRECTOTY()添加子工程,子工程有它自己的CMakeList.txt。如果在父工程和子工程中都对同一缓存变量赋值,cmake时父工程率先将变量存入cache中,子工程直接在cache中调用该值,保证了父子工程的一致性。当父工程需要改变该变量,而子程序需要利用原值时,可以直接在父工程中设置同名称的一般变量即可。

Option 的用法

  • 使用场景 : 编译脚本传递参数 -> CMake脚本接收option -> 源代码宏

编译脚本传入参数

1
2
3
#!/bin/sh
cmake -DTEST_DEBUG=ON .
cmake --build .

CMake脚本接收option

  • cmake 脚本定义TEST_DEBUG 默认关闭OFF
    1
    2
    3
    4
    5
    6
    7
    8
    project(test)

    option(TEST_DEBUG "option for debug" OFF)\

    if (TEST_DEBUG)
    add_definitions(-DTEST_DEBUG)
    endif ()
    ...

源代码宏 test.c

1
2
3
4
5
#include "test.h"

#ifdef TEST_DEBUG
...
#endif

add_subdirectory 的用法

  • 一般情况下,我们的项目各个子项目都在一个总的项目根目录下,但有的时候,我们需要使用外部的文件夹,怎么办呢?

  • add_subdirectory命令,可以将指定的文件夹加到build任务列表中。


add_library

  • 使用指定的源文件向工程中添加一个库

    1
    2
    3
    add_library(<name> [STATIC | SHARED | MODULE]
    [EXCLUDE_FROM_ALL]
    source1 source2 ... sourceN)
  • 其中表示库文件的名字,该库文件会根据命令里列出的源文件来创建。而 STATIC、SHARED 和 MODULE 的作用是指定生成的库文件的类型。

    1. STATIC库是目标文件的归档文件,在链接其它目标的时候使用。
    2. SHARED库会被动态链接(动态链接库),在运行时会被加载。
    3. MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数。
  • 默认状态下,库文件将会在于源文件目录树的构建目录树的位置被创建,该命令也会在这里被调用。

  • 而语法中的 source1 source2 分别表示各个源文件。

  • 使用下述格式,add_library命令也可以用来创建导入的库目标:
    1
    add_library(${PROJECT_NAME} SHARED src/track/Tracking.cpp)

link_directories

  • 该指令的作用主要是指定要链接的库文件的路径,该指令有时候不一定需要。因为find_package和find_library指令可以得到库文件的绝对路径。
  • 不过你自己写的动态库文件放在自己新建的目录下时,可以用该指令指定该目录的路径以便工程能够找到。
  • 例子如下:
    1
    2
    3
    link_directiories(
    lib
    )

target_link_libraries

  • 该指令的作用为将目标文件与库文件进行链接。该指令的语法如下:
    1
    2
    target_link_libraries(<target> [item1] [item2] [...]
    [[debug|optimized|general] <item>] ...)
  • 上述指令中的是指通过 add_executable() 和 add_library() 指令生成已经创建的目标文件。而 [item] 表示库文件没有后缀的名字。
  • 默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。
  • 这个传递的接口存储在interface_link_libraries的目标属性中,可以通过设置该属性直接重写传递接口
  • 例子如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    link_directiories(
    ${svo_SOURCE_DIR}/include/svo
    ${svo_SOURCE_DIR}/include/svo/track
    )

    add_library(${PROJECT_NAME} SHARED
    src/track/Tracking.cpp
    )

    add_executable(${PROJECT_NAME}_node src/svo_node.cpp src/system.cpp)

    target_link_libraries(${PROJECT_NAME}_node
    ${PROJECT_NAME}
    )

include_directories

  • 设置头文件位置

一些关键字

  • UNIX : 在所有的类UNIX平台为TRUE,包括OS X和cygwin
  • WIN32 : 在所有的win32平台为TRUE,包括cygwin