In episode 5, we started to use C++ but we didn't (*) add C++ specific compiler options. In this episode we will see how to do this. Because guess what: C++ and C are different language and compiling C code with C++ specific options may not be a good idea 😄
Generator expression
(*) This isn't completely true: to fix a linker error I suggested to add -fno-exceptions
and this is clearly a C++ specific option. I didn't show any CMake code but if you tried on your computer, I suppose you simply added it like this and compilation went fine:
target_compile_options(${EXECUTABLE} PRIVATE
# some options...
-Wall
-Wextra
-pedantic
-fno-exceptions
$<$<CONFIG:Debug>:-Og>)
Let's try to add an highly recommended option for embedded C++, -fno-rtti
, and recompile the project:
target_compile_options(${EXECUTABLE} PRIVATE
# some options...
-Wall
-Wextra
-pedantic
-fno-exceptions
-fno-rtti
$<$<CONFIG:Debug>:-Og>)
λ mingw32-make.exe all
[...]
[ 3%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c.obj
cc1.exe: warning: command line option '-fno-rtti' is valid for C++/D/ObjC++ but not for C
[ 7%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c.obj
cc1.exe: warning: command line option '-fno-rtti' is valid for C++/D/ObjC++ but not for C
[ 11%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c.obj
cc1.exe: warning: command line option '-fno-rtti' is valid for C++/D/ObjC++ but not for C
[ 15%] Building C object CMakeFiles/nucleo-f413zh.out.dir/BSP/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c.obj
cc1.exe: warning: command line option '-fno-rtti' is valid for C++/D/ObjC++ but not for C
[...]
OK 😦 Interesting 😐 What do we do?
The solution is in the title of this section (and shown in the previous code extract with $<$<CONFIG:Debug>:-Og>
): a generator expression to add this option only when the language is C++. Simply replace fno-rtti
with $<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
in target_compile_options()
. That's it! Problem solved! 😙
GCC's option files
No doubt that you will add other options for C+++, such as -Wsuggest-override
, -Wold-style-cast
or -Wuseless-cast
. You can use the generator expression each time an option causes warnings, but there is an easier solution: put all C++ options in a file and tell GCC to read this file with the @ option:
@file
Read command-line options from
file
. The options read are inserted in place of the original@file
option. Iffile
does not exist, or cannot be read, then the option will be treated literally, and not removed.Options in
file
are separated by whitespace. A whitespace character may be included in an option by surrounding the entire option in either single or double quotes. Any character (including a backslash) may be included by prefixing the character to be included with a backslash. The file may itself contain additional@file
options; any such options will be processed recursively.
Create a simple text file called gcc-options-cxx.txt
:
-Wold-style-cast
-Wuseless-cast
-Wsuggest-override
-fno-exceptions
-fno-rtti
And change the compiler options:
target_compile_options(${EXECUTABLE} PRIVATE
# some options...
-Wall
-Wextra
-pedantic
$<$<COMPILE_LANGUAGE:CXX>:@${CMAKE_SOURCE_DIR}/gcc-options-cxx.txt>
$<$<CONFIG:Debug>:-Og>)
This technique has advantages:
- Generator expressions are quite heavy so having the option in a file improve readability of the CMake code.
- You don't have to manually add the generator expression to each option.
But it has one drawback:
- Source files are not recompiled when the file is changed 😕
I think we may find a CMake trick to trigger a full project rebuild. Since this file is almost never modified, I simply force a project rebuild if needed.
Update from 2024 February 8th. I don't known if this is a new feature in CMake, or if 4 years ago I wasn't aware of this possibility, but you can put all your C++ options into a single generator expression:
$<$<COMPILE_LANGUAGE:CXX>:
-fno-rtti
-Wold-style-cast
-Wuseless-cast
-Wsuggest-override
>
I strongly recommend this solution over a file with all the options. It may be fine when working alone on a project, but neither your coworkers not your CI server will see that the file has changed and that the whole project must be rebuilt.
Avoid warnings caused by 3rd party headers
If you recompile the project with above options you will see that compilation of the C files (generated by STM32CubeMX) is OK. Nevertheless many warnings are raised on application.cpp
because of useless casts. Example:
D:\cmake_stm32\BSP\Drivers\CMSIS\Include/core_cm4.h:1663:75: warning: useless cast to type 'uint32_t' {aka 'long unsigned int'} [-Wuseless-cast]
1663 | reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
|
In episode 3, we learned how to avoid warnings in 3rd party files, but that was for source files.
Here, the warnings are caused by 3rd party header files in one of our source files.
The solution is to tell CMake that the directories that contains these files are system include directories:
If
SYSTEM
is specified, the compiler will be told the directories are meant as system include directories on some platforms (signalling this setting might achieve effects such as the compiler skipping warnings, or these fixed-install system files not being considered in dependency calculations - see compiler docs).
"See compiler docs" ? OK, let's have a look at GCC's documentation about Options for Directory Search and how system directories are treated differently from normal directories. In the second page we can read:
All warnings, other than those generated by
#warning
(see Diagnostics), are suppressed while GCC is processing a system header.
This is have the desired effect then!
Change your CMakeLists.txt
so that you have to separate calls to target_include_directories()
:
target_include_directories(${EXECUTABLE} SYSTEM PRIVATE
BSP/Inc
BSP/Drivers/STM32F4xx_HAL_Driver/Inc
BSP/Drivers/CMSIS/Device/ST/STM32F4xx/Include
BSP/Drivers/CMSIS/Include)
target_include_directories(${EXECUTABLE} PRIVATE
source/)
Warnings have disappeared :)
Conclusion
In this episode, we have seen that generator expressions are again a good solution to conditionally add options. This is the modern C++ technique to add C++ specific options to the compiler. COMPILE_LANGUAGE
in generator expression was introduced in CMake 3.3 and prior to this version, the now-deprecated technique was to modify the CMAKE_CXX_FLAGS
property directly.
We have also seen that we can avoid warnings caused by 3rd party headers by considering them as system includes. We cannot modify them so we ask GCC to suppress their warnings.
Top comments (0)