ChrisG
ChrisG

Reputation: 251

Different CMAKE_BUILD_TYPE per target

I am working on a really large project, which I'm in the process of moving from using custom Makefiles to using cmake instead, but I'm still missing a functionality that was implemented with the Makefiles.

The project has many sub-directories, each one of which is compiled into a static library, and then linked into the final executable.

Here is a small example

src/
  lib1/
  lib2/
  lib3/
  main.cpp
  CMakeLists.txt

and in CMakeLists.txt might be something like this:

add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(lib3)
add_executable(test main.cpp)
target_link_libraries(test PUBLIC lib1 lib2 lib3)

I want to debug the final executable, but I don't want to build all static libraries with debug symbols and no optimizations, because then the debugging becomes too slow.

So I want to build lib2 with CMAKE_BUILD_TYPE=Release and lib1 and lib3 with CMAKE_BUILD_TYPE=Debug.

Please bear in mind that instead of three libraries, there are actually ~10, and I want to be able to do that for each one of them, and for a number of them at the same time.

Is there a way to do that from the main CMakeLists.txt?

What I would prefer would be something that would make this possible from the command line:

cmake -DDEBUG_LIBS={lib1,lib3} /path/to/src
cmake --build .

Upvotes: 7

Views: 4400

Answers (3)

fdk1342
fdk1342

Reputation: 3564

Don't set CMAKE_BUILD_TYPE keep it blank or make a custom one where you set exactly what you want the base flags to be. Then add the additional debug and optimizations for the libraries that you want. I would suggest creating a function (or macro) that each library target calls that checks to see if it shows up in DEBUG_LIBS and then call target_compile_options with the correct values. But you should set is as -DDEBUG_LIBS=lib1;lib3 so that list handling works.

function(check_debug libname)
  if(${libname} IN_LIST DEBUG_LIBS)
    target_compile_options(${libname} PRIVATE -g -O0)
  endif()
end_function()

Upvotes: 1

eerorika
eerorika

Reputation: 238461

Debug symbols do not usually affect speed. You can probably keep it for all targets. If the symbols are a problem, they can be stripped from the executable/library afterwards. This approach has the benefit that you can still debug the library that you didn't expect the need to debug (although if it is optimised, it may not necessarily be ideal experience).

You could enable or disable optimisation for single target with target_compile_options so that it is always or never optimised.


Specifying a different CMAKE_BUILD_TYPE can be problematic because it can affect macros which can affect the ABI of the program if you do things like add members based on NDEBUG macro. If you mix build types, then you must make sure that debug macros are not used in headers.

Simplest way to achieve that is to build the libs in separate projects and import the built libs into the final program that depends on the libraries.

Upvotes: 0

Thomas
Thomas

Reputation: 182083

You can use ExternalProject instead of add_subdirectory for your dependencies, tell it to install into a subdirectory of ${CMAKE_CURRENT_BINARY_DIR}, then use find_package to find it there. This lets you use any combination of build options that you desire.

This does require a two-stage process when you first build it: run cmake, then make to build the dependencies, then cmake again to detect the dependencies, then make again to build your own targets. There are ways around that if you want, too.

For example, here's how I'm building OpenCV as part of my project (simplified):

# Build OpenCV from source.
include(ExternalProject)
ExternalProject_Add(opencv
    SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/opencv
    CMAKE_ARGS
        -DBUILD_LIST=core,imgproc
        -DBUILD_SHARED_LIBS=OFF
        -DCMAKE_BUILD_TYPE=Release
        -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/deps/opencv
)

# Find it in the directory where we just built it.
find_package(OpenCV
    PATHS ${CMAKE_CURRENT_BINARY_DIR}/deps/opencv
    NO_DEFAULT_PATH
)

# Abort if we weren't able to find it.
if(NOT OpenCV_FOUND)
    message(STATUS "OpenCV was not found. This means it still needs to be built from source. To build it, run:")
    message(STATUS "")
    message(STATUS "    cmake --build ${CMAKE_CURRENT_BINARY_DIR} --target all")
    message(STATUS "")
    message(STATUS "Then re-run cmake to detect the built library.")
    return()
endif()

# Consume it in the usual way.
add_binary(my_binary ...)
target_link_libraries(my_binary ${OpenCV_LIBS})

No idea if this is "best practice" or not, but it works for me.

Upvotes: 1

Related Questions