CMake使用学习

1. 概述

CMake 工具能够自动生成 Makefile 文件,减轻手写 Makefile 文件的工作量,同时减少书写 Makefile 文件产生的错误。

2. CMake 命令

CMake 运行主要分为两个阶段:

  • 配置阶段:解析 CMakeLists.txt 文件

  • 生成阶段:生成构建环境

有关 cmake 的详细参数参见 cmake --help,本文仅对其中较难理解的选项加以描述。

2.1 缓存选项

CMake 支持缓存选项。在 CMake 中,如果一个变量被标记为「缓存」,则 cmake 的时候会将其写入到 CMakeCache.txt 文件中。当 cmake 命令寻找变量时,它会首先去 CMakeCache.txt 文件中寻找。cmake 创建缓存选项的格式如下:

cmake -D <var>[:<type>]=<value>     # <var>[:<type>]=<value> 具体参见下文「CMakeCache.txt 编写」

2.2 常用选项

  • -DCMAKE_BUILD_TYPE= :指定编译软件的版本格式,取值为 ReleaseRelWithDebInfoDebug 等。

  • -DCMAKE_INSTALL_PREFIX= :指定需要安装的软件路径,默认为安装路径为 /usr/local(默认系统安装)。

仅用户安装一般指定安装路径为 ~/.local,当然也可以随用户自定义。

  • -DBUILD_SHARED_LIBS=DBUILD_SHARED_LIBS 是一个全局的 flag,为 bool 类型,取值为 ONOFF(默认为 ON)。其作用是:

如果 DBUILD_SHARED_LIBS 设定为 ON,则 CMakeLists.txt 中所有的 add_library() 创建的库都默认为共享库而不是静态库,除非 add_library() 中有显式地指定编译为静态库。反之则为静态库。

  • -DBUILD_TESTING=DBUILD_TESTING 是一个全局的 flag,为 bool 类型,取值为 ONOFF(默认为 ON)。其作用是:

当使用 CTest 模块时,DBUILD_TESTING 用来控制是否使能 testing。

  • --trace :用于详细输出 cmake 的每一行信息。

--trace-source=<file> :将 cmake 输出的所有信息都保存在 file 文件中。

3. CMakeLists.txt 编写

3.1 设定编译器

在运行 CMake 前首先需要指定 CC、CXX 编译器,否则 CMake 将使用系统默认的 CC、CXX 编译器。可以在 bash shell 中临时设定:

CC=/usr/bin/gcc
CXX=/usr/bin/g++

也可以在 CMakeLists.txt 文件中设定:

set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")

3.2 添加 CMake 最小要求版本

cmake_minimum_required(VERSION 3.1)

3.3 添加项目信息

project(MyProject   VERSION 1.0         # 项目版本
                    DESCRIPTION "xxx"   # 项目描述
                    LANGUAGES CXX)      # 项目语言

CMake 支持的语言有:C , CXX , Fortran , ASM , CUDA (CMake 3.8+), CSharp (3.8+), and SWIFT (CMake 3.15+experimental)。默认为 CCXX

3.4 生成文件

  • 生成目标文件
add_executable(one two.cpp three.h)

one 既是生成的可执行文件名称,也是 CMake 创建的目标文件的名称。one 后面紧跟着项目的源文件,比如这里示例的 two.cpp three.h 都是源文件。

  • 生成库文件
add_library(onelib STATIC twolib.cpp threelib.h)

可选的库文件类型有:STATIC、SHARED、MODULE

3.5 为目标文件添加内容

【注】可以为目标文件添加这些内容:

include directories, linked libraries (or linked targets), compile options, compile definitions, compile features, and more.

  • 为目标文件添加 include 路径
target_include_directories(one PUBLIC include)

PUBLIC/PRIVATE/INTERFACE 用来指定「是/否/仅需要时」影响其他依赖于当前目标文件 one 的目标文件。

  • 为目标文件添加链接库
target_link_libraries(one -lcrypt -lcap)

one 是 CMake 创建的目标文件的名称。

3.6 设定变量、缓存项、属性

【注】访问一个名为 VARIABLE 的局部变量使用 ${VARIABLE},访问一个名为 VARIABLE 的环境变量使用 $ENV{VARIABLE}

  • 设定局部变量
set(MY_VARIABLE "value")

局部变量的值还可以是列表:

set(MY_VARIABLE "one" "two")
set(MY_VARIABLE "one;two")

