如何在CMakeLists.txt文件中抑制第三方库头文件中Clang的警告?

17

我目前正试图设置一个项目,将使用多个编译器(包括Clang、MSVC和GCC),使用Visual Studio 2019的新CMake功能(特别是使用Clang和Ninja与CMake和VS2019一起使用)。

我正在使用CMake来配置项目,使其“与编译器无关”,因此我不需要编辑代码本身来处理不同的编译器通过预处理指令或#pragma指令。

此项目需要配置为具有高警告级别(对于MSVC为/W4,对于Clang为-Wall-Wextra-Wpedantic),并且必须将警告视为错误。

当涉及到配置项目的MSVC部分时,我没有任何问题。许多这些设置具有“理智”的默认值,可以按照我的期望“正常工作”。然而,当涉及到Clang时,我遇到了一个问题:

我似乎无法禁用第三方库头文件的警告。我目前正在使用Dear ImguiSFML库。由于Dear Imgui没有预编译,因此我只需在我的CMakeLists.txt文件中执行以下操作即可将其包含:

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/imgui
)

我使用了SFML的静态链接版本,所以我按照以下方式进行包含:

# 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
)

很遗憾,SFML目前没有遵循当前的CMake标准方式添加库,因此通过CMake进行配置和使用有些奇怪。

现在,当涉及到包含库到我的项目中时,上述内容完全可以工作(但这可能是我需要更改的东西,所以我已经在帖子中包含了它)。当我尝试在使用Clang时将警告和警告作为错误的配置强加给它们时,问题就出现了。

下面是我CMakeLists.txt文件中处理Clang和我的C++配置的部分:

# 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
)

使用上述配置会导致Dear ImGui的源文件中出现数百个警告/错误(由于使用了“老派”的C ++ / C样式代码),以及在SFML自己的源文件和头文件中出现大量警告/错误。

我已经寻找了近一个星期的解决方法,在最终采用以下解决方案之前(但并不完全有效,稍后会有更多说明):

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"
)

首先,我使用 GLOB 命令查找我的库源文件(注意:我知道通常不建议使用GLOB命令,但我认为对于第三方库文件来说这是可以接受的,因为它们不应该被修改)。然后将它们传递给 set_source_files_properties 函数并应用 -Wno-everything 标记,似乎可以正确地抑制这些文件的所有错误和警告。

一切都运行得很好,除了一个警告,我无法在不使用 #pragma 指令的情况下禁用它(我想要避免这种方式)。当编译一个包含 SFML 头文件的空的 main 函数时,我会收到有关其 .hpp 文件的警告(这些文件不能传递到 set_source_files_properties 函数中)。

代码如下:

#include <SFML/Graphics.hpp>

int main()
{
}

以下是出现的警告/错误信息:

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]

在这些对应的SFML文件中:

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

我尝试过的其他方法:

  • set_source_files_properties CMake函数中放置.h/.hpp文件(与.cpp文件一起)。这对于Dear Imgui有效,但它的所有错误都在.cpp文件中,而不是在头文件中。对于SFML的头文件无效。
  • (对于Dear Imgui而非SFML)将目录包含为SYSTEM包含以抑制警告。在Windows上似乎无效。不能使用CMake的find_package函数做到这一点,因为我正在使用它而不是手动执行所有操作。
  • 使用#pragma指令。虽然这有效,但每个SFML文件都有数十个不同的错误,我想避免在所有地方使用#pragma(或将SFML的头文件包装在我的自己的头文件中,只需在其中包装#include指令即可使用#pragma)。

在不使用#pragma的情况下是否可能抑制这些库头文件的警告?我以前从未真正使用过Clang,如果这似乎是一个简单的问题,请谅解,但是在线搜索并没有给我任何有效的结果:

  • 在命令行之外(我使用的是带有CMake的Visual Studio)。
  • 在Windows上(系统标志似乎无法与此设置一起使用)。
  • 可以与CMake特定的操作一起使用。

我知道gcc和可能也包括clang会为系统包含文件禁用一些警告。因此,如果您使用-isystem命令行选项指定头文件目录而不是-I,它应该可以工作。使用cmake时,您可以使用target_include_directories(<target> SYSTEM <dir>)来指定这个选项。 - Oliv
1
如帖子中所述,我无法对SFML使用SYSTEM关键字,因为我不是自己包含其目录,而是使用find_package命令以其自己的方式包含了SFML目录。 - micka190
抱歉,我没有阅读完你的长问题。你仍然可以使用target_include_directories(<your_target> SYSTEM <dir_of_SFML>)。你可以使用get_property获取<dir_of_SFML>。gcc和可能的clang也认为同时使用-I和-isystem指定的目录是系统目录。 - Oliv
@Oliv 刚刚尝试将SFML包含目录添加到我的target_include_directories函数中(使用SYSTEM关键字),但似乎不起作用。我还尝试在find_packagetarget_link_libraries函数之前和之后放置该函数,但仍然无效。这可能是因为警告/错误是头文件本身引起的? - micka190
1
我曾经遇到过这个问题,简单来说:VS有一种类似于-isystem的实验性等效项(请参见https://devblogs.microsoft.com/cppblog/broken-warnings-theory/),因此`clang-cl`并没有明确支持它。你仍然可以通过`-Xclang -isystem /path/to/libs/imgui`进行黑客攻击。但是...它似乎存在缺陷,可能会导致导入的头文件无法编译...你必须自己检查。(我将尝试将其转换为带有一些代码示例的答案,但我需要查看我正在处理的代码)。 - R2RT
这个链接可能有助于“已弃用”的警告:https://dev59.com/onVC5IYBdhLWcg3wcgmd - kiner_shah
2个回答

13

你可以将包含路径标记为SYSTEM。大多数编译器不会在系统头文件中报告警告。在你的情况下,可能会像这样:

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>)

例如,这个虚拟项目:

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>")

失败,显示以下错误信息:

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

但是在添加了这一行之后:

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

它能够无错误地进行构建。


2
天啊 - 我已经和这个斗争了好长时间!非常感谢你,朋友。 - pdm

3

有一种方法可以用CMake的方式抑制第三方头文件中的警告。

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

系统关键字很可能是您想要的内容。
If SYSTEM is specified, the compiler will be told 
the directories are meant as system include directories on some platforms
< p > SYSTEM 关键字为 GCC/Clang 添加了 -isystem。它不像普通的包含目录一样被处理。 < /p >
# 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.

在最近的CMake 3.22中,MSVC编译器终于支持了这个功能,之前一直没有解决方案。

这里是MSVC博客文章,他们讨论了新的编译器功能。

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).

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接