'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})


Solution 1:[1]

There are several recent best practices (here, here and here) that suggest your CMakeLists.txt should be clean and flexible.

I would suggest moving those flags externally (i.e. not placed in your CMakeLists.txt), by keeping them in related bash scripts, e.g.

C_FLAGS=""
C_FLAGS="${C_FLAGS} -Wall"
C_FLAGS="${C_FLAGS} -g -gline-tables-only"

LINKER_FLAGS=""

BUILD_TYPE=Debug

#

cmake \
  -GNinja \
  -DCMAKE_C_COMPILER=clang \
  -DCMAKE_CXX_COMPILER=clang++ \
  -DCMAKE_POLICY_DEFAULT_CMP0056=NEW \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=On \
  -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
  -DCMAKE_C_FLAGS="${C_FLAGS}" \
  -DCMAKE_EXE_LINKER_FLAGS="${LINKER_FLAGS}" \
  -DCMAKE_SHARED_LINKER_FLAGS="${LINKER_FLAGS}" \
  -DCMAKE_MODULE_LINKER_FLAGS="${LINKER_FLAGS}" \
  "${SRC_DIR}"

This way you can have sets of flags for what you are trying to do.

A next step would be to allow for generator expressions and configure per target for specific options per build type, but it is hard to suggest without seeing an example from your CMakeLists.txt.

You could even have project-specific CMake variables that can be influenced and used per target, but that might be too complicated for your project.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1