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})
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With