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

时间:2015-02-26 00:08:26

标签: c++ cmake compiler-optimization

我想在CMake中更改CMAKE_CXX_FLAGS_RELEASECMAKE_CXX_FLAGS_DEBUG的默认值。基本上,我有一些项目默认值与CMake略有不同(例如发布),我不应该问自己"哦,他们的-O3或我们的-O2添加add_compile_options时优先。"

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

问题是CMAKE为这些设置并缓存了它们自己的默认值,如果你试图在不使用FORCE的情况下覆盖变量,那么"默认值为"永远不会改变。如果我在我的set命令set(CMAKE_CXX_FLAGS_DEBUG blah CACHE STRING "" FORCE)中使用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)

显然,这是一个讨厌的黑客,并没有完全奏效(在cmake -Dwhatever = thisorthat的情况下)。我也可以添加其他构建类型,但我真的不明白为什么只需改变一些简单的东西就可以了。

编辑2015年3月1日:

我已经创建了一个有效的解决方案,尽管我仍然不会对我必须做的事感到非常激动。我看到其他评论解决了设置CMAKE_CXX_FLAGS_DEBUG和朋友的问题而没有让他们受到破坏,但这最初对我没有用,因为我试图根据编译器选择它们正在使用中。但是,编译器尚未确定,直到它已经为我填充了变量。我使用的技巧如下。您必须将标志变量设置为' 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完全覆盖的默认值。

2 个答案:

答案 0 :(得分:9)

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

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

    <强> GNUToolchain.cmake

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

    然后将其与

    一起使用
    cmake -DCMAKE_TOOLCHAIN_FILE:string=GNUToolchain.cmake ...
    
  2. 您可以通过选中CMAKE_GENERATOR(在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值:

      

    在加载CMake的内置编译器和平台信息模块之后但在使用信息之前加载它。该文件可以设置平台信息变量以覆盖CMake的默认值。

    <强> MyInitFlags.cmake

    # Overwrite the init values choosen by CMake
    if (CMAKE_COMPILER_IS_GNUCXX)
        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. 您可以通过检查编译器标志变量的..._INIT变体来从3月1日开始简化您的解决方案:

    <强>的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_COMPILER_IS_GNUCXX)
            set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
        endif()
    endif()
    
  5. 评论

    我更喜欢并使用工具链变体。但我承认它的缺点是必须手动提供工具链文件(如果你没有通过脚本/批处理文件调用cmake)。

    参考

答案 1 :(得分:1)

弗洛里安使用工具链文件的答案对于较早版本的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)。如果有人尝试使用其他编译器(甚至是同一编译器的不同版本)来编译您的库,例如,如果您添加的警告标志太新或不被支持,则他们可能会遇到问题。您可以使用生成器表达式和/或复杂的逻辑(例如上面的_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 ()

或者您也可以将标志放在已经知道您正在使用哪种编译器的工具链/预设中。

相关问题