15

I'm currently trying to setup a project that will make use of multiple compilers (including Clang, MSVC, and GCC) using Visual Studio 2019's new CMake functionalities (notably using Clang and Ninja in conjunction with CMake and VS2019).

I'm using CMake to configure the project to be "compiler-agnostic", so that I don't need to edit the code itself to handle different compilers via pre-processor instructions or #pragma instructions.

This project needs to be configured to have a high warning level (/W4 for MSVC, -Wall, -Wextra, and -Wpedantic for Clang), and must treat warnings as errors.

I don't have any issues when it comes to configuring the MSVC portion of the project. A lot of these settings have "sane" defaults that "just work" as I'd expect them to. When it comes to Clang, however, I've encountered a problem:

I can't seem to disable warnings for third-party library header files. I'm currently using the Dear Imgui and SFML libraries. Since Dear Imgui isn't pre-compiled, I simply do the following in my CMakeLists.txt file to include it:

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/imgui
)

I use the statically linked version of SFML, so I do the following to include it:

# Find SFML and link statically to it.
# Note: We need to set the SFML_DIR variable manually.
set(SFML_STATIC_LIBRARIES TRUE)
set(SFML_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libs/SFML-2.5.1/lib/cmake/SFML")
find_package(SFML 2.5.1 COMPONENTS system audio window graphics REQUIRED)

target_link_libraries(${PROJECT_NAME}
    PRIVATE
        sfml-system
        sfml-audio
        sfml-window
        sfml-graphics
)

Sadly, SFML doesn't currently follow the current CMake standard way of adding libraries, so it's kind of weird to work with or configure it via CMake.

Now, the above works just fine when it comes to including the libraries in my project (but it might be something I need to change, so I've included it in the post). The problems come when I try to impose the warnings and warnings-as-errors configurations to them when using Clang.

Here are the parts of my CMakeLists.txt file which handle Clang and my C++ configurations:

# Set project to use C++ 17 standard.
set_target_properties(
    ${PROJECT_NAME}
    PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED ON
)


target_compile_options(${PROJECT_NAME} PRIVATE
    # All warnings, warnings as errors, be pedantic.
    -Wall
    -Wextra
    -Werror
    -Wpedantic

    # Disable warnings about C++98 incompatibility. We're using C++17 features...
    -Wno-c++98-compat
    -Wno-c++98-compat-pedantic
)

Using the above configuration results in hundreds of warnings/errors in Dear Imgui's source files (due to the usage of "old-school" C++/C-style code), as well as a whole bunch of them in SFML's own source files and header files.

I've been looking for ways to get around this for nearly a week, before settling on the following solution (which doesn't entirely work, more on that later):

set(LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/libs")
set(IMGUI_FOLDER "${LIBS_FOLDER}/imgui")
set(SFML_FOLDER "${LIBS_FOLDER}/SFML-2.5.1/include/SFML")

file(GLOB LIBRARY_FILES
    # Dear-imgui
    "${IMGUI_FOLDER}/*.cpp"
    "${IMGUI_FOLDER}/misc/freetype/*.cpp"
    "${IMGUI_FOLDER}/misc/fonts/*.cpp"
    "${IMGUI_FOLDER}/misc/cpp/*.cpp"

    # SFML
    "${SFML_FOLDER}/Audio/*.cpp"
    "${SFML_FOLDER}/Graphics/*.cpp"
    "${SFML_FOLDER}/Network/*.cpp"
    "${SFML_FOLDER}/System/*.cpp"
    "${SFML_FOLDER}/Window/*.cpp"
)

set_source_files_properties(
    ${LIBRARY_FILES}
    PROPERTIES
    COMPILE_FLAGS
    "-Wno-everything"
)

I begin by GLOB-ing my library source files (NOTE: I know GLOB is usually looked down upon, but I felt like using it with third-party library files was fine since they're not supposed to change anyway). I then pass them to the set_source_files_properties function to apply the -Wno-everything flag, which seems to properly suppress all errors and warnings from those files.

Everything seems to work just fine, except for one warning which I can't seem to disable without using a #pragma instruction in my code (which I want to avoid). When compiling an empty main function that includes SFML headers, I get warnings about their .hpp files (which can't be passed to the set_source_files_properties function).

This:

#include <SFML/Graphics.hpp>

int main()
{
}

Results in the following warnings/errors:

zero as null pointer constant [-Werror,-Wzero-as-null-pointer-constant]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]

In these respective SFML files:

ThreadLocal.hpp (57)
Keyboard.hpp (161)
Event.hpp (105)
PrimitiveType.hpp (52)

Other things I've tried that didn't work:

  • Putting the .h/.hpp files in the set_source_files_properties CMake function (alongside the .cpp files). Works for Dear Imgui, but all its errors were in .cpp files, not its headers. Doesn't work for SFML's headers.
  • (Not for SFML, but for Dear Imgui) Including the directories as SYSTEM includes to suppress the warnings. Doesn't appear to work on Windows. Can't really do this with SFML, since I'm using CMake's find_package function instead of doing everything manually.
  • Using #pragma instructions. While this worked, every SFML file has dozens of different errors, and I want to avoid using #pragmas everywhere (or wrapping SFML's headers in my own headers that just wrap the #include instruction in #pragmas).

Is it even possible to suppress these warnings for my library headers without #pragmas? I've never really used Clang before, so apologies if this seems like a simple question, but searching online hasn't really given me anything that would work:

  • Outside of a commandline (I'm using Visual Studio with CMake).
  • On Windows (the system flag doesn't seem to work with this setup).
  • That would work with CMake specifically.
micka190
  • 742
  • 2
  • 8
  • 20
  • I know gcc and probably also clang disable some warning for system include files. So if you specifies your headers directories with -isystem command line option in place of -I it should work. With cmake you can specify this using `target_include_directories( SYSTEM ).` – Oliv Jun 21 '19 at 17:05
  • 1
    @Oliv As mentioned in the post, I can't really use the `SYSTEM` keyword for SFML since I'm not including its directories myself, I'm using the `find_package` command which includes the SFML directories in its own way. – micka190 Jun 21 '19 at 17:10
  • Sorry, I did not read all your long question. You can still do target_include_directories( SYSTEM ). You can get using get_property. gcc and probably clang too consider that directories specified with both -I and -isystem are system directories. – Oliv Jun 21 '19 at 17:14
  • @Oliv Just tried adding the SFML include directory to my `target_include_directories` function (with the `SYSTEM` keyword) and it doesn't appear to work. I've also tried placing the function before and after both the `find_package` and `target_link_libraries` functions, but it still didn't work. It might be because the warnings/errors are the header files themselves? – micka190 Jun 21 '19 at 17:29
  • 1
    I've faced this issue, short story: VS has kinda experimental equivalent for `-isystem` (see https://devblogs.microsoft.com/cppblog/broken-warnings-theory/) thus `clang-cl` does not support it explicitly. You can still hack it with `-Xclang -isystem /path/to/libs/imgui`. But... it seems to be buggy and may cause imported headers to not compile... You have to check it on your own. (I will try to converty it to an answer with some code samples later, but I need to take a look at code I was working on). – R2RT Jun 24 '19 at 19:24
  • This link may help for "deprecated" warnings: https://stackoverflow.com/q/295120 – kiner_shah May 25 '21 at 13:27

2 Answers2

13

You can mark the include paths as SYSTEM. Most compilers will not report warnings in system headers. In your case that might look something like this:

include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/libs/imgui)

set_target_properties(sfml-system PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-system,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-audio PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-audio,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-window PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-window,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-graphics PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-graphics,INTERFACE_INCLUDE_DIRECTORIES>)

For example, this dummy project:

project(example)
cmake_minimum_required(VERSION 3.18)

add_library(dep INTERFACE)
target_include_directories(dep INTERFACE include)
file(WRITE include/header.h "static int a;")

add_library(lib STATIC lib.c)
target_link_libraries(lib PRIVATE dep)
target_compile_options(lib PRIVATE -Wunused -Werror)
file(WRITE lib.c "#include <header.h>")

Fails with:

$ cmake . && make
include/header.h:1:12: error: ‘a’ defined but not used [-Werror=unused-variable]

But after adding this line:

set_target_properties(dep PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:dep,INTERFACE_INCLUDE_DIRECTORIES>)

It builds with no errors.

Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31
2

There is a way to suppress warnings from third party headers in a CMake way.

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

The system keyword is most likely what you want.

If SYSTEM is specified, the compiler will be told 
the directories are meant as system include directories on some platforms

The SYSTEM keyword adds -isystem for GCC/Clang. Instead of treating the directory like a normal include directory.

# GCC docs
Warnings from system headers are normally suppressed.
On the assumption that they usually do not indicate real problems
and would only make the compiler output harder to read.

For a while there was no solution for MSVC until recently in CMake 3.22, because the MSVC compiler finally added support for this.

Here is the MSVC blog post where they talk about the new compiler functionality.

The “Ninja” and “NMake Makefiles” generators now use
the MSVC “-external:I” flag for system includes. 
This became available as of VS 16.10 
(toolchain version 14.29.30037).