工程实践:CMake 编译类型的配置

2 阅读5分钟

以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」mp.weixin.qq.com/s/CsvCzH6r8…

程序在正常情况下的运转,我们都希望它跑得又稳又快,资源又不占用太多,文件大小还不占空间,在开发者看来,这属于发布版本,编译输出时应该是 Release 模式。

相反,在软件开发过程中,大多数时候编译类型都是默认调试(Debug)模式,因为 Debug 模式可以输出大量方便调试的信息,在 linux 系统下甚至允许产生 core-dump 文件,帮助还原崩溃现场的堆栈信息,但对运行速度却没有过多要求。

那么为什么编译类型不同会对执行程序的使用产生不一样的能力呢?

其实说白了,编译类型对执行程序的影响是通过编译器在编译时对软件功能的开关作用和代码结构的转化等。不同编译参数和选项,开关不同的功能,比如调试信息的输出,以及对应不同的代码优化策略:

冗余的删除,比如死代码删除,常量或者复写传播,公共子表达式删除等等。

计算强度削弱,包含但不仅限于一些基于逻辑或者算术表达式的优化,例如把乘法转化成一系列的位移和加减法的组合,常量计算过程转换成结果等。

扩大优化器的作用范围的优化,例如内联,基本块合并等等,基本目的在于获得更大的优化目标

体系相关的优化,简单如指令选择时的一些窥孔性质的指令合并,复杂如寄存器分配,指令调度等np问题的解决。

以 C/C++ 流行的构建系统 cmake 为例,cmake 会依据预定义的编译类型给编译器输入指定的参数和标志位,也即是说编译类型对于 cmake 来说就是枚举类型,但是每个类型分别对应着不同的编译参数和标志。既然如此,编译类型其实也可以自定义,因为最终关心的是给编译器输入什么具体参数。

那么除了 debug 模式,cmake 提供了哪几种预定义的可配置编译类型呢?

CMAKE_BUILD_TYPE说明
Debug调试版本,没有优化,开启断言,最全调试信息
Release正式版本,最高优化,没有调试信息,关闭断言
MinSizeRel体积较小的版本,但速度没有优化
RelWithDebInfo优化,保留了调试信息,关闭断言

上面的这些预定义的编译类型又是如何控制传递给编译器的参数和标志位呢?

Cmake 提供了两组很直观的参数变量:

CMAKE_<LANG>_FLAGS
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
...
CMAKE_<LANG>_FLAGS_<CONFIG>
CMAKE_C_FLAGS_DEBUG
CMAKE_CXX_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_RELWITHDEBINFO
...

CMAKE_<LANG>_FLAGS 变量组存储的是针对特定语言的编译参数和选项。编译代码的时候,编译器输入的参数会包含这组参数变量中对应变量的内容。

比如,编译 C 语言代码,会传输 CMAKE_C_FLAGS 的内容给到编译器;编译 C++ 语言代码,会传输 CMAKE_CXX_FLAGS 的内容给到编译器。

通常,CMAKE_<LANG>_FLAGS_<CONFIG> 变量组会包含特定语言特定模式的对应编译参数和选项。编译代码的时候,编译器输入的参数会按照编译类型来决定包含这组参数变量中对应变量的内容。

比如,编译 C 语言代码,如果选择 Debug 模式,会输入 CMAKE_C_FLAGS_DEBUG 的内容给到编译器;如果选择 Release 模式,会输入 CMAKE_C_FLAGS_RELEASE 的内容给到编译器,以此类推。

配置方法

1. 配置 CMAKE_<LANG>_FLAGS

如果是直接配置 CMAKE_<LANG>_FLAGS 的值,则可以忽略 CMAKE_BUILD_TYPE 的值。<LANG> 代指各种编程语言,比如 C、CXX

SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -lm -lrt -Os -g -rdynamic -funwind-tables -ffunction-sections")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -lm -lrt -Os -g -rdynamic -funwind-tables -ffunction-sections")
SET(LIBRARY_OUTPUT_PATH "./../../src/libs")

但通常写法是利用 CMAKE_BUILD_TYPE 的值作为条件变量,用于判断选择配置 CMAKE_<LANG>_FLAGS 的值

IF (CMAKE_BUILD_TYPE STREQUAL Debug)
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -lm -lrt -Os -g -rdynamic -funwind-tables -ffunction-sections -Wall -Werror")
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -lm -lrt -Os -g -rdynamic -funwind-tables -ffunction-sections -Wall -Werror")
ELSEIF (CMAKE_BUILD_TYPE STREQUAL Release)
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -lm -lrt -Os -s -ffunction-sections -Wall")
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -lm -lrt -Os -s -ffunction-sections -Wall -Werror")
ELSE ()
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -lm -lrt -Os -s")
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -lm -lrt -Os -s")
ENDIF()

2. 配置 CMAKE_<LANG>_FLAGS_<CONFIG>

配置所有 CMAKE_<LANG>_FLAGS_<CONFIG> 的值,CMake 自动根据 CMAKE_BUILD_TYPE 的值选择将哪个 CMAKE_<LANG>_FLAGS_<CONFIG> 值传递给编译器

# SET(CMAKE_BUILD_TYPE Debug)
SET(CMAKE_BUILD_TYPE Release)

SET(FLAGS_C "-pthread -lm -lrt")
SET(FLAGS_CXX "-pthread -lm -lrt")

SET(FLAGS_DEBUG "-g -rdynamic -funwind-tables -ffunction-sections -Wall -ggdb")
#SET(FLAGS_RELEASE "-O3 -s -DNDEBUG -DLINUX -fshort-enums -lmsmart")
SET(FLAGS_RELEASE "-pthread -lm -lrt -Os -s -fPIC -O2 -g -DLINUX -Wall -Werror")
SET(FLAGS_MINSIZEREL "-Os -s -DNDEBUG")

SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} ${FLAGS_C} ${FLAGS_DEBUG}")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS} ${FLAGS_CXX} ${FLAGS_DEBUG}")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} ${FLAGS_C} ${FLAGS_RELEASE}")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS} ${FLAGS_CXX} ${FLAGS_RELEASE}")
SET(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS} ${FLAGS_C} ${FLAGS_MINSIZEREL}")
SET(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS} ${FLAGS_CXX} ${FLAGS_MINSIZEREL}")

从名字来看,cmake 的自带变量都是以 CMAKE 开头。FLAGS_C、FLAGS_CXX、FLAGS_DEBUG、FLAGS_RELEASE、FLAGS_MINSIZEREL 都是自定义变量,存储了某些参数选项,方便重复使用。

CMAKE_BUILD_TYPECMAKE_<LANG>_FLAGS_<CONFIG>
DebugCMAKE_C_FLAGS_DEBUG
CMAKE_CXX_FLAGS_DEBUG
...
ReleaseCMAKE_C_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELEASE
...
MinSizeRelCMAKE_C_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_MINSIZEREL
...
RelWithDebInfoCMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_RELWITHDEBINFO
...