如何在CMake中设置警告级别?

164

如何使用CMake项目(而不是整个解决方案)设置警告级别? 应该在Visual StudioGCC上工作。

我发现了各种选项,但大多数似乎要么不起作用,要么与文档不一致。


听起来你是从Visual Studio背景过来的。我在那个领域没有深厚的背景,但我相信VS项目是CMake目标,而VS解决方案类似于CMake项目。由于这是关于CMake的问题,如果您使用CMake术语而不是VS术语,可能会帮助其他读者。 - starball
7个回答

165

在现代CMake中,以下内容运行良好:

if(MSVC)
  target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX)
else()
  target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()

我的同事建议了另一种版本:

target_compile_options(${TARGET_NAME} PRIVATE
  $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
)

用实际的目标名称替换${TARGET_NAME}-Werror是可选的,它将所有警告转换为错误。

或者按照评论中@aldo的建议使用add_compile_options(...)将其应用于所有目标。

此外,请确保理解PRIVATEPUBLIC之间的区别(公共选项将被依赖于给定目标的目标继承)。

正如@davidfong在评论中指出的那样,自CMake v3.24起,存在CMAKE_COMPILE_WARNING_AS_ERROR变量,可以将编译警告视为错误。如果它设置在CMakeLists.txt中,则用户仍然可以使用--compile-no-warning-as-errorcmake标志关闭它。如果要手动添加警告作为错误,请将/WX添加到Windows中,将-Werror添加到其他地方,并将其添加到target_compile_options中。


35
如果你想将其应用于所有目标,请简单地使用add_compile_options(...) - aldo
5
使用add_compile_options()的问题在于,警告会传播到通过add_subdirectory()添加的目标。如果以这种方式包含外部库,则如果该库是使用不同警告级别设计的,则可能会收到大量警告。 - trozen
6
请不要无条件地添加-Werror-Werror对于开发来说非常好,但最终用户可能会使用带有新警告的更新编译器。您不希望破坏他的进程。 - Jérôme Pouiller
4
不是要挑剔,但为了统一起见,“挑剔”标志应该被称为“-Wpedantic”。 - Raleigh L.
1
@RaleighL,谢谢,已修复 - pedantic 赢了! - mrts
这个可以运行,但会产生很多 D9025 警告,覆盖了 '/W3' 的 '/W4'。 - Dan M.

107

更新:此回答早于现代CMake时代。每个明智的CMake用户都应避免直接操作CMAKE_CXX_FLAGS,而应调用target_compile_options命令。请查看mrts' answer,其中提供了推荐的最佳实践。

您可以执行类似以下内容的操作:

if(MSVC)
  # Force to always compile with W4
  if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
    string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
  endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
  # Update if necessary
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
endif()

请注意,Visual Studio的新版本(至少2013)支持/Wall标志(名为EnableAllWarnings)。它产生的警告甚至比/W4还要多。然而,根据我的经验,它会产生过多的警告。 - Adam Badura
13
如果您想采用“减法”策略来处理警告信息,就像clang的“-Weverything”一样,那么/Wall是可以使用的。不需要选择要启用的警告信息,而是先启用所有内容,然后再选择要禁用的特定警告信息。 - bames53
1
请注意,“现代 CMake”指的是 CMake 版本 3.20 或更高版本,它不再强制默认构建标志包括“/W3 /GR”。 - Chuck Walbourn
@ChuckWalbourn,如果其他地方设置了“默认”警告级别(即顶层的“add_compile_options”),你仍然需要进行一些调整。 - Dan M.

28

我写的一些CMake模块包括实验性的跨平台警告抑制

sugar_generate_warning_flags(
    target_compile_options
    target_properties
    ENABLE conversion
    TREAT_AS_ERRORS ALL
)

set_target_properties(
    foo
    PROPERTIES
    ${target_properties}
    COMPILE_OPTIONS
    "${target_compile_options}"
)

