在跨平台 cmake 项目中设置编译器标志的现代方法 [英] Modern way to set compiler flags in cross-platform cmake project

查看:17
本文介绍了在跨平台 cmake 项目中设置编译器标志的现代方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

if(MSVC)设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS}/std:c++latest/W4")# 默认调试标志没问题设置(CMAKE_CXX_FLAGS_RELEASE{CMAKE_CXX_FLAGS_RELEASE}/O2")别的()设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} -std=c++1z -Wall -Wextra -Werror")设置(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG} 一些其他标志")设置(CMAKE_CXX_FLAGS_RELEASE${CMAKE_CXX_FLAGS_RELEASE} -O3")if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} -stdlib=libc++")别的()# 目前对 gcc 没有什么特别的万一()万一()

但是我有几个问题:

  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,但如果这有什么不同,我对更新版本的答案更感兴趣.

解决方案

您的方法 - 正如@Tsyvarev 所评论的那样 - 绝对没问题,因为您在 CMake 中要求新"方法,这就是您的代码将转化为:

cmake_minimum_required(VERSION 3.8)项目(你好世界)细绳(APPEND _opts"$,""/W4;$<$:/O2>,"-Wall;-Wextra;-Werror;"$<$:-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)

<小时>

您使用

CMakeSettings.json

<代码>{//有关此文件的更多信息,请参阅 https://go.microsoft.com//fwlink//?linkid=834763.配置":[{"name": "x86-调试","generator": "Visual Studio 15 2017","configurationType": "调试","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}","buildCommandArgs": "-m -v:minimal",},{"name": "x86-Release","generator": "Visual Studio 15 2017","configurationType": "发布","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}","buildCommandArgs": "-m -v:minimal",},{"name": "Clang-调试","generator": "Visual Studio 15 2017","configurationType": "调试","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}","cmakeCommandArgs": "-T"LLVM-vs2014"","buildCommandArgs": "-m -v:minimal",},{"name": "Clang-Release","generator": "Visual Studio 15 2017","configurationType": "发布","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}","cmakeCommandArgs": "-T"LLVM-vs2014"","buildCommandArgs": "-m -v:minimal",},{"name": "GNU 调试","generator": "MinGW Makefiles","configurationType": "调试","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}",变量":[{"name": "CMAKE_MAKE_PROGRAM","value": "${projectDir}\mingw32-make.cmd"}]},{"name": "GNU 版本","generator": "Unix Makefiles","configurationType": "发布","buildRoot": "${env.LOCALAPPDATA}\CMakeBuild\${workspaceHash}\build\${name}",变量":[{"name": "CMAKE_MAKE_PROGRAM","value": "${projectDir}\mingw32-make.cmd"}]}]}

mingw32-make.cmd

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

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

I want to write a cmake file that sets different compiler options for clang++, g++ and MSVC in debug and release builds. What I'm doing currently looks something like this:

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

But I have a couple of problems with this:

  1. First the trivial: Is there relly no command like appen that would allow me to replace set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo") with append(CMAKE_CXX_FLAGS "Foo")?
  2. I've read multiple times, that one should not manually set CMAKE_CXX_FLAGS and similar variables in the first place, but im not sure what other mechanism to use.
  3. Most importantly: The way I do it here, I need a separate build directory for each compiler and configuration Ideally I'd like to transform that into havin multiple targets in the same directory so I can e.g. call make foo_debug_clang.

So my questions are

  • a) Is there a better way to write th cmake script that solves my "pain points"? solution to the points mentioned above?
  • b) Is there something like an accepted, modern best practice of how to set up such projects?

Most references I could find on the internet are either out of date or show only trivial examples. I currently using cmake3.8, but if that makes any difference, I'm even more interested in the answer for more recent versions.

解决方案

Your approach would - as @Tsyvarev has commented - be absolutely fine, just since you've asked for the "new" approach in CMake here is what your code would translate to:

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)


You take add_compile_options() and - as @Al.G. has commented - "use the dirty generator expressions".

There are some downsides of generator expressions:

  1. The very helpful $<IF:...,...,...> expression is only available in CMake version >= 3.8
  2. You have to write it in a single line. To avoid it I used the string(APPEND ...), which you can also use to "optimize" your set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ... calls.
  3. It's difficult to read and understand. E.g. the semicolons are needed to make it a list of compile options (otherwise CMake will quote it).

So better use a more readable and backward compatible approach with 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()


And yes, you don't explicitly specify the C++ standard anymore, you just name the C++ feature your code/target does depend on with target_compile_features() calls.

For this example I've chosen cxx_lambda_init_captures which would for e.g. an older GCC compiler give the following error (as an example what happens if a compiler does not support this feature):

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

"GNU"

version 4.8.4.


And you need to write a wrapper script to build multiple configurations with a "single configuration" makefile generator or use a "multi configuration" IDE as Visual Studio.

Here are the references to examples:

So I've tested the following with the Open Folder Visual Studio 2017 CMake support to combine in this example the , and compilers:

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

So you can use any CMake generator from within Visual Studio 2017, there is some unhealthy quoting going on (as for September 2017, maybe fixed later) that requires that mingw32-make.cmd intermediator (removing the quotes).

这篇关于在跨平台 cmake 项目中设置编译器标志的现代方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