【问题标题】:What are CMAKE_BUILD_TYPE: Debug, Release, RelWithDebInfo and MinSizeRel?什么是 CMAKE_BUILD_TYPE:Debug、Release、RelWithDebInfo 和 MinSizeRel?
【发布时间】:2018-07-23 02:29:25
【问题描述】:

来自docs page

CMAKE_BUILD_TYPE

指定单一配置生成器的构建类型。

这静态指定将在此构建树中构建的构建类型(配置)。可能的值为空,DebugReleaseRelWithDebInfoMinSizeRel。此变量仅对单配置生成器有意义(例如Makefile GeneratorsNinja),即在 CMake 运行时选择单个配置以生成构建树的生成器,而不是提供构建配置选择的多配置生成器在生成的构建环境中。有许多 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 page

CMAKE_BUILD_TYPE:STRING

如果您使用的是 Visual Studio 等 IDE,则应使用 IDE 设置来设置构建类型。请注意,Release 和 RelWithDebInfo 在大多数平台上使用不同的优化级别。

如果我想生成一个生产版本,我应该选择Release吗?

【问题讨论】:

  • 如果我想生成生产版本,我应该选择发布吗? 是的。我总是这样做。
  • 我猜 RelWithDebInfo 意味着创建可调试的二进制文件,而 MinSizeRel 意味着创建尽可能小的二进制文件。 没错。 RelWithDebInfo 是 Visual Studio 上带有调试符号的发布二进制文件。

标签: cmake


【解决方案1】:

重要提示CMAKE_BUILD_TYPE 仅适用于单目标生成器,例如 Makefile。它不用于多目标生成器,因为它们只是生成能够构建所有构建类型(调试、发布等)的构建系统。

CMAKE_BUILD_TYPE是关于,

  1. 优化(级别)[-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4]
  2. 在可执行文件中包含“调试信息”[-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]]
  3. 是否为assert() 生成代码[-DNDEBUG]
  4. 是否包含调试(输出)代码[自定义]

大多数此类编译器选项都是特定于编译器和/或平台的。因此,对构建类型的扩展支持需要更新您想要支持的每个现有工具链。

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(看,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" )

【讨论】:

  • 谢谢楼主,分享的很详细!
  • 不客气。在我写这样的答案的那一刻,我只是做了很多研究,因为我自己需要它,我只是在完成研究后花额外的时间将我的知识添加到匹配的问题中。那时,我对这一切似乎都一清二楚,因为我深入研究了材料。但是当我一年后读回我的答案时,我常常觉得它根本不那么清楚! :(。所以,很高兴听到这对你来说是一个很好的信息来源。
  • 如果每个人都像你一样分享,那么我们将有更多的未知之光!
【解决方案2】:

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

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

MinSizeRelRelease 相同,只是将优化配置设置为 Minimize Size 而不是 Maximize Speed,如 Visual Studio 中的 this link 所示,用于例子。

如果我想生成一个生产版本,我应该选择发布吗?

是的,那应该为您做正确的工作。 Debug/Release 是最常用的选项。

阅读this CMAKE FAQ实际上会对你有很大帮助。

【讨论】:

  • 请注意,在大多数编译器上,RelWithDebInfo 生成的代码的效率不应低于Release 生成的代码,但您仍然会得到调试符号。如果您计划向用户发布二进制文件,这是一个巨大的胜利,您需要稍后进行调试。因此,我认为RelWithDebInfo 是普遍比Release 更好的选择,除非你有很好的理由不使用它。
  • 默认优化级别发生变化。对于 gcc 和 clang,至少,RelWithDebInfo 使用 -O2,但 Release 使用 -O3
  • 值得注意的是,无论您选择使用哪个CMAKE_BUILD_TYPE,您都可以拥有自己的优化级别。
  • 这是否意味着 MinSizeRel 的性能可能较慢?我对您提到的“最大化速度”配置感到困惑。
  • @KhoiV 我承认存在一些误导性信息,因此我对其进行了一些编辑。很久以前我写了这个答案,但如果我没记错的话,我提到 Visual Studio 的原因可能是 OP 有一些 C# 问题,所以我认为 OP 希望它是面向 Windows 的(也在问题中提到)。它具有 Maximize Speed 的原因可能是在复制和粘贴链接时犯了一个愚蠢的错误,并且它的标题巧合地是 "(Minimize Size, Maximize Speed)"。感谢您的提醒。
【解决方案3】:

是的,你是对的:

我猜RelWithDebInfo 的意思是创建可调试的二进制文件,MinSizeRel 的意思是创建尽可能小的二进制文件。

RelWithDebInfo 将添加编译器标志以生成调试信息(GCC / clang 的-g 标志),并将生成可调试但更大的二进制文件。

MinSizeRel 将添加编译器标志以生成更紧凑的二进制文件(GCC / clang 的 -Os 标志),可能会牺牲程序速度。

如果我想生成一个生产版本,我应该选择Release吗?

是的,Release 将是一个不错的选择。它应该生成更快的二进制文件,通过指定编译器优化级别以提高速度(-O3 用于 GCC / clang),并且不包括调试符号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 2018-07-20
    • 1970-01-01
    • 2016-02-06
    • 2012-04-03
    • 2023-03-19
    • 1970-01-01
    相关资源
    最近更新 更多