在跨平台 cmake 项目中设置编译器标志的现代方法

新手上路,请多包涵

我想编写一个 cmake 文件,在调试和发布版本中为 clang++、g++ 和 MSVC 设置不同的编译器选项。我目前正在做的事情看起来像这样:

 if(MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4")
    # Default debug flags are OK
    set(CMAKE_CXX_FLAGS_RELEASE "{CMAKE_CXX_FLAGS_RELEASE} /O2")
else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -Wall -Wextra -Werror")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} some other flags")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")

    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
    else()
        # nothing special for gcc at the moment
    endif()
endif()

但我有几个问题:

  1. 首先是微不足道的:真的没有像 appen 这样的命令可以让我用 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") append(CMAKE_CXX_FLAGS "Foo") 吗?
  2. 我已经读过很多次了,不应该首先手动设置 CMAKE_CXX_FLAGS 和类似的变量,但我不确定要使用什么其他机制。
  3. 最重要的是:我在这里做的方式,我需要为每个编译器和配置一个单独的构建目录理想情况下,我想将其转换为同一目录中的多个目标,这样我就可以调用 make foo_debug_clang

所以我的问题是

  • a) 有没有更好的方法来编写解决我的“痛点”的 cmake 脚本?解决上述几点?
  • b) 关于如何建立此类项目,是否存在类似公认的现代最佳实践?

我可以在互联网上找到的大多数参考资料要么已过时,要么仅显示琐碎的示例。我目前使用 cmake3.8,但如果这有什么不同,我对更新版本的答案更感兴趣。

原文由 MikeMB 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 962
2 个回答

您的方法 - 正如@Tsyvarev 评论的那样 - 绝对没问题,因为您在 CMake 中要求使用“新”方法,您的代码将转换为:

 cmake_minimum_required(VERSION 3.8)

project(HelloWorld)

string(
    APPEND _opts
    "$<IF:$<CXX_COMPILER_ID:MSVC>,"
        "/W4;$<$<CONFIG:RELEASE>:/O2>,"
        "-Wall;-Wextra;-Werror;"
            "$<$<CONFIG:RELEASE>:-O3>"
            "$<$<CXX_COMPILER_ID:Clang>:-stdlib=libc++>"
    ">"
)

add_compile_options("${_opts}")

add_executable(HelloWorld "main.cpp")

target_compile_features(HelloWorld PUBLIC cxx_lambda_init_captures)


你把 add_compile_options() 和 - 作为@Al.G.已评论 - “使用肮脏的 生成器表达式”。

生成器表达式有一些缺点:

  1. 非常有用的 $<IF:...,...,...> 表达式仅在 CMake 版本 >= 3.8 中可用
  2. 您必须将其写在一行中。为了避免这种情况,我使用了 string(APPEND ...) ,您也可以使用它来“优化”您的 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ... 调用。
  3. 这很难阅读和理解。例如,需要分号来使其成为编译选项列表(否则 CMake 将引用它)。

所以最好使用 add_compile_options() 的更具可读性和向后兼容的方法:

 if(MSVC)
    add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
else()
    add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
        add_compile_options("-stdlib=libc++")
    else()
        # nothing special for gcc at the moment
    endif()
endif()


是的,您不再明确指定 C++ 标准,您只需使用 target_compile_features() 调用命名您的代码/目标确实依赖的 C++ 功能

对于这个例子,我选择了 cxx_lambda_init_captures 例如,旧的 GCC 编译器会给出以下错误(例如,如果编译器不支持此功能会发生什么情况):

 The compiler feature "cxx_lambda_init_captures" is not known to CXX compiler

"GNU"

version 4.8.4.


您需要编写一个包装脚本来使用 “单一配置”makefile 生成器 构建多个配置,或者使用 “多配置”IDE 作为 Visual Studio。

以下是对示例的引用:

因此,我使用 Open Folder Visual Studio 2017 CMake 支持测试了以下内容,以在此示例中结合 clclangmingw 编译器:

配置

CMakeSettings.json

 {
    // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
    "configurations": [
        {
            "name": "x86-Debug",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "x86-Release",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "Clang-Debug",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "cmakeCommandArgs": "-T\"LLVM-vs2014\"",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "Clang-Release",
            "generator": "Visual Studio 15 2017",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "cmakeCommandArgs": "-T\"LLVM-vs2014\"",
            "buildCommandArgs": "-m -v:minimal",
        },
        {
            "name": "GNU-Debug",
            "generator": "MinGW Makefiles",
            "configurationType": "Debug",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "variables": [
                {
                    "name": "CMAKE_MAKE_PROGRAM",
                    "value": "${projectDir}\\mingw32-make.cmd"
                }
            ]
        },
        {
            "name": "GNU-Release",
            "generator": "Unix Makefiles",
            "configurationType": "Release",
            "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
            "variables": [
                {
                    "name": "CMAKE_MAKE_PROGRAM",
                    "value": "${projectDir}\\mingw32-make.cmd"
                }
            ]
        }
    ]
}

mingw32-make.cmd

 @echo off
mingw32-make.exe %~1 %~2 %~3 %~4

因此,您可以使用 Visual Studio 2017 中的任何 CMake 生成器,有一些不健康的引用正在进行(至于 2017 年 9 月,可能稍后修复),需要 mingw32-make.cmd 中介(删除引号)。

原文由 Florian 发布,翻译遵循 CC BY-SA 3.0 许可协议

您可以使用 target_compile_options() 来“附加”编译选项。

原文由 user8537712 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题