在 CMake 中更改 CMAKE_CXX_FLAGS_DEBUG 和朋友的默认值

Change default value of CMAKE_CXX_FLAGS_DEBUG and friends in CMake

我想在 CMake 中更改 CMAKE_CXX_FLAGS_RELEASECMAKE_CXX_FLAGS_DEBUG 的默认值。基本上,我有一些项目默认值与 CMake 的(例如发布)略有不同,我不应该问自己 "Oh, does their -O3 or our -O2 take precedence when added with add_compile_options."

现在,我知道如何设置这些值,但我不知道如何让用户可以通过两种常用方式编辑它们:通过在命令行上使用 -DCMAKE_CXX_FLAGS_DEBUG=yourflags 或通过配置它ccmake 或 CMakeSetup.

问题是 CMAKE 为这些设置和缓存了自己的默认值,如果您尝试在不使用 FORCE 的情况下覆盖变量,"defaults" 永远不会改变。如果我在我的设置命令中使用 FORCE:set(CMAKE_CXX_FLAGS_DEBUG blah CACHE STRING "" FORCE),它会在每次脚本为 运行 时覆盖它,从而消除了用户根据需要更改它的可能性。

我通过执行以下操作设法破解它以与 CCMAKE 一起工作,但这仍然不适用于 cmake -DCMAKE_CXX_FLAGS_DEBUG,因为它会在他完成后覆盖用户更改:

set(DEFAULTS_SET FALSE CACHE BOOL "")
set(CMAKE_CXX_FLAGS_DEBUG "-this -that" CACHE STRING "" FORCE)
set(DEFAULTS_SET TRUE CACHE BOOL "" FORCE)

显然,这是一个令人讨厌的 hack,并不完全有效(在 cmake -Dwhatever=thisorthat 的情况下)。我也可以添加其他构建类型,但我真的不明白为什么仅仅为了改变一些简单的事情就需要这样做。

编辑 2015 年 3 月 1 日:

我已经创建了一个有效的解决方案,但我仍然对我必须要做的事情感到不那么兴奋。我看到其他评论解决了设置 CMAKE_CXX_FLAGS_DEBUG 和朋友的问题而不让他们受到破坏,但这最初对我不起作用,因为我试图也 select 他们基于编译器那是在使用中。然而,编译器在它已经为我填充了变量之前是不确定的。我使用的技巧如下。您必须在项目命令之前将标志变量设置为 'special'。

set(CMAKE_CXX_FLAGS_DEBUG "_UNSET" CACHE STRING "")
project(your_project C CXX)

if(${CMAKE_CXX_FLAGS_DEBUG} STREQUAL "_UNSET")
    # Do some compiler switching here and then set your flags with FORCE.
    set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
endif()

这现在允许我选择默认值,这些默认值可以通过带有 -D 的命令行或在 cmake-gui 中完全覆盖。

我只是想添加我看到的四种可能性:

  1. 拥有自己的工具链文件,其中包含您支持的每个编译器的预设,例如:

    GNUToolchain.cmake

     set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "")
    

    然后与

    一起使用
     cmake -DCMAKE_TOOLCHAIN_FILE:string=GNUToolchain.cmake ...
    
  2. 你可以尝试通过检查CMAKE_GENERATOR (which is valid before the project()命令来确定编译器:

    CMakeLists.txt

     if("${CMAKE_GENERATOR}" MATCHES "Makefiles" OR 
        ("${CMAKE_GENERATOR}" MATCHES "Ninja" AND NOT WIN32))
         set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "")
     endif()
    
     project(your_project C CXX)
    
  3. 您可以使用 CMAKE_USER_MAKE_RULES_OVERRIDE 为脚本提供您自己的 ..._INIT 值:

    It is loaded after CMake’s builtin compiler and platform information modules have been loaded but before the information is used. The file may set platform information variables to override CMake’s defaults.

    MyInitFlags.cmake

     # Overwrite the init values choosen by CMake
     if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
         set(CMAKE_CXX_FLAGS_DEBUG_INIT "-ggdb3 -O0")
     endif()
    

    CMakeLists.txt

     set(CMAKE_USER_MAKE_RULES_OVERRIDE "MyInitFlags.cmake")
    
     project(your_project C CXX)
    
  4. 从 3 月 1 日起,您可以通过检查编译器标志变量的 ..._INIT 变体来简化您的解决方案:

    CMakeLists.txt

     project(your_project C CXX)
    
     if (DEFINED CMAKE_CXX_FLAGS_DEBUG_INIT AND  
         "${CMAKE_CXX_FLAGS_DEBUG_INIT}" STREQUAL "${CMAKE_CXX_FLAGS_DEBUG}")
         # Overwrite the init values choosen by CMake
         if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
             set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
         endif()
     endif()
    

评论:

我更喜欢并使用工具链变体。但我承认它的缺点是必须手动提供工具链文件(如果你不是通过 script/batch 文件调用 cmake)。

参考文献:

  • Switching between GCC and Clang/LLVM using CMake

Florian 使用工具链文件的答案对于早期版本的 CMake 来说是一个很好的答案。但是 CMake 3.19 添加了一个名为 presets 的功能,可帮助管理项目的通用缓存变量集。基本上,您至少创建两个文件之一,CMakePresets.jsonCMakeUserPresets.json(通常添加到 .gitignore 或类似文件),其中包含如何配置项目的规范。

例如,您可以这样写:

{
  "version": 1,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 19,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "default",
      "displayName": "Default",
      "description": "Build using Ninja and a GCC-like compiler",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build",
      "cacheVariables": {
        "CMAKE_CXX_FLAGS_DEBUG": "-ggdb3 -O0"
      }
    },
    {
      "name": "default-vcpkg",
      "displayName": "Default (vcpkg)",
      "description": "Default build with vcpkg (from VCPKG_ROOT)",
      "inherits": "default",
      "cacheVariables": {
        "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
      }
    }
  ]
}

然后,从源目录开始,您的 CMake 命令行将变为:

$ cmake --preset=default

这种方法有几个优点:

  1. 它使命令行很多更简单
  2. 它与其他工具链文件兼容(如第二个预设中的 vcpkg)
  3. 它可以覆盖通常无条件添加到 *_INIT 标志中的标志。
  4. 您不必在 CMakeLists.txt 中编写笨拙的逻辑。
  5. 用户可以选择加入预设,这在分发库时很重要。

扩展第 4 点和第 5 点:添加标志是个坏主意,除非它们绝对必须在那里才能正确编译没有用于到达这些标志的内置功能(例如 CMAKE_CXX_STANDARD)。如果有人试图用不同的编译器(甚至是同一编译器的不同版本)编译你的库,他们可能 运行 出现问题,例如,你添加了一个太新或不受支持的警告标志。您可以使用生成器表达式 and/or 复杂逻辑(如上面的 _UNDEF 技巧)来解决这个问题,但通常使用工具链或这些新预设更容易和更方便。

例如,要正确添加 -Wsuggest-override,您需要编写:

target_compile_options(lib PRIVATE $<$<AND:$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5.1>,$<COMPILE_LANG_AND_ID:CXX,GNU>>:-Wsuggest-override>)

# ... or ...

# Note: only correct if using "PRIVATE". Must use a genex for INTERFACE/PUBLIC because the whole genex gets exported, whereas this flag will get exported verbatim.
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 5.1)
  target_compile_options(lib PRIVATE -Wsuggest-override)
endif ()

或者您可以将标志放在 toolchain/preset 中,您已经知道您使用的是什么编译器。