82

From the docs page:

CMAKE_BUILD_TYPE

Specifies the build type on single-configuration generators.

This statically specifies what build type (configuration) will be built in this build tree. Possible values are empty, Debug, Release, RelWithDebInfo and MinSizeRel. This variable is only meaningful to single-configuration generators (such as Makefile Generators and Ninja) i.e. those which choose a single configuration when CMake runs to generate a build tree as opposed to multi-configuration generators which offer selection of the build configuration within the generated build environment. There are many per-config properties and variables (usually following clean SOME_VAR_<CONFIG> order conventions), such as CMAKE_C_FLAGS_<CONFIG>, specified as uppercase: CMAKE_C_FLAGS_[DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL]. For example, in a build tree configured to build type Debug, CMake will see to having CMAKE_C_FLAGS_DEBUG settings get added to the CMAKE_C_FLAGS settings. See also CMAKE_CONFIGURATION_TYPES.

I'm aware the differences between Debug builds and Release builds, but what are the differences between Release, RelWithDebInfo and MinSizeRel? I'm guessing RelWithDebInfo meant creating debuggable binaries, and MinSizeRel meant creating smallest possible size binaries.

From the LLVM CMake page:

CMAKE_BUILD_TYPE:STRING

If you are using an IDE such as Visual Studio, you should use the IDE settings to set the build type. Be aware that Release and RelWithDebInfo use different optimization levels on most platforms.

If I want to generate a production build, should I choose Release?

Community
  • 1
  • 1
MiP
  • 5,846
  • 3
  • 26
  • 41
  • ***If I want to generate a production build, should I choose Release?*** Yes. I always do. – drescherjm Feb 12 '18 at 20:36
  • ***I'm guessing RelWithDebInfo meant creating debuggable binaries, and MinSizeRel meant creating smallest possible size binaries.*** That is correct. `RelWithDebInfo` is release binaries with debug symbols on Visual Studio. – drescherjm Feb 12 '18 at 20:37

4 Answers4

126

IMPORTANT: CMAKE_BUILD_TYPE only makes sense for single-target generators, like Makefiles. It is not used for multi-target generators as those simply generate a build system capable of building all build types (debug, release, etc).

