Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to handle compiler flags when using CMake

I am struggling to find a proper way to propagate correct compiler flags for all targets.

Let's imagine that there is a project which contains a library and unit tests.

ProjectFolder
|-WorkerLibFolder
  |-Worker.cpp
  |-Worker.hpp
  |-CMakeLists.txt  (2)
|-main.cpp
|-CMakeLists.txt (1)
|-TestsFolder
  |-UnitTests.cpp
  |-CMakeLists.txt  (3)

In CMakeLists.txt (1) I'd like to set compile options globally because I assume that optimization level and other flags should be the same for all libraries of the project. add_compile_options(-Wall -Werror -Wno-error=maybe-uninitialized) is used to achieve it.

Also I use CMAKE_BUILD_TYPE feature which automatically sets needed optimization level with help of CMAKE_CXX_FLAGS_RELEASE or CMAKE_CXX_FLAGS_DEBUG flags.

Also the fact that afterwards one of these variables is implicitly passed to the compiler seems to be annoying, becaues I anyway have to set these variables in advance for needed optimization flags set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -DNDEBUG -DBOOST_DISABLE_ASSERTS") only because -O3 is by defaut set with Release configuration.

Anyway everything is ok up to the moment when I want to alway have -O0 being set when compiling test environment (UniTests.cpp). CMakeLists.txt (3) produces project_ut executable. I want WorkerLib to be compiled with configured optimization level (taked from CMAKE_BUILD_TYPE), but it automatically means that CMAKE_CXX_FLAGS_RELEASE with -Ofast is propagated to UnitTest.cpp

I guess that I might be doing something strange here and there is a better way to deal with an issue. One option here is to pass optimization flags without help of CMAKE_BUILD_TYPE feature but it seems to be a wrong (as I do not want to maintain lists of flags for every target).

EDIT:

#CMakeLists.txt(1):
cmake_minimum_required(VERSION 3.14.1)
project(TestProject LANGUAGES CXX C)

#####  common comp flags #####

add_compile_options(-Wall -Werror -Wno-error=maybe-uninitialized)

##############################

set(RELEASE_FLAGS "-Ofast -DNDEBUG -DBOOST_DISABLE_ASSERTS")
set(DEBUG_FLAGS "-O0 -ggdb3")

set(CMAKE_CXX_FLAGS_RELEASE ${RELEASE_FLAGS})
set(CMAKE_C_FLAGS_RELEASE ${RELEASE_FLAGS})

set(CMAKE_CXX_FLAGS_DEBUG ${DEBUG_FLAGS})
set(CMAKE_C_FLAGS_DEBUG ${DEBUG_FLAGS})

add_subdirectory(WorkerLibFolder)
add_subdirectory(TestsFolder)


add_executable(mainExec main.cpp)
target_link_libraries(mainExec PRIVATE worker)

#CMakeLists.txt(3):
add_executable(tests UnitTests.cpp)
target_link_libraries(tests PRIVATE worker)
#I want sommehow fix optimization flags for that particular target, while for worker library left them as they were set
#CMakeLists.txt(2):
add_library(worker Worker.cpp)
target_include_directories(worker PUBLIC ${CMAKE_CURRENT_LIST_DIR})
like image 677
NwMan Avatar asked Dec 31 '22 11:12

NwMan


1 Answers

The proper way to set flags is with set_compile_options and target_compile_options and macros with add_compile_definitions and target_compile_definitions. You should not (or rarely) touch CMAKE_*_FLAGS yourself and with the creation of generator expressions, you rarely should touch CMAKE_*_FLAGS_*, too. Using $<CONFIG:RELEASE> is simpler, because you don't need to care about case (-DCMAKE_BUILD_TYPE=Release and -DCMAKE_BUILD_TYPE=rElEaSe are both release builds) and for my eyes much cleaner to read.

Do in your main CMakeLists.txt:

add_compile_options(
       -Wall -Werror -Wno-error=maybe-uninitialized
       $<$<CONFIG:RELEASE>:-Ofast>
       $<$<CONFIG:DEBUG>:-O0>
       $<$<CONFIG:DEBUG>:-ggdb3>
)
add_compile_definitions(
        $<$<CONFIG:RELEASE>:NDEBUG>
        $<$<CONFIG:RELEASE>:BOOST_DISABLE_ASSERTS>
)
add_subdirectory(WorkerLibFolder)
add_subdirectory(TestsFolder)
add_executable(main ...)

I want to alway have -O0 being set when compiling test environment

So do exactly that. Inside TestsFolder do:

add_compile_options(-O0)
add_library(test_environment  ....)

Or better:

add_library(test_environment ..
target_compile_options(test_environment PRIVATE -O0)

Because compile options are accumulated, the options added in TestsFolder will be suffixed/the last options on the compile line so it will work, I mean ex. gcc -O3 -Ofast -O0 will compile the same as gcc -O0.

target_include_directories(worker PUBLIC ${CMAKE_CURRENT_LIST_DIR})

The CMAKE_CURRENT_LIST_DIR is usually used from include(this_files) files to indicate from where the file is. To include current directory the CMAKE_CURRENT_SOURCE_DIR is more commonly used which, well, indicates the current source directory cmake is processing.

PS: I wouldn't unittest a program/library with different optimize options then the release is build with. I unittest with exactly the same compile options as the release builds. Some bugs show up only with optimizations enabled. The way I preferably unittest a (previous) library is to compile and unittest with both optimization disabled and enabled.

like image 94
KamilCuk Avatar answered Jan 04 '23 03:01

KamilCuk