Xcode 的结果:

  • 设置 CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION Xcode 属性(也称为 构建设置 -> 警告 -> 可疑的隐式转换 ->
  • 添加编译器标志:-Werror

Makefile gcc 和 clang:

  • 添加编译器标志:-Wconversion-Werror

Visual Studio:

  • 添加编译器标志:/WX/w14244

链接


2
很遗憾,CMake没有提供这个功能。 - Slava
3
好消息。很抱歉我在这里发布而不是在cmake邮件列表中发布,但如果没有级别,这将是无用的,有太多警告需要列出它们全部。如果您想要统一它,一种方法是使用两个单独的cmake_level——基于clang的统一警告集和特定于编译器含义的native_level。其中之一可能可以缩短为level。很抱歉如果我没有真正遵循谈话并且理解有误。 - Slava
1
@void.pointer提出了一个很好的观点。你提出的答案是:“我计划添加这个功能”。它并没有说明你已经进行了一些初步的研究,现在希望有人为你做更多的工作。如果你不想被归功于实现(以及关于其进展的问题),你需要编辑你的答案,并与你已经超过一年没有取得任何进展的任务分离。 - IInspectable
好的,那么您介意分享一下您的路线图吗?这样未来的访问者就会知道这个功能何时可用了。 - IInspectable
6
你似乎不理解。如果你建议实现一个功能,那么你需要在适当的时间内实现该功能。如果不能做到,你被要求从你提出的答案中删除该承诺。你没有表现出任何实现该功能的承诺,所以不要声称有。我知道这很困难。我也知道你可能无法完成这个任务。我只是要求你让你的回答反映出这一点。 - IInspectable
显示剩余2条评论

20
根据Cmake 3.27.4 documentation的说明:
if (MSVC)
    # warning level 4
    add_compile_options(/W4)
else()
    # additional warnings
    add_compile_options(-Wall -Wextra -Wpedantic)
endif()

GCC和Clang共享这些标志,所以这应该涵盖了全部3个。

1
不要使用这个。相反,使用target_compile_options()。参考最新文档似乎是“正确”的,但它只是为了向后兼容而存在的古老条目。 - caoanan
5
文档中没有提到向后兼容性。add_compile_options是适用于整个目录的,而target_compile_options仅适用于单个目标。 - Signal

9

目前我找到的最佳解决方案(包括编译器检查)如下:

if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake)")
    add_definitions(/W2)
endif()

GCC的等价选项是-Wall(未经测试)。


9
GCC 的警告标志是 -Wall,可能还有 -Wextra,详情请参见 http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html。 - Milliams
1
我使用的清单是“-W -Wall -Wextra -pedantic”。据我回忆,“-Wextra”在较新版本的GCC中替代了“-W”,但为了兼容性,我保留了两者。 - Jimmio92
3
add_definitions的预期目的并不是这样的(“它的作用是添加预处理器定义”)。这不仅仅是最佳实践建议。传递给该命令的参数将出现在生成的构建脚本中,调用那些不希望收到它们的工具(例如资源编译器)。 - IInspectable
2
那不是“编译器检查”,而是构建工具检查。 - Thomas

4
if(MSVC)
    string(REGEX REPLACE "/W[1-3]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()

如果您使用target_compile_options,cmake将尝试使用双重/W*标志,这会导致编译器发出警告。

谢谢。我一直天真地使用add_compile_options,结果得到了大量警告,提示/W3/W4覆盖。CMake没有解决这个基本选项(设置警告级别)的事实令人难以置信。 - Resurrection

1
如何使用CMake为项目(而不是整个解决方案)设置警告级别?
(我假设这是指CMake目标,而不是CMake项目。)
我找到了各种选项,但大多数似乎要么不起作用,要么与文档不一致。
Kitware的API可能会阻止您使构建系统变得脆弱和容易出错。其他答案鼓励特殊情况违反了现代CMake构建系统的至少两个重要原则...
首先,最好不要在CMakeLists.txt文件中指定特定于工具链的详细信息。这会使构建系统变得脆弱。例如,如果将来版本的工具链中出现新的警告,则编译器将发出错误,并且用户可能需要修改您的项目以构建目标。
相反,编写与工具链无关的CMakeLists.txt文件,并保留用户根据需要自定义的能力。理想情况下,您的项目应该在使用基本工具链配置的情况下在任何地方构建 - 即使这不会默认启用您首选的警告。
其次,如果您打算将二进制文件链接在一起,则标志应保持一致。这降低了不兼容性的风险,可能导致程序形式不正确。但是,警告标志不太可能影响代码生成,因此在链接在一起的目标之间变化这些标志可能是安全的。
因此...如果您希望针对每个工具链指定标志,并且如果您绝对必须为不同的目标使用不同的标志,请使用自定义变量:
# CMakeLists.txt
project(my_project)

add_executable(my_target source_file.cpp)
target_compile_options(my_target PRIVATE "${MY_PROJECT_ELEVATED_WARNING_FLAGS}")

有许多设置这些变量的方法,例如CMakeCache.txt、工具链文件以及通过CMAKE_PROJECT_INCLUDE_BEFORE。但最简单的方式是在配置期间使用命令行进行设置,对于GCC来说。

cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="-Wall;-Wextra;-Wpedantic;-Werror" <path-to-project>

适用于MSVC

cmake -DMY_PROJECT_ELEVATED_WARNING_FLAGS:STRING="/W4;/WX" <path-to-project>

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