【问题标题】:Where to set CMAKE_CONFIGURATION_TYPES in a project with subprojects在带有子项目的项目中设置 CMAKE_CONFIGURATION_TYPES 的位置
【发布时间】:2015-10-11 08:00:26
【问题描述】:

假设我有一个包含两个独立子项目的项目。如果我对 cmake 的理解正确,想法是让一个根 CMakeLists.txt 定义一个 project(...),然后使用 add_subdirectory(...) 包含子项目。每个子项目都有自己的CMakeLists.txt 定义自己的项目。这种方式可以一起构建项目(使用根 cmake 文件)或单独构建(使用子项目 cmake 文件)。

我现在想更改CMAKE_CONFIGURATION_TYPES。我应该在根 CMakeLists.txt 还是在每个子项目中执行此操作,还是两者都执行?

在根目录中更改它意味着单独构建子项目会提供错误的配置类型;其他选项将复制 cmake 代码。我想我在这里遗漏了一些东西。

【问题讨论】:

  • 为什么要更改CMAKE_CONFIGURATION_TYPES,你想怎么修改?
  • 我想要 Release、Debug 和 Profile,而不是 Release、Debug 和 RelWithDebugInfo。
  • 我目前这样做:set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile") set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "List of supported configurations.") mark_as_advanced(CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE) message("Defaulting to release build.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: ${CMAKE_CONFIGURATION_TYPES}." FORCE) endif()
  • 那么你真的需要在根项目和子项目中做同样的事情。您唯一能做的就是将配置机器分解为从每个 CMakeLists.txt 调用的外部宏
  • 谢谢。两次定义事物会不会有任何问题,即我是否必须注意不要覆盖事物?你想把你的评论变成答案吗?

标签: cmake subproject


【解决方案1】:

分解设置依赖于配置的设置的代码。使用以下内容创建一个文件,例如 SetUpConfigurations.cmake

if(NOT SET_UP_CONFIGURATIONS_DONE)
    set(SET_UP_CONFIGURATIONS_DONE TRUE)

    # No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator
    # Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator.
    get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
    if(isMultiConfig)
        set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile" CACHE STRING "" FORCE) 
    else()
        if(NOT CMAKE_BUILD_TYPE)
            message("Defaulting to release build.")
            set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
        endif()
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
        # set the valid options for cmake-gui drop-down list
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;Profile")
    endif()
    # now set up the Profile configuration
    set(CMAKE_C_FLAGS_PROFILE "...")
    set(CMAKE_CXX_FLAGS_PROFILE "...")
    set(CMAKE_EXE_LINKER_FLAGS_PROFILE "...")
endif()

然后include(..)这个文件在CMakeLists.txt的开头。

关于将SetUpConfigurations.cmake 放在哪里,您有两种选择,这取决于您如何组织项目、存储库:

  1. 快速的'n'dirty 方法:复制并提交此脚本到每个需要它的项目中。它的位置将是固定的,相对于项目的CMakeLists.txt。所以你可以包含它,例如,include(${CMAKE_CURRENT_SOURCE_DIR}/<...>/SetUpConfigurations.cmake)

  2. 规范的方式:使用您的自定义 CMake 脚本维护一个存储库,例如这个。每次使用cmake 命令生成项目时,都会在CMAKE_MODULE_PATH 变量中传递此存储库的路径:

    cmake -DCMAKE_MODULE_PATH=<dir-of-cmake-script-repo> ...
    

在这种情况下,包括带有include(SetUpConfigurations) 的脚本(没有.cmake 扩展名)。

关于什么是多配置生成器的说明:

XcodeVisual Studio 是多配置生成器。他们尊重CMAKE_CONFIGURATION_TYPES 的值,但CMAKE_BUILD_TYPE 没有任何效果,因为在处理CMakeLists.txt 时没有定义具体配置。稍后将在 IDE 的用户界面上选择它。

