The SYSTEM property from CMake 3.25
Tags: cpp, programming, todayilearned, cmake
Published: 2022-12-09 15:05:46

CMake 3.25 introduced a new variable called `SYSTEM`. It will help us handle warnings from 3rd party libraries. Let's see how!

## The issue (with a minimal example project)

Here is a minimal example project to reproduce the issue. It simply prints something with the great [`fmt`](https://github.com/fmtlib/fmt) library:

```cpp
#include <fmt/core.h>

int main()
{
    fmt::print("The answer is {}", 42);
}
```

The library is fetched from GitHub thanks to CMake's [`FetchContent` module](https://cmake.org/cmake/help/latest/module/FetchContent.html):

```cmake
cmake_minimum_required(VERSION 3.25)
project(CMake_SYSTEM)

# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Fetch fmt from GitHub
include(FetchContent)

FetchContent_Declare(
        fmt
        GIT_REPOSITORY https://github.com/fmtlib/fmt.git
        GIT_TAG 9.1.0
)

FetchContent_MakeAvailable(fmt)

# Create executable
add_executable(${PROJECT_NAME} main.cpp)

target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -pedantic -Wswitch-enum)

target_link_libraries(${PROJECT_NAME} PRIVATE fmt)
```

The project can be built with:

```bash
mkdir build
cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=RELEASE
ninja
```

There is nothing very fancy here. However: when both C++20 and `-Wswitch-enum` are used, warnings like these are emitted:

```raw
C:/.../cmake_system/build/_deps/fmt-src/include/fmt/core.h:2796:3: warning: enumeration value 'dec' not handled in switch [-Wswitch-enum]
 2796 |   switch (specs.type) {
      |   ^~~~~~
C:/.../cmake_system/build/_deps/fmt-src/include/fmt/core.h:2796:3: warning: enumeration value 'oct' not handled in switch [-Wswitch-enum]
C:/.../cmake_system/build/_deps/fmt-src/include/fmt/core.h:2796:3: warning: enumeration value 'hex_lower' not handled in switch [-Wswitch-enum]
```

Either use C++17 or remove the compiler option, and the warnings will disappear. Both of these solutions are not satisfying, but it's worth mentioning them because you may not run into the issue if you compile the code without one of these parameters.

The satisfying solution would be to ask GCC to treat `fmt`'s headers as system headers, so that no warning will be emitted.

## Before CMake 3.25

To ask GCC to treat a directory as a system include directory, you have to the following in CMake:

```cmake
target_include_directories(target_name SYSTEM path/to/the/directory).
```

Here, we don't have any explicit call to `target_include_directories()` as we directly call `target_link_libraries()`. There is a [generic solution]((https://stackoverflow.com/questions/52135983/cmake-target-link-libraries-include-as-system-to-suppress-compiler-warnings)) to get the path to any target's include directory, and we simply have to add these two magic lines at the end of our `CMakeLists.txt`:

```cmake
get_target_property(fmt_include_dir fmt INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${fmt_include_dir})
```

That's it! Problem solved.

In case you wonder: here, the directory is `build/_deps/fmt-src/include`. Not something very easy to guess.

## CMake 3.25 to the rescue

Obviously, the solution shown in the previous section isn't perfect. For instance, if `fmt` was used by several targets, we would have to call `target_include_directories()` several times. In some cases, `include_directories()` will help if you accept that you can't really control the affected targets.

This is why the [SYSTEM target property](https://cmake.org/cmake/help/v3.25/prop_tgt/SYSTEM.html) was created in [CMake 3.25](https://gitlab.kitware.com/cmake/cmake/-/issues/18040):

> Specifies that a target is a `SYSTEM` library. This has the following effects:
>
> - Entries of `INTERFACE_INCLUDE_DIRECTORIES` are treated as `SYSTEM` include directories when compiling consumers.
> - Entries of `INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` are not affected, and will always be treated as `SYSTEM` include directories.

There is also a [directory property](https://cmake.org/cmake/help/v3.25/prop_dir/SYSTEM.html) with the same name:

> This directory property is used to initialize the `SYSTEM` target property for targets created in that directory. It is set to true by `add_subdirectory()` and `FetchContent_Declare()` when the `SYSTEM` option is given as an argument to those commands.

## How to use the SYSTEM property

### With `FetchContent_Declare()`

To fix our example project, we can simply add a parameter to `FetchContent_Declare()`:

```cmake
FetchContent_Declare(
        fmt
        GIT_REPOSITORY https://github.com/fmtlib/fmt.git
        GIT_TAG 9.1.0
        SYSTEM
)
```

The warnings are now gone!

NOTE = I got error with this, but I don't know why:

### Manually setting the property

Another solution would be to manually change the property of the target. Just after `FetchContent_MakeAvailable(fmt)`, the target is available and can be modified with `set_target_properties(fmt PROPERTIES SYSTEM TRUE)`.

You can do this with any particular target, and you can do the same with a particular directory thanks to `set_directory_properties()`.

### With `add_subdirectory()`

Let's imagine we had manually downloaded `fmt`'s sources and placed them in our project. We would have called `add_subdirectory(fmt_directory SYSTEM)` and all the target created there would have been flagged as `SYSTEM`.

## Conclusion

Even if we might feel `FetchContent` is solely used to get 3rd party libraries from GitHub, [this is not the only use case](https://gitlab.kitware.com/cmake/cmake/-/issues/22401). This is why the `SYSTEM` is `FALSE` by default. Personally, I will probably always set it to `TRUE` when I get libraries from GitHub to avoid warnings!

PS: once upon a time, I wrote [an article](https://dev.to/younup/cmake-on-stm32-episode-3-handle-warnings-in-3rd-party-files-4gg4) on how to avoid warnings in specific source files.