【问题标题】:How to set compiler options with CMake in Visual Studio 2017如何在 Visual Studio 2017 中使用 CMake 设置编译器选项
【发布时间】:2018-02-10 06:08:26
【问题描述】:

Visual Studio 2017 带有完整的 CMake 集成。为了了解这种组合,我从这个基本示例开始:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(foo)
add_executable(foo foo.cpp)

// foo.cpp
int main() {}

这会正确生成构建脚本,并且可以毫无问题地编译和链接。这很容易。

另一方面,尝试设置编译器选项却并非易事。就我而言,我试图将警告级别设置为 4。

显而易见的解决方案

add_compile_options("/W4")

没有像预期的那样成功。传递给编译器的命令行现在包含/W4(如预期)和/W3(从其他地方获取),产生以下警告:

cl : Command line warning D9025: overriding '/W3' with '/W4'

要解决这个问题,我需要替换任何不兼容的编译器选项,而不仅仅是添加一个。 CMake 没有为此提供任何即时支持,标准解决方案(如this Q&A suggests)似乎是:

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

然而,这有两个问题:

  • 它设置全局CMAKE_CXX_FLAGS,适用于所有C++目标。这可能不是故意的(现在对我来说不是问题)。
  • 它无法扩展。对于要添加的每个编译器选项,您都必须阅读不兼容的选项,然后先手动删除这些选项。这将不可避免地失败1

我的问题有两个:

  1. CMake 集成从哪里获取默认设置,是否可以控制?
  2. 一般如何设置编译器选项? (如果这个话题过于宽泛,我很乐意仅在设置警告级别方面提供帮助。)


1顺便说一句,我复制的解决方案无法解释 /Wall 选项,这也与 /W4 不兼容。

【问题讨论】:

  • 一些编译器选项可以通过跨平台兼容的 CMake 变量或属性进行更改。但不幸的是,警告级别没有定义它们的通用方法。你可能想看看@ruslo 找到的here 的工作以及关于扩展CMake 本身here 的讨论。
  • @Florian:感谢您的确认,CMake 不直接支持设置警告级别。不过,您发布的链接对我并没有立即帮助:Ruslo 的作品仅支持设置/Wall,而第二个链接通过调整CMAKE_CXX_FLAGS 来工作。 @sakra 发布的用于覆盖默认编译器选项的 answer 看起来像是一个更简洁的解决方案,允许对设置编译器选项进行更细粒度的控制。
  • 当我为this question 工作时,我注意到add_compile_options() 命令确实替换/覆盖了警告级别规范,而不是像附加到@ 时那样仅将其附加到“附加选项”中987654339@。我不确定为什么会有区别(必须检查 CMake 的代码),但可以试试 add_compile_options("/W4") 吗?在我的 CMake 3.9 版中,我不再收到该编译器警告。
  • @Florian: add_compile_options("/W4") 仍然给我一个编译器警告。我正在使用 "Ninja" 生成器,使用 Visual Studio 2017 (15.3.3) 中内置的 CMake 支持,因此沿途没有生成 .vcxproj(据我所知)。这是使用cmake version 3.8.0-MSVC_2

标签: visual-c++ cmake visual-studio-2017


【解决方案1】:

编译器的默认设置取自 CMake 安装的 Modules 目录中的标准模块文件。实际使用的模块文件取决于平台和编译器。例如,对于 Visual Studio 2017,CMake 将从文件 Windows-MSVC.cmake 加载默认设置,并从 Windows-MSVC-C.cmakeWindows-MSVC-CXX.cmake 加载特定于语言的设置。

要检查默认设置,请在项目目录中创建一个文件CompilerOptions.cmake,其内容如下:

# log all *_INIT variables
get_cmake_property(_varNames VARIABLES)
list (REMOVE_DUPLICATES _varNames)
list (SORT _varNames)
foreach (_varName ${_varNames})
    if (_varName MATCHES "_INIT$")
        message(STATUS "${_varName}=${${_varName}}")
    endif()
