什么是CMAKE_BUILD_TYPE:Debug、Release、RelWithDebInfo和MinSizeRel?

wqsoz72f  于 2023-04-12  发布在  其他
关注(0)|答案(4)|浏览(596)

关于docs page

CMAKE_BUILD_TYPE

指定单配置生成器上的生成类型。
这静态地指定了什么构建类型(配置)将在此生成树中生成。可能的值为空,DebugReleaseRelWithDebInfoMinSizeRel。此变量仅对单配置生成器有意义(例如Makefile GeneratorsNinja),即当CMake运行时选择单个配置以生成构建树而不是多个configuration generators,在生成的构建环境中提供构建配置的选择。有许多per-config属性和变量(通常遵循干净的SOME_VAR_<CONFIG>顺序约定),例如CMAKE_C_FLAGS_<CONFIG>,指定为大写:CMAKE_C_FLAGS_[DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL]。例如,在配置为构建类型Debug的构建树中,CMake将看到将CMAKE_C_FLAGS_DEBUG设置添加到CMAKE_C_FLAGS设置中。另请参见CMAKE_CONFIGURATION_TYPES
我知道Debug构建和Release构建之间的区别,但是ReleaseRelWithDebInfoMinSizeRel之间的区别是什么?我猜RelWithDebInfo意味着创建可调试的二进制文件,而MinSizeRel意味着创建尽可能小的二进制文件。
从LLVM CMake页面:

CMAKE_BUILD_TYPE:String

如果您使用的是Visual Studio等IDE,则应使用IDE设置来设置生成类型。请注意,Release和RelWithDebInfo在大多数平台上使用不同的优化级别。
如果我想生成一个生产版本,我应该选择Release吗?

4nkexdtk

4nkexdtk1#

RelWithDebInfoRelease相同,允许您拥有symbol files进行调试。

例如,在Visual Studio中,您将有.pdb文件,如果没有它们,将很难调试,因为二进制文件中的所有签名都不是“人类可读”的,而且无法将它们Map到源代码。

MinSizeRelRelease相同,只是优化配置为Minimize Size,而不是像Visual Studio中的this link那样设置为Maximize Speed

如果我想生成一个生产版本,我应该选择发布吗?
是的,这应该为你做正确的工作。调试/发布是最常用的选项。
阅读this CMAKE FAQ实际上会对你有很大的帮助。

2nbm6dog

2nbm6dog2#

是的,你是对的:
我猜RelWithDebInfo意味着创建可调试的二进制文件,而MinSizeRel意味着创建尽可能小的二进制文件。
RelWithDebInfo将添加用于生成调试信息的编译器标志(GCC / clang的-g标志),并将产生可调试的,但更大的二进制文件。
MinSizeRel将添加编译器标志以生成更紧凑的二进制文件(GCC / clang的-Os标志),这可能会牺牲程序速度。
如果我想生成一个生产版本,我应该选择Release吗?
是的,Release是一个不错的选择。它应该产生更快的二进制文件,通过指定有利于速度的编译器优化级别(-O3用于GCC / clang),并且不包括调试符号。

kqqjbcuj

kqqjbcuj3#

您还可以通过以下方式获得构建类型的概述:

cmake -LAH .. | grep -C1 CMAKE_CXX_FLAGS

如在以下文献中所述:How to list all CMake build options and their default values?列出所有内置变量并给出:

// Flags used by the CXX compiler during all build types.
CMAKE_CXX_FLAGS:STRING=

// Flags used by the CXX compiler during DEBUG builds.
CMAKE_CXX_FLAGS_DEBUG:STRING=-g

// Flags used by the CXX compiler during MINSIZEREL builds.
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG

// Flags used by the CXX compiler during RELEASE builds.
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG

// Flags used by the CXX compiler during RELWITHDEBINFO builds.
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG

对于那些熟悉相应GCC选项的人来说,每个构建类型的含义应该很清楚。
默认情况下,如果没有传递-DMAKE_BUILD_TYPE,则CMAKE_BUILD_TYPE为空,并且不会添加任何额外的CMAKE_CXX_FLAGS_XXX值。
在Ubuntu 22.10,CMake 3.24.2上测试。

mrfwxfqh

mrfwxfqh4#

