重要提示:CMAKE_BUILD_TYPE 仅适用于单目标生成器,例如 Makefile。它不用于多目标生成器,因为它们只是生成能够构建所有构建类型(调试、发布等)的构建系统。
CMAKE_BUILD_TYPE是关于,
- 优化(级别)[
-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4]
- 在可执行文件中包含“调试信息”[
-g, -gline-tables-only, -gmodules, -glevel, -gcoff, -gdwarf, -gdwarf-version, -ggdb, -grecord-gcc-switches, -gno-record-gcc-switches, -gstabs, -gstabs+, -gstrict-dwarf, -gno-strict-dwarf, -gcolumn-info, -gno-column-info, -gvms, -gxcoff, -gxcoff+, -gz[=type]]
- 是否为
assert() 生成代码[-DNDEBUG]
- 是否包含调试(输出)代码[自定义]
大多数此类编译器选项都是特定于编译器和/或平台的。因此,对构建类型的扩展支持需要更新您想要支持的每个现有工具链。
cmake 自带的默认构建类型或多或少的意思如下,
1. Release: high optimization level, no debug info, code or asserts.
2. Debug: No optimization, asserts enabled, [custom debug (output) code enabled],
debug info included in executable (so you can step through the code with a
debugger and have address to source-file:line-number translation).
3. RelWithDebInfo: optimized, *with* debug info, but no debug (output) code or asserts.
4. MinSizeRel: same as Release but optimizing for size rather than speed.
就编译器标志而言,这通常意味着(因为在大多数情况下,所有平台都支持这些标志):
1. Release: `-O3 -DNDEBUG`
2. Debug: `-O0 -g`
3. RelWithDebInfo: `-O2 -g -DNDEBUG`
4. MinSizeRel: `-Os -DNDEBUG`
在支持此功能的平台上添加了定义NDEBUG(它禁用了assert())。这就是为什么你应该确保你的断言没有副作用,当然。
扩展构建类型
虽然为不同的工具链添加需要不同选项的东西并不是你真正想做的事情(尽管编译器选项基本上是编译器/语言特定的,所以如果你愿意,你可以很容易地检查compiler ID然后根据它选择你的标志)。
当您将自己限制为 [-g, -O0, -O2, -O3and-Os] 时,以更改优化标志或调试标志的形式添加支持相当容易,删除可能的 -DNDEBUG 标志和/或添加自定义宏。
假设我们想要定义一个宏 DEBUG 以包含特定的调试代码(例如,可能包括编写调试输出)。
然后我们有四个优化级别,是否调试信息、是否断言代码和是否调试代码,总共有 4 * 2 * 2 * 2 = 32 种配置(构建类型)。但显然并非所有配置都非常实用。 最好看看配置的用例是什么。
很明显,我们有Release 构建,它是大规模发布的无错误代码;它是生产代码。你不会经常编译它,当你这样做时,生成的代码是否快速(或小?)比编译它需要多长时间更重要。这导致了生产代码的两种现有构建类型:
1. Release
2. MinSizeRel
但事实证明,在生产代码中毕竟存在导致应用程序崩溃的错误。你无法重现它,它只是有时会发生。您实施了一种反馈机制,让您的用户向您发送核心转储,但信息还不够。您想获取堆栈跟踪,希望它能告诉您更多信息。您要求某些用户(或者您自己,作为“用户”每天使用它)下载一个可以正常使用的特殊版本(它足够快,经过优化),但它包含调试信息,因此需要更长的时间去下载。这些用户并不介意:他们希望修复此崩溃。支持
3. RelWithDebInfo
当然,作为开发人员,您需要一个可以通过调试器逐步完成的版本。它不一定要很快——你已经知道如何重现一个不依赖于优化的错误(这是一个逻辑错误,你的代码中的一个问题——不是 Heisenbug)。为此,您使用,
4. Debug
但是 - 您也有 Beta 测试人员(也许您自己每天都以“用户”的身份使用该程序)。在这种情况下,您希望优化代码,使其足够快 - 但您还希望打开所有断言;断言可能会告诉您问题在哪里比稍后发生的核心转储好得多。或者更糟的是,它可能只是表现得很奇怪,根本不会崩溃。您需要确保您的任何断言都不会触发,即使在生产代码中也是如此。这就是 Beta 测试人员的用途。让我们称之为构建类型,
5. BetaTest [`-O3 -g`] - aka Release minus the `-DNDEBUG` but with `-g`.
最后,不是可以使用调试器单步执行的调试版本;一些错误根本无法重现,堆栈跟踪也没有帮助(因为它没有核心转储,或者问题不会导致立即崩溃)。有很多,如果不是大多数,这样的错误。找到这些错误(一旦发生)的唯一方法是使用 extra 调试代码和/或写入日志文件的大量调试输出。您希望至少使用-O2 编译此代码,但您也希望断言(为什么不)并且您需要定义宏DEBUG。当然,我们也可以包含调试信息,因为可执行文件的大小在这里不太重要。让我们称之为这样的构建
6. RelWithDebug [`-O2 -g -DDEBUG`] - aka RelWithDebInfo but `-DNDEBUG` removed and `-DDEBUG` added.
我在这里建议-O2,因为这是您作为开发人员最常使用的编译器,因为您自己将永远是这样的用户,因为如果发生意外情况,您想知道是什么原因造成的(有这些日志! ) 并且您不想一直使用慢得多的 -O3 进行编译...
为了支持这两种额外的构建类型,我们需要能够做两个
因此:获取现有构建类型的标志(更改它们)并将这些标志用于新的(自定义)构建类型。
这里是如何做到这一点
如果您将以下四行添加到项目根CMakeLists.txt 文件的顶部,然后使用-DCMAKE_BUILD_TYPE=BetaTest(或RelWithDebug),将使用上述标志。当然,如果您的 .cmake 文件中的其他内容取决于构建类型,您可能需要进行更多更改。以下是我个人使用的示例:CW_OPTIONS.cmake(看,betatest 和 relwithdebug 不区分大小写,加上根据这两个值设置的变量)。
string(REGEX REPLACE "( -DNDEBUG$|-DNDEBUG )" "" CMAKE_CXX_FLAGS_BETATEST "${CMAKE_CXX_FLAGS_RELEASE}" )
string(REGEX REPLACE "( -DNDEBUG$|-DNDEBUG )" "" CMAKE_C_FLAGS_BETATEST "${CMAKE_C_FLAGS_RELEASE}" )
string(REGEX REPLACE "-DNDEBUG " "" CMAKE_CXX_FLAGS_RELWITHDEBUG "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDEBUG" )
string(REGEX REPLACE "-DNDEBUG " "" CMAKE_C_FLAGS_RELWITHDEBUG "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDEBUG" )