endforeach()

然后在你的CMakeLists.txt中初始化CMAKE_USER_MAKE_RULES_OVERRIDE变量:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set (CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/CompilerOptions.cmake")
project(foo)
add_executable(foo foo.cpp)

在 Visual Studio 2017 中使用Open Folder 打开目录后配置项目时,IDE 的输出窗口中将显示以下信息:

 ...
 -- CMAKE_CXX_FLAGS_DEBUG_INIT= /MDd /Zi /Ob0 /Od /RTC1
 -- CMAKE_CXX_FLAGS_INIT= /DWIN32 /D_WINDOWS /W3 /GR /EHsc
 -- CMAKE_CXX_FLAGS_MINSIZEREL_INIT= /MD /O1 /Ob1 /DNDEBUG
 -- CMAKE_CXX_FLAGS_RELEASE_INIT= /MD /O2 /Ob2 /DNDEBUG
 -- CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT= /MD /Zi /O2 /Ob1 /DNDEBUG
 ...

所以警告设置 /W3 是从 CMake 变量 CMAKE_CXX_FLAGS_INIT 中提取的,然后应用于项目中生成的所有 CMake 目标。

要控制 CMake 项目或目标级别的警告级别,可以通过在文件中添加以下行来更改 CompilerOptions.cmake 中的 CMAKE_CXX_FLAGS_INIT 变量:

if (MSVC)
    # remove default warning level from CMAKE_CXX_FLAGS_INIT
    string (REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT}")
endif()

然后可以通过在CMakeLists.txt 中设置目标编译选项来控制警告标志:

...
add_executable(foo foo.cpp)
target_compile_options(foo PRIVATE "/W4")

对于大多数 CMake 项目,控制规则覆盖文件中的默认编译器选项而不是手动调整变量(如 CMAKE_CXX_FLAGS)是有意义的。

更改CompilerOptions.cmake 文件时,需要重新创建构建文件夹。在Open Folder 模式下使用Visual Studio 2017 时,从CMake 菜单中选择命令Cache ... -> Delete Cache Folders,然后从CMake 菜单中选择Cache ... -> Generate 以重新创建构建文件夹。

【讨论】:

  • 完美解释了默认值的来源,在单个位置调整默认值也看起来像是一个可维护的解决方案。转储变量列表及其值的脚本也非常有用。由于我以前找不到任何这些,我将假设存在我不知道的资源。您是否知道任何可以推荐的学习 CMake 的资源(在线或书籍)?
  • 查看源代码 ;-) CMake 的源代码在 cmets 中包含有趣的信息,不幸的是,这些信息在其他任何地方都不可用,例如:github.com/Kitware/CMake/blob/…
  • @sakra 这似乎在 VS2017 15.8 中不起作用。没有列出 CMAKE_CXX_FLAGS_。
【解决方案2】:

把我的评论变成答案

CMake 确实带有一些预设的编译器开关。对于 Visual Studio,主要是标准链接库、警告级别、优化级别、异常处理、调试信息和平台特定定义。

当您想要更改 CMake 生成的编译器设置时,您现在必须区分以下用例:

  1. 其他编译器标志 CMake 未定义与更改 CMake 的预设设置
  2. 项目默认设置与项目用户定义设置

那么让我们讨论一下针对这些情况的常见解决方案。


用户更改/添加到项目/CMake 编译器标志默认值

标准方法是使用 CMake 附带的工具(如 cmake-guiccmake)来修改缓存的编译器标志变量。

要在 Visual Studio 中实现这一点,您必须:

  • CMake / Cache / View CMakeCache
  • 手动更改,例如CMAKE_CXX_FLAGS/Wall

    CMakeCache.txt

    //Flags used by the compiler during all build types.
    CMAKE_CXX_FLAGS:STRING= /DWIN32 /D_WINDOWS /Wall /GR /EHsc
    
  • CMake / Cache / Generate