注意事项CMAKE_BUILD_TYPE只适用于单目标生成器,如Makefile。它不适用于多目标生成器,因为它们只是生成一个能够构建所有构建类型(调试,发布等)的构建系统。

CMAKE_BUILD_TYPE大约是,
1.优化(级别)[-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4]
1.在可执行文件[-g, -gline-tables-only, -gmodules, -g * 级别 * , -gcoff, -gdwarf, -gdwarf- * 版本 * , -ggdb, -grecord-gcc-switches, -gno-record-gcc-switches, -gstabs, -gstabs+, -gstrict-dwarf, -gno-strict-dwarf, -gcolumn-info, -gno-column-info, -gvms, -gxcoff, -gxcoff+, -gz[= * 类型 * ]]中包含“调试信息”
1.是否为assert()生成代码[-DNDEBUG]
1.是否包含调试(输出)代码[自定义]
大多数这样的编译器选项是编译器和/或平台特定的。因此,对构建类型的扩展支持需要更新您想要支持的每个现有工具链。
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的平台上添加定义NDEBUG(它禁用assert())。这就是为什么你应该确保你的Assert没有副作用。

扩展构建类型

尽管为不同的工具链添加需要不同选项的东西通常不是你真正想做的事情(尽管,编译器选项基本上是编译器/语言特定的,所以如果你愿意,你可以很容易地检查compiler ID,然后根据它选择你的标志)。
当你把自己限制在[-g, -O0, -O2, -O3-Os],删除一个可能的-DNDEBUG标志和/或添加自定义宏时,以改变优化标志或调试标志的形式添加支持是相当容易的。
假设我们有一个宏DEBUG,我们希望定义它来包含特定的调试代码(例如,这可能包括编写调试输出)。
然后我们有四个优化级别,调试信息或没有,Assert代码或没有,调试代码或没有,总共有4 * 2 * 2 * 2 = 32个配置(构建类型)。但显然不是所有配置都是非常实用的。最好看看配置的用例是什么。
很明显,我们有Release构建,它是无bug的代码,并被广泛发布;它是生产代码。你不会经常编译它,当你这样做的时候,更重要的是生成的代码是快的(或小的?),而不是编译它需要多长时间。这导致了生产代码的两种现有构建类型:

1. Release
2. MinSizeRel

但是后来发现在产品代码中有一个bug导致应用程序崩溃。你不能重现它,它只是偶尔发生。你实现了一个反馈机制,让你的用户向你发送核心转储,但是信息是不够的。你想得到堆栈跟踪,希望它能告诉你更多。你问某些用户(或者你自己,作为“用户”每天使用它)下载一个特殊的版本,可以正常使用(它足够快,优化),但它包含调试信息,所以下载时间要长得多。这些用户不介意:他们希望修复此崩溃。

3. RelWithDebInfo

当然,作为开发人员,你需要一个可以通过调试器逐步调试的版本。它不需要很快-你已经知道如何重现一个不依赖于优化的错误(它是一个逻辑错误,代码中的问题-而不是Heisenbug)。为此,你可以使用,

4. Debug

但是--你也有beta测试者(也许你自己每天都以“用户”的身份使用程序)。在这种情况下,你希望代码被优化,所以它足够快--但是你也希望所有Assert都被打开;一个Assert可能会告诉你哪里有问题,而不是稍后发生的核心转储。或者更糟的是,它可能只是表现得很奇怪,根本不会崩溃。你需要确保没有任何Assert被触发,即使是在生产代码中。这就是beta测试人员的作用。让我们称之为构建类型,

5. BetaTest [`-O3 -g`] - aka Release minus the `-DNDEBUG` but with `-g`.

最后,有些调试构建是 * 不 * 通过调试器逐步完成的;有些错误是无法重现的,堆栈跟踪也没有帮助(因为它没有核心转储,或者问题没有导致立即崩溃)。找到这些虫子的唯一方法(一旦发生)是带有 * 额外的 * 调试代码和/或写入日志文件的调试输出负载。您希望此代码至少使用-O2编译,但您也希望使用Assert(为什么不呢),并且需要定义宏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(看,betatestrelwithdebug的大小写不敏感,加上作为这两个值的结果设置的变量)。

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

相关问题