CMAKE_CXX_FLAGS - removing defaults, yet allowing user-specified values

I would like to remove the default values from CMAKE_CXX_FLAGS while still allowing users to specify values in configuration mode (i.e., cmake ... -DCMAKE_CXX_FLAGS="...").

Clearly, this could be done by just writing to CMAKE_CXX_FLAGS, but the general consensus is to never alter CMAKE_* variables from within a CMakeLists.txt- which I agree is a good practice.

if(CMAKE_CXX_FLAGS)
  # Append user flags
  set(CMAKE_CXX_FLAGS "${MY_FLAGS} ${CMAKE_CXX_FLAGS}") # Booooo!
else()
  set(CMAKE_CXX_FLAGS ${MY_FLAGS})
endif()

Ideally, I’d like to do something like this

list(APPEND MY_FLAGS "-fpie")
if(CMAKE_CXX_FLAGS)
  list(APPEND MY_FLAGS ${CMAKE_CXX_FLAGS})
  unset(CMAKE_CXX_FLAGS)
endif()
add_executable(test test.cpp)
target_compile_options(test PRIVATE $<$<CONFIG:RELWITHDEBINFO>:${MY_FLAGS}>)

But using this gives

$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-g3"
  /usr/bin/c++ -g3 -O2 -g -DNDEBUG -fpie -g3 ...

I would expect it to generate

/usr/bin/c++ -fpie -g3 ...

From Strictly appending to CMAKE_<LANG>_FLAGS, I understand that CMAKE_CXX_FLAGS_RELWITHDEBINFO is essentially being built using set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_INIT} "-O2 -g -DNDEBUG"). In the case above, -g3 will override -g (at least on all compilers I’m aware of), so that’s not a problem. But I need to get rid of -DNDEBUG. I could do

list(APPEND MY_FLAGS "-fpie -UDNDEBUG")

but then I’d need to know all of the possible defaults CMAKE uses. At this point, I gave up and just overwrote CMAKE_CXX_FLAGS_RELWITHDEBINFO.

list(APPEND MY_FLAGS "-fpie")
if(CMAKE_CXX_FLAGS)
  list(APPEND MY_FLAGS ${CMAKE_CXX_FLAGS})
  set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${MY_FLAGS})
endif()
add_executable(test test.cpp)

but this gives

/usr/bin/c++ -g3 -fpie;-g3 -fpie -g3 ...

which is… neat. I manually convert the list to a spaced string

list(APPEND MY_FLAGS "-fpie")
if(CMAKE_CXX_FLAGS)
  list(APPEND MY_FLAGS ${CMAKE_CXX_FLAGS})
  list(JOIN MY_FLAGS " " _f)
  set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${_f})
endif()
add_executable(test test.cpp)

which gives what I expect

/usr/bin/c++ -g3 -fpie ...

Am I just left to overwrite CMAKE_CXX_FLAGS_RELWITHDEBINFO? I’d like to get around it, if at all possible.

Flags are strings; options are list. Use string(APPEND) here (this resolves the ; instance).

Otherwise, this behavior of CMake is very ancient and there have been efforts to help fix this (e.g., the MSVC runtime selection flags now being a target property). Other flags have yet to follow suit. For the -fpie flag specifically, there is already such a property: POSITION_INDEPENDENT_CODE. The CheckPIESupported module is also relevant and has an example.

However, it doesn’t seem to support per-config management (i.e., genex support). That’s probably worth a feature request.