或者您通过CMakeSettings.json 文件预设CMAKE_CXX_FLAGS 缓存变量:

  • CMake / Change CMake Settings

    cmakeCommandArgs 中强制使用-DCMAKE_CXX_FLAGS:STRING=... 缓存条目

    CMakeSettings.json

    {
        // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
        "configurations": [
            {
                "name": "x86-Debug (all warnings)",
                "generator": "Visual Studio 15 2017",
                "configurationType": "Debug",
                "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
                "cmakeCommandArgs": "-DCMAKE_CXX_FLAGS:STRING=\"/DWIN32 /D_WINDOWS /Wall /GR /EHsc\"",
                "buildCommandArgs": "-m -v:minimal"
            }
        ]
    }
    
  • 如果您将这个 CMakeSettings.json 文件与您的 CMake 项目一起提供,它将永久保存


对 CMake 编译器标志默认值的项目更改

如果您想保留 CMake 的大部分编译器标志,@sakra 的答案绝对是正确的选择。

对于我的 VS 项目,我已将 CXX 标志设置放入项目本身附带的工具链文件中。主要是为了冻结这些设置,并且不依赖于使用的 CMake 版本或任何环境变量设置。

以上面的例子为例:

VS2017Toolchain.cmake

set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /Wall /GR /EHsc" CACHE INTERNAL "")

CMakeSettings.json

    {
        // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
        "configurations": [
            {
                "name": "x86-Debug (all warnings)",
                "generator": "Visual Studio 15 2017",
                "configurationType": "Debug",
                "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
                "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=\"${projectDir}\\VS2017Toolchain.cmake\"",
                "buildCommandArgs": "-m -v:minimal"
            }
        ]
    }

参考文献

【讨论】:

  • 这些是一些不错的选择。设置CMAKE_TOOLCHAIN_FILE 变量特别吸引人,因为它将环境固定为固定值,允许将这些值置于源代码控制之下。尽管如此,这感觉有点像滥用工具链文件的用途。这在 CMake 构建其第一个测试项目之前很早就引入了 CMake 行为的更改(就像 CMAKE_USER_MAKE_RULES_OVERRIDE 变量一样)。我是否过度担心,或者这确实会引发问题? (我是 CMake 新手,我对 CMake 工作原理的心智模型很可能不正确。)
  • @IInspectable 我更喜欢工具链方法,因为它很好地分离了不同的编译器预设置,并使我的通用CMakeLists.txt 文件更具可读性。并且没有工具链不仅用于交叉编译,而且您也可以使用 -C 预加载缓存命令行选项(工具链的优势在于它们还可以传播到 CMake 可能即时生成的任何项目中)。并且从一开始就拥有这些标志的好处是,如果我弄错了编译器检查(配置步骤)将会失败。
【解决方案3】:

在 CMake 3.15 中,CMake 为这个 MSVC 特定警告引入了 fix

cl : Command line warning D9025: overriding '/W3' with '/W4'

不再自动添加编译器警告标志(如/W3)。因此,通过升级到 CMake 3.15 或更高版本,此警告不应再出现。来自文档:

在 CMake 3.14 及更低版本中使用类似 MSVC 的编译器时,/W3 等警告标志默认添加到 CMAKE_<LANG>_FLAGS。这对于想要以编程方式选择不同警告级别的项目来说是有问题的。特别是,它需要在了解 CMake 内置默认值的情况下对 CMAKE_<LANG>_FLAGS 变量进行字符串编辑,以便可以替换它们。

默认情况下,CMake 3.15 及更高版本更喜欢从 CMAKE_<LANG>_FLAGS 的值中省略警告标志。

与此修复一起,CMake 引入了策略 CMP0092,它允许您在必要时切换回 OLD 行为(默认添加警告标志)。

【讨论】:

    猜你喜欢
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 2019-10-31
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 1970-01-01
    相关资源
    最近更新 更多