CMAKE_BUILD_TYPE is about,

  1. Optimization (level) [-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4]
  2. Including 'debug info' in the executable [-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. Generating code for assert() or not [-DNDEBUG]
  4. Including debug (output) code or not [custom]

Most such compiler options are compiler and/or platform specific. So, extended support for a build type needs updating every existing tool chain that you want to support.

The default build types that come with cmake more or less mean the following,

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.

In terms of compiler flags that usually means (since these are supported in most cases on all platforms anyway):

1. Release: `-O3 -DNDEBUG`
2. Debug: `-O0 -g`
3. RelWithDebInfo: `-O2 -g -DNDEBUG`
4. MinSizeRel: `-Os -DNDEBUG`

Where defining NDEBUG is added on platforms that support this (it disables assert()). This is why you should make sure that none of your asserts have side effects, of course.

Extending the build type

Although adding stuff that needs different options for different tool chains is not something you really want to do in general (although, compiler options are basically compiler/language specific, so you could rather easily check the compiler ID if you wanted and then pick your flags depending on that).

It is rather easy to add support in the form of altering optimization flags or debug flags when you restrict yourself to [-g, -O0, -O2, -O3and-Os], removing a possible -DNDEBUG flag and/or adding custom macros.

Suppose we have a macro DEBUG that we want to define to include specific debug code (which could include writing debug output for example).

Then we have four optimization levels, debug info or not, assert code or not and debug code or not, for a total of 4 * 2 * 2 * 2 = 32 configurations (build types). But clearly not all configurations are very practical. It is better to look at what the use case is for a configuration.

Clearly we have the Release build, which is bug-free code that is released at large; it is production code. You will not compile it very often and when you do it is more important that the resulting code is fast (or small?) than that it matters how long it takes to compile it. That leads to the two existing build types for production code:

1. Release
2. MinSizeRel

But then it turns out there is a bug after all in the production code that makes the application crash. You can't reproduce it and it only happens sometimes. You implemented a feedback mechanism for your users to send you the core dump, but the info just isn't enough. You want to get the stack trace in the hope it will tell you more. You ask certain users (or maybe yourself, using it on a daily basis as 'user') to download a special version that is usable as normal (it is fast enough, optimized) but it has debug information included, so takes a lot longer to download. Those users don't mind that: they want this crash to be fixed. This is supported with

3. RelWithDebInfo

Of course, you as the developer need a version that you can step through with a debugger. It doesn't have to be fast - you already know how to reproduce a bug that doesn't depend on optimization (it is a logic bug, a problem in your code - not a Heisenbug). For this you use,

4. Debug

But -- you also have beta testers (maybe you yourself on a daily basis using the program as a 'user'). In this case you want the code to be optimized, so it is fast enough - but you want also all asserts to be turned on; an assert might tell you where a problem is way better than a core dump that happens later. Or worse, it might just behave strangely and not crash at all. You need to be sure that none of your asserts fire, even in production code. That is what beta testers are for. Lets call this build type,

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

Finally, there debug builds that are not to step through with a debugger; some bugs are simply not reproducable, nor does a stack trace help (because either it doesn't core dump, or the problem isn't causing an immediate crash). There are many, if not most, such bugs. And the only way to find those bugs (once they occur) is with extra debug code and/or loads of debug output written to a log file. You want this code to be compiled with -O2 at least, but you want asserts on too (why not) and you need the macro DEBUG to be defined. We might as well include debug info too, because the size of the executable is of lesser concern here, of course. Lets call such a build

6. RelWithDebug [`-O2 -g -DDEBUG`] - aka RelWithDebInfo but `-DNDEBUG` removed and `-DDEBUG` added.

I suggest -O2 here because this is what you'd compile with most, as developer, because you yourself will always be such a user, because IF something unexpected happens you want to know what caused it (have those logs!) and you don't want to compile with the much slower -O3 all the time...

To support these two extra build types we need to be able to do two things therefore: get the flags of an existing build type (change them) and use those flags for a new (custom) build type.

Here is how to do this

If you add the following four lines somewhere to the top of your project roots CMakeLists.txt file then using -DCMAKE_BUILD_TYPE=BetaTest (or RelWithDebug), will use the flags as outlined above. Of course you might have to make more changes if anything else in your .cmake files depends on the build type. Here is an example of what I am using personally: CW_OPTIONS.cmake (look, case insensive for betatest and relwithdebug plus the variables that are set as a result of what values those two have).

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" )
Carlo Wood
  • 5,648
  • 2
  • 35
  • 47
  • 1
    thank you sir, that's a very fine & details sharing! – ITW Nov 22 '21 at 00:52
  • 6
    You are welcome. At the moment I write an answer like this, I just did a lot of research, because I needed it myself, I just take the extra time to add my knowledge to a matching question once I finished my research. And at that time it all seems crystal clear to me, because I am deep into the material. But when I read back my answer(s) a year later I often have the feeling it isn't that clear at all! :(. So, nice to hear it was a good source of information for you. – Carlo Wood Nov 22 '21 at 23:08
  • if everyone share like you, then we will have more light over the unknown! – ITW Nov 23 '21 at 00:45
  • 2
    This is a great answer, but I am curious where in the cmake documentation it lists these defaults. I've been searching, and for the life of me cannot find them. All the docs mention is that the build options exist, NOT what they do or what flags they set. – Cole Nov 24 '22 at 10:33
  • @cole, it's toolchain specific- if you look at the [CMake source code](https://gitlab.kitware.com/search?group_id=415&project_id=541&repository_ref=master&search=RelWithDebInfo&search_code=true), you can see the specific flags that are set in the files under `Modules/platform` and `Modules/compiler` – irowe Dec 01 '22 at 22:01
  • @irowe Thanks for the info. I eventually figured out the flags can be found in the `CMakeCache.txt` file generated after running cmake. They can also be seen by running the cmake gui (`ccmake` on Linux). I do think it's really poor, however, that there's not any official documentation listing these things. – Cole Dec 05 '22 at 06:23
  • FYI in at least `Cmake 3.16.4` on Linux, debug is just `-g` and does not include `-O0`, the lack of which can cause some serious headaches – Cole Dec 05 '22 at 06:27
  • @Cole, well it is an open-source project so you (or I) could propose an improvement to the docs! – irowe Dec 21 '22 at 15:09
  • I personally don't think that Debug should contain -O0. All that does is cancel a possible -O2 (or -O3 etc) on the left of it; which shouldn't have been there in the first place. – Carlo Wood Dec 30 '22 at 10:45
40

RelWithDebInfo is the same as Release, allowing you to have symbol files for debugging.

For example in Visual Studio, you'll have .pdb files and without them, it'll be hard to debug because all the signatures in the binary files are not going to be human readable and there's no way to map them to the source code.

MinSizeRel is the same as Release, with its optimization configuration just set to Minimize Size instead of Maximize Speed as illustrated in this link in Visual Studio, for example.

If I want to generate a production build, should I choose Release?

Yes, that should do the right job for you. Debug/Release are the most commonly used options.

Reading this CMAKE FAQ will actually help you a lot.

Dean Seo
  • 5,486
  • 3
  • 30
  • 49
  • 7
    Note that on most compilers, the code generated by `RelWithDebInfo` should not be less efficient than what is generated by `Release`, but you still get debug symbols. This is a *huge* win if you plan to release binaries to users, which you will need to debug later. I would therefore argue that `RelWithDebInfo` is universally a better choice than `Release`, unless you have very good reasons for not using it. – ComicSansMS Feb 13 '18 at 14:54
  • 13
    The default optimization level changes. With gcc and clang, at least, `RelWithDebInfo` uses `-O2`, but `Release` uses `-O3`. – David Stone Mar 16 '19 at 19:35
  • 1
    It's worth noting that you get to have your own optimization level if you want, regardless of whichever `CMAKE_BUILD_TYPE` you choose to use. – Dean Seo Oct 05 '19 at 03:31
  • Does this mean that MinSizeRel is potentially slower in performance? I am confused by your mention of the "Maximize speed" configuration. – pooya13 Apr 29 '20 at 20:21
  • Official CMake doc: [MinSizeRel](https://cmake.org/cmake/help/v3.5/variable/CMAKE_LANG_FLAGS_MINSIZEREL.html) short for minimum size release. They are not mention maximize speed. Should this answer need downvote because it make misleading? – Khoi V Aug 01 '21 at 13:51
  • @pooya13 you are right, MinSizeRel doesn't mention maximize speed. Dean answer also make me confused – Khoi V Aug 01 '21 at 13:54
  • 1
    @KhoiV I do admit that there's some misleading information so I made some edits on it. It was a long time ago I wrote this answer but if I remember correctly the reason I mentioned Visual Studio is probably the OP had some C# questions so I assumed the OP wanted it to be Windows-oriented (also mentioned in the question). The reason it has **Maximize Speed** is probably a silly mistake while copy-and-pasting the link and its title was coincidentially being *"(Minimize Size, Maximize Speed)"*. Thanks for the heads up. – Dean Seo Aug 01 '21 at 15:30
  • @pooya13 Please see my edit. Regarding the performance, I believe it depends. Minimized binary size should not mean it gives up on performance. – Dean Seo Aug 01 '21 at 15:32
  • Note that (as of posting) RelWithDebInfo is currently *deliberately broken* for generators targeting toolchains that have external symbol files, such as MSVC, and gcc/clang (dwarf), as it does *not* use the same optimization flags as Release. See https://gitlab.kitware.com/cmake/cmake/-/issues/20812 – Richard1403832 Feb 27 '23 at 15:13
2

Yes, you are correct:

I'm guessing RelWithDebInfo meant creating debuggable binaries, and MinSizeRel meant creating smallest possible size binaries.

RelWithDebInfo will add compiler flags for generating debug information (the -g flag for GCC / clang), and will result in debuggable, yet much larger binaries.

MinSizeRel will add compiler flags for generating more compact binaries (the -Os flag for GCC / clang), possibly on the expense of program speed.

If I want to generate a production build, should I choose Release?

Yes, Release would be a good choice. It should produce faster binaries, by specifying compiler optimization level for favoring speed (-O3 for GCC / clang), and not including debug symbols.

valiano
  • 16,433
  • 7
  • 64
  • 79
2

You could also get an overview of the built types with:

cmake -LAH .. | grep -C1 CMAKE_CXX_FLAGS

which as mentioned at: How to list all CMake build options and their default values? lists all built-in variables and gives:

// 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

The meaning of each build type should be clear from these for those familiar with the corresponding GCC options.

By default, if -DMAKE_BUILD_TYPE is not passed, CMAKE_BUILD_TYPE is empty, and none of the extra CMAKE_CXX_FLAGS_XXX values are added.

Tested on Ubuntu 22.10, CMake 3.24.2.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985