上述两条语句等价,因为变量的列表值在内部存储时就是使用 ";" 分隔的。可以添加 PARENT_SCOPE 关键字指定将变量的作用域往外跳一级。

  • 设定缓存变量
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description")       # 不会覆盖已存在的变量
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description" FORCE) # 会覆盖已存在的变量
  • 修改命令行参数变量
option(MY_OPTION "This is settable from the command line" OFF)  # 此处假定 MY_OPTION 为 bool 值
  • 设定环境变量
set(ENV{variable_name} value)
  • 设定属性 & 访问属性
    属性可以看作是依附于某一项(比如目录、目标文件等)的全局变量。
set_property(TARGET TargetName[TargetName1...] PROPERTY CXX_STANDARD 11)                # 可以为多个 targets/files/tests 设置属性
set_target_properties(TargetName[TargetName1...] PROPERTIES CXX_STANDARD 11)            # 仅用于为 targets 设置属性
get_property(ResultVariable TARGET TargetName[TargetName1...] PROPERTY CXX_STANDARD)    # 获取 targets 的属性值并保存在 ResultVariable 中

3.7 if 语句块

## 第一种格式
if(variable)
    # If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number
else()
    # If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND`
endif()
# If variable does not expand to one of the above, CMake will expand it then try again

## 第二种格式
if("${variable}")
    # True if variable is not false-like
else()
    # Note that undefined variables would be `""` thus false
endif()

if 语句块中可以包含的关键字有:

  • 一元:NOT、TARGET、EXISTS(file)、DEFINED 等

  • 二元:STREQUAL、AND、OR、MATCHES(regular expression)、VERSION_LESS、VERSION_LESS_EQUAL 等

  • 括号:()

3.8 生成表达式

  • $<KEYWORD> :计算 KEYWORD 的值

  • $<KEYWORD:value> :根据 KEYWORD 的值来控制整个表达式的值,KEYWORD = 1 时表达式值为 value,KEYWORD = 0 时表达式值为空字符串。

常用的生成表达式有 BUILD_INTERFACEINSTALL_INTERFACE 生成表达式。

target_include_directories(
    MyTarget
    PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

BUILD_INTERFACE 表达式包装的构建需求只被在同一个构建系统下,或者使用 export() 指令导出的目标上使用。INSTALL_INTERFACE 表达式包装的构建需求只被用在使用 install(EXPORT) 指令安装和导出的目标上:

3.9 函数和宏

函数和宏的唯一区别在于作用域,函数有作用域但宏没有。

## 函数举例
function(SIMPLE REQUIRED_ARG)
    message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
    set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()
## 宏举例
macro(SIMPLE REQUIRED_ARG)
    message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
    set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endmacro()

CMake 中有一个参数接收模块:cmake_parse_arguments(),用来定义函数/宏需要接收的参数。

## 定义函数
function(COMPLEX)
    cmake_parse_arguments(
        COMPLEX_PREFIX
        "SINGLE;ANOTHER"
        "ONE_VALUE;ALSO_ONE_VALUE"
        "MULTI_VALUES"
        ${ARGN}
    )
    ……
endfunction()
## 调用函数
complex(SINGLE ONE_VALUE value MULTI_VALUES some other values)

上文调用函数后可以发现:

COMPLEX_PREFIX_SINGLE = TRUE
COMPLEX_PREFIX_ANOTHER = FALSE
COMPLEX_PREFIX_ONE_VALUE = "value"
COMPLEX_PREFIX_ALSO_ONE_VALUE = <UNDEFINED>
COMPLEX_PREFIX_MULTI_VALUES = "some;other;values"

如果函数/宏调用时传入了 cmake_parse_arguments() 未指定的其他参数,则其内容均保存在 COMPLEX_PREFIX_UNPARSED_ARGUMENTS 变量中。

4. CMakeCache.txt 编写

CMakeCache.txt 文件本是由 cmake 命令默认生成的,但其内容是可以修改的。CMakeCache.txt 文件主要保存的是 cmake 过程中需要使用的环境变量,当 cmake 命令寻找变量时,它会首先去 CMakeCache.txt 文件中寻找。

4.1 内容条目

随便看一个 CMakeCache.txt 文件就会发现,其文首给出了该文件的作用和内容条目格式:

# This is the CMakeCache file.
# For build in directory: xxx/build
# It was generated by CMake: yyy/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.

简单来说,CMakeCache.txt 文件就是一条条环境变量的键(包含类型)值对条目组成的,而条目的具体格式如下:

KEY:TYPE=VALUE

5. 注释

在可编写的 CMake 文件(CMakeLists、CMakeCache.txt等)中,其注释规则都是一样的。

5.1 单行注释

# 要注释的单行内容

5.2 多行注释

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