2017-08-30 127 views
10

我想编写一个cmake文件,它在debug和release版本中为clang ++,g ++和MSVC设置不同的编译器选项。 我在做什么目前看起来是这样的:在跨平台cmake项目中设置编译器标志的现代方法

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. 首先简单:是否有relly没有命令澳鹏一样,让我用append(CMAKE_CXX_FLAGS "Foo")替换set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo")
  2. 我已经多次阅读,不应该手动设置CMAKE_CXX_FLAGS和类似的变量,但我不知道使用什么其他机制。
  3. 最重要的是:我在这里做的方式,我需要为每个编译器和配置一个单独的构建目录理想情况下,我想要将它转换为同一目录中的多个目标,所以我可以致电make foo_debug_clang

所以我的问题是

  • 一)有没有更好的方式来写个,解决了我的“痛点” cmake的脚本? 解决上述问题?
  • b)有没有像如何建立这样的项目的公认的现代最佳实践?

我可以在互联网上找到的大多数参考文献都是过时的或只显示微不足道的例子。我目前使用cmake3.8,但如果这有什么不同,我更感兴趣的是更新版本的答案。

+0

关于前两点:https://cmake.org/cmake/help/v3.3/command/add_compile_options.html –

+0

@ AI.G。谢谢。是否还有单独的调试和释放集,我必须通过另一级别的if_else解决?我用生成器表达式进行了一些尝试,但我感觉这变得更不可读。 – MikeMB

+0

当我看到它,你既可以用脏发电机表达式或'设置(CMAKE_CXX_FLAGS_DEBUG“$ {} CMAKE_CXX_FLAGS_DEBUG --flags”)'相当详细的语法。在这种情况下(考虑到这些不满意),你可以尝试编写一些帮助函数,将脏代码包装成更令人愉快的东西。 –

回答

6

你的办法将 - 如@Tsyvarev曾评论 - 绝对很好,只是因为你已经要求在C进行“新”办法这里是你的代码会翻译什么:

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()和 - as @ Al.G。已评论 - “使用脏generator expressions”。

有发电机表达式的一些缺点:

  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++标准了,你刚才命名C++ feature代码/目标确实取决于target_compile_features()调用。

在这个例子中,我选择了cxx_lambda_init_captures,旧的GCC编译器提供以下错误(作为一个例子会发生什么,如果编译器不支持此功能):

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

"GNU" 

version 4.8.4. 

而且你需要写一个包装脚本构建与"single configuration" makefile generator或多个配置作为Visual Studio使用"multi configuration" IDE

下面是引用的例子:

所以,我测试了Open Folder Visual Studio中的下列2017年CMake的支持,在结合此示例为,编译器:

Configurations

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 

所以,你可以使用任何CMake的发电机从Visual Studio 2017年中,还有一些不健康的报价正在进行(截至2017年9月,mayb e稍后修复),即需要mingw32-make.cmd中介(删除引号)。

+0

我严重违反规定个人标准语言功能的要求,而不是指定我编码反对的标准,但在其他方面感谢全面的答案。 – MikeMB

+0

@MikeMB不客气。你也可以对'target_compile_features(cxx_std_17)'使用一个通用声明。 – Florian

+0

我不是结果完全满意,但就是definetively更多的cmake的错比你的。顺便说一句.:指定'cxx_std_XX'比指定每个功能越好,但AFAIK cmake的可解决此到'-std = GNUC++ XX'开关代替'C++ XX'which是跨平台的发展IMHO问题。通常我尽量把所有的编译器尽可能接近标准的一致性越好('-std = C++ XX,/ permissive-,-pedantic'等),然后回退,如果某些第三方库不会在他们编译,但这超出了问题的范围。 – MikeMB

1

另一种方法是使用.rsp文件。

set(rsp_file "${CMAKE_CURRENT_BINARY_DIR}/my.rsp") 
configure_file(my.rsp.in ${rsp_file} @ONLY) 
target_compile_options(mytarget PUBLIC "@${rsp_file}") 

这可能会使包含多个和深奥的选项更容易管理。

+0

@Tsyvarev有趣,你是对的。将更新。 –

+0

我不得不承认,我从来没有听说过rsp_files - 你能举个例子吗?无论如何,我不确定将现在在单个位置的信息拆分为多个单独的文件会使过程变得更好。 – MikeMB

+0

@MikeMB rsp文件(响应文件)只是将命令行选项放入文件而非命令行的一种方法。我能想到的所有支持他们的编译器 –

1

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

1

寻址所述第一两分,而不是第三:

  • 我已经多次读取,即一个不应该手动在所述第一设定CMAKE_CXX_FLAGS和类似的变量地方,但我不知道还有什么其他机制使用。
  • 你想要的命令是set_property。 CMake支持一系列属性 - 不是一切,而是大量 - 以某种方式为您节省了编译器特定工作的麻烦。例如:

    set_property(TARGET foo PROPERTY CXX_STANDARD 17) 
    

    这对于一些编译器将导致--std=c++17但对于早期的具有--std=c++1z(前C++ 17定稿)。或:

    set_property(TARGET foo APPEND PROPERTY COMPILE_DEFINITIONS HELLO WORLD) 
    

    将导致它-DHELLO-DWORLD对于GCC,铛和MSVC但奇怪的编译器可能会使用其他交换机。

    是否有relly像追加没有命令,让我与append(CMAKE_CXX_FLAGS "Foo")?

    set_property不仅可以被用来在设定的模式,或以附加模式替换set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} Foo")(参见上文实施例)。

    我不能说这是否是最好add_compile_optionstarget_compile_features,虽然。

    相关问题