另一方面,makefile 风格的生成器对CMAKE_CONFIGURATION_TYPES 不感兴趣。 CMAKE_BUILD_TYPE 定义配置。当CMakeLists.txt文件被处理但仍然是一个具体的值:永远不要根据CMAKE_BUILD_TYPE的值做任何决定:

if(CMAKE_BUILD_TYPE STREQUAL "Release") # WRONG!
    ....
endif()

您的项目无法在多配置生成器中按预期工作。

【讨论】:

  • 我还有一个后续问题。你对include(..)说每个CMakeLists.txt开头的文件。这是否意味着我必须使用像${CMAKE_CURRENT_SOURCE_DIR}/../SetUpConfigurations.cmake 这样的相对包含?这意味着我不能只是将我的子目录复制到某个地方并在副本上调用 cmake。这不是一个真正的问题,我想除了复制每个子目录中的文件之外没有办法避免它,但为了确定,我想问一下。
  • @Flogo,我对此提出了建议并扩展了答案
  • 谢谢,我可以在 Visual Studio 上补充一点,我正在使用此代码重新定义 CMAKE_CXX_FLAGS_ 以包含 /MP 编译时间开关,以将编译提升到所有可用的 CPU 内核
  • 这是一个很好的答案,可以优雅地处理单配置和多配置生成器。
  • 另外,根据 cmake 共同维护者 Craig Scott 的帖子 (gitlab.kitware.com/cmake/cmake/issues/17645#note_367457),GENERATOR_IS_MULTI_CONFIG 属性应该用于检查 multiconfig,而不是检查 CMAKE_CONFIGURATION_TYPES 变量。我现在将编辑答案以包含他的建议。
【解决方案2】:

在子项目目录中使用add_subdirectory 时,您会将几乎所有变量传播到该子项目中,这与“子项目独立性”相矛盾。

相反,最好在execute_process() 中使用嵌套的cmake 调用来构建和安装子项目。如果您想让某些子项目的定义可用于顶级项目,则需要在安装子项目时“导出”这些定义。 This question/answer 帖子描述了如何做到这一点。

【讨论】:

  • 这对我来说似乎是一种解决方法。你有一个使用它的项目的链接吗?也许我问题中的措辞也具有误导性。子项目是独立的,因为它们不共享代码并且不相互链接。但是编译和使用它们仍然是常见的情况。我也不介意向下传播东西,事实上,我向下传播CMAKE_CONFIGURATION_TYPES。最好不必指定两次。
  • @Tsyvarev:嵌套的 cmake 调用是好事。但是在非常常见的情况下,add_subdirectory 是一个独立项目的方式。示例:想象一个包含许多内部库和应用程序存储库的应用程序套件。每个 repo 都是在其独立项目中开发的。然后将这些项目作为子目录放入 app-suite 超级项目中,以便能够在一个 IDE 解决方案中编辑和构建它们的源文件。
  • @Flogo:例如,查看那个 cmake 文件:github.com/euspectre/kedr/blob/master/sources/CMakeLists.txtelse (KEDR_GEN) 下的分支安装子项目kedr_gen。如果您希望子项目共享一些代码,请将此代码放在共享的.cmake 文件中,并将include() 放在子项目中。 @tamas.kenez:是的,在某些情况下,合并子项目比独立更重要。
  • @Tsyvarev:谢谢你的例子。不过,我仍然不相信。 cmake 文件调用像 mkdirrmmake 这样的命令,这似乎违背了 cmake 独立于平台的目的。正如 tamas.kenez 指出的那样,我之前也没有意识到嵌套 cmake 调用意味着您将不再将所有项目都放在一个 IDE 解决方案中。就我而言,我更喜欢将项目放在一个解决方案中。对我来说,所有项目都在同一个仓库中,但它们是独立的应用程序,类似于应用程序套件。
猜你喜欢
  • 1970-01-01
  • 2020-06-08
  • 2013-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 2022-01-27
相关资源
最近更新 更多