DEV Community

Cover image for CMake on SMT32 | Episode 6: add C++ specific compiler options
Pierre Gradot
Pierre Gradot

Posted on • Edited on

CMake on SMT32 | Episode 6: add C++ specific compiler options

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>)
Enter fullscreen mode Exit fullscreen mode

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>)
Enter fullscreen mode Exit fullscreen mode
ฮป 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
[...]
Enter fullscreen mode Exit fullscreen mode

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. If file 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
Enter fullscreen mode Exit fullscreen mode

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>)
Enter fullscreen mode Exit fullscreen mode

This technique has advantages:

  1. Generator expressions are quite heavy so having the option in a file improve readability of the CMake code.
  2. You don't have to manually add the generator expression to each option.

But it has one drawback:

  1. 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
        >
Enter fullscreen mode Exit fullscreen mode

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               */
      |   
Enter fullscreen mode Exit fullscreen mode

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/)
Enter fullscreen mode Exit fullscreen mode

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)