How to enable assert in CMake Release mode?
Asked Answered
S

4

24

CMake is being used to compile some C++ files. There are assert calls in the code. These calls are disabled in Release mode of CMake. It defines NDEBUG in Release mode, I guess.

If I'm interested in having assert in Release mode of CMake, how do I enable it?

Sarnoff answered 3/3, 2014 at 6:56 Comment(2)
Remove this definition from CMAKE_CXX_RELEASE_FLAGS, obviously.Exocentric
Note that if you find yourself in need to do the checks in Release, assert might be the wrong tool for the job (although there are still valid use cases for your question, for instance debugging a problem that only occurs in Release). Consider introducing additional diagnostic macros that have weaker semantics than assert (which de facto specifies a condition that must never fail) that but can still be enabled selectively (for instance, a condition that can fail if the user passes invalid arguments to a function).Snowber
R
8

1

If you interested in assert functionality only in your own code then the simple one solution is to provide custom assert. For instance:

#if (MY_DEBUG)
# define MY_ASSERT(A) ... checks here ...
#else
# define MY_ASSERT(A) ... ignore A ...
#endif

Use option to enable/disable assert:

# CMakeLists.txt
option(ENABLE_MY_ASSERT "Turn on MY_ASSERT checks" OFF)
if(ENABLE_MY_ASSERT)
  add_definitions(-DMY_DEBUG=1)
else()
  add_definitions(-DMY_DEBUG=0)
endif()

In this case you have full control over your checks, you can verify one component and ignore others:

... FOO_DEBUG=0 BOO_DEBUG=1 BAR_DEBUG=0 ...

2

Add custom CMAKE_BUILD_TYPE (also see CMAKE_CONFIGURATION_TYPES):

cmake_minimum_required(VERSION 2.8.12)
project(foo)

set(CMAKE_CXX_FLAGS_MYREL "-O3")

add_library(foo foo.cpp)

output:

# Debug
# ... -g ...

# Release
# ... -O3 -DNDEBUG ...

# RelWithDebInfo
# ... -O2 -g -DNDEBUG ...

# MyRel
# ... -O3 ...
Ruffian answered 5/3, 2014 at 5:21 Comment(0)
W
15

See this answer in the CMake FAQ, i.e.:

Fix it manually by changing the definition of the cache variables CMAKE_C_FLAGS_RELEASE and CMAKE_CXX_FLAGS_RELEASE. This has to be done every time you set up a new build directory.

To fix it permanently, create a custom CMake rules file in your source folder with the desired settings for the release flags (omit the option /D NDEBUG). Then in your outermost CMakeLists.txt point the variable CMAKE_USER_MAKE_RULES_OVERRIDE to the custom CMake rules file.

Westerly answered 3/3, 2014 at 12:44 Comment(1)
This should be the accepted answer, as it fixes OPs problem without changing any CMake related code.Bonbon
S
15

This would be a solution for the MSVC compiler:

string( REPLACE "/DNDEBUG" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")

A better option may be to enable asserts not in Release mode but in RelWithDebInfo mode instead:

string( REPLACE "/DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")

But this depends on your project and preferences of course.

Subchloride answered 8/2, 2017 at 17:35 Comment(3)
For gcc or clang, /DNDEBUG -> -DNDEBUG. To clean this up, you would have wrap it in an if statement like if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") ... endif()Gusgusba
But why not just use the "Debug" build type then? Wouldn't that be equivalent?Atomizer
It's useful when you want to apply this change only to a target, without enabling assertions in the whole project (that would also require to recompile everything)Oatmeal
R
8

1

If you interested in assert functionality only in your own code then the simple one solution is to provide custom assert. For instance:

#if (MY_DEBUG)
# define MY_ASSERT(A) ... checks here ...
#else
# define MY_ASSERT(A) ... ignore A ...
#endif

Use option to enable/disable assert:

# CMakeLists.txt
option(ENABLE_MY_ASSERT "Turn on MY_ASSERT checks" OFF)
if(ENABLE_MY_ASSERT)
  add_definitions(-DMY_DEBUG=1)
else()
  add_definitions(-DMY_DEBUG=0)
endif()

In this case you have full control over your checks, you can verify one component and ignore others:

... FOO_DEBUG=0 BOO_DEBUG=1 BAR_DEBUG=0 ...

2

Add custom CMAKE_BUILD_TYPE (also see CMAKE_CONFIGURATION_TYPES):

cmake_minimum_required(VERSION 2.8.12)
project(foo)

set(CMAKE_CXX_FLAGS_MYREL "-O3")

add_library(foo foo.cpp)

output:

# Debug
# ... -g ...

# Release
# ... -O3 -DNDEBUG ...

# RelWithDebInfo
# ... -O2 -g -DNDEBUG ...

# MyRel
# ... -O3 ...
Ruffian answered 5/3, 2014 at 5:21 Comment(0)
M
7

Here's how LLVM does it. They add a LLVM_ENABLE_ASSERTIONS option to the project (change LLVM to your project's prefix), then check for it and filter the compile flags. There is a special case for MSVC.

Overall, this seems more sensible to me than defining a build configuration. Enabling assertions is IMO orthogonal concern to the overall build configuration.

# This is commonly needed so define it before we include anything else.
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)

[...]

if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" )
    option(QD_ENABLE_ASSERTIONS "Enable assertions" ON)
else()
    option(QD_ENABLE_ASSERTIONS "Enable assertions" OFF)
endif()

[...]

if(QD_ENABLE_ASSERTIONS)
    if(NOT MSVC)
        add_definitions(-D_DEBUG)
    endif()
    # On non-Debug builds cmake automatically defines NDEBUG, so we explicitly undefine it:
    if(NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
        # NOTE: use `add_compile_options` rather than `add_definitions` since
        # `add_definitions` does not support generator expressions.
        add_compile_options($<$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>:-UNDEBUG>)

        # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines.
        foreach (flags_var_to_scrub
                CMAKE_CXX_FLAGS_RELEASE
                CMAKE_CXX_FLAGS_RELWITHDEBINFO
                CMAKE_CXX_FLAGS_MINSIZEREL
                CMAKE_C_FLAGS_RELEASE
                CMAKE_C_FLAGS_RELWITHDEBINFO
                CMAKE_C_FLAGS_MINSIZEREL)
            string (REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " "
                    "${flags_var_to_scrub}" "${${flags_var_to_scrub}}")
        endforeach()
    endif()
endif()

Source: https://opensource.apple.com/source/llvmCore/llvmCore-2358.3/CMakeLists.txt.auto.html

Meeks answered 29/1, 2021 at 12:56 Comment(3)
The foreach block should be moved outside the CMAKE_BUILD_TYPE check block. Apart from that, it works well.Procumbent
Why is that the case?Meeks
For MSVC (default generator with msbuild), CMAKE_BUILD_TYPE is generally not specified at configure time. The configuration is chosen at make time. Putting the foreach block inside the CMAKE_BUILD_TYPE does not seem useful to me. (However, it does not harm too much, for generally people do not specified CMAKE_BUILD_TYPE, so the "NOT" condition is still satisfied generally.)Procumbent

© 2022 - 2024 — McMap. All rights reserved.