【问题标题】:How to extract parts of complex configuration with CONFIG generator expression in CMake如何在 CMake 中使用 CONFIG 生成器表达式提取复杂配置的一部分
【发布时间】:2025-12-25 10:20:26
【问题描述】:

在我们的项目中,我们有大量的配置,这些配置源自多种目标硬件类型,再加上几种模式。

为了避免不必要的细节,我们假设配置的格式为<hw>_<mode> are

  • <hw> 是以下之一:ABC
  • <mode> 是以下之一:123

此外,为了接近实际情况,我们假设A_3C_1 是不受支持的异常。 (但是,我认为这并不重要。)

这给我们留下了 3 x 3 - 2 = 7 支持的配置。


现在,我们希望根据配置进行设置(其中还有编译器和 sysroot 的路径)。此外,某些来源应仅包含在某些配置中。我们更愿意根据部分配置来做。

例如,我们想对所有A_* 配置使用/this/g++,对所有其他配置使用/that/g++。或者我们想为所有 *_2 配置而不是其他配置添加 mode2.cpp 文件。

如果我们使用CMAKE_BUILD_TYPE,这是一个简单的任务。我们可以用正则表达式 (string(REGEX MATCH) 拆分它,并且每个部分都有变量。然后简单的if 就可以了。

但是,这种方法对多配置生成器并不友好(目前似乎只有 Visual Studio 和 Xcode)。为了很好地使用多配置生成器,AFAIK,我们必须使用generator expressions


但问题是,我看不出有办法在生成器表达式中提取配置 (CONFIG) 的部分。

例如,我可以这样做:

add_executable(my_prog
    source_1.cpp
# ...
    source_n.cpp
    $<$<CONFIG:A_2>:mode2.cpp>
    $<$<CONFIG:B_2>:mode2.cpp>
    $<$<CONFIG:C_2>:mode2.cpp>
)

但考虑到我们迟早会添加新的硬件类型(或删除过时的硬件类型),这看起来不像是一种可维护的方法。

有没有办法在生成器表达式中进行某种形式的匹配?

到目前为止,我发现的唯一解决方法是使用这样的方法:

set(CONFIG_IS_MODE_2 $<OR:$<CONFIG:A_2>,$<CONFIG:B_2>,$<CONFIG:C_2>>)

add_executable(my_target
    source_1.cpp
# ...
    source_n.cpp
    $<${CONFIG_IS_MODE_2}:mode2.cpp>
)

这至少允许集中这些表达式,并且当添加新的硬件类型时,只有一个地方可以更新。但是,仍有许多变量需要更新。


有没有更好的解决方案?

【问题讨论】:

    标签: cmake


    【解决方案1】:

    使用target_sources() 命令和function(),您仍然可以使用正则表达式来匹配您的配置。

    这看起来像这个示例代码:

    cmake_minimum_required(VERSION 3.0)
    
    project(TestConfigRegEx)
    
    function(my_add_sources_by_config_regex _target _regex)
        foreach(_config IN LISTS CMAKE_CONFIGURATION_TYPES CMAKE_BUILD_TYPE)
            if (_config MATCHES "${_regex}")
                target_sources(${_target} PRIVATE $<$<CONFIG:${_config}>:${ARGN}>)
            endif()
        endforeach()
    endfunction()
    
    file(WRITE main.cpp "int main() { return 0; }")
    file(WRITE modeRelease.cpp "")
    
    add_executable(my_target main.cpp)
    
    my_add_sources_by_config_regex(my_target Release modeRelease.cpp)
    

    但这给了我来自 CMake 版本 3.11.1 Visual Studio 15 2017 生成器端的错误:

    Target "my_target" has source files which vary by configuration.  This is
    not supported by the "Visual Studio 15 2017" generator.
    
    Config "Debug":
    
      .../main.cpp
    
    Config "Release":
    
      .../main.cpp
      .../modeRelease.cpp
    

    很奇怪,它仍然会生成解决方案。


    替代品

    • 经典的方法是添加一个包含配置的定义,并使用 #if 检查处理 C/C++ 代码中的差异

    • 您区分的不是每个配置,而是其他目标(例如 my_targetmy_target_2

    【讨论】:

    • 这似乎是生成器中缺少的功能。 Visual Studio 本身在排除/包含源文件甚至整个项目方面没有问题,具体取决于配置。实际上,很高兴看到有一个文件,但它在当前选择的配置中是“非活动的”。
    • 我刚刚使用 VS 2017 和 CMake 3.9.3 进行了检查。确实生成项目会发出许多警告(不仅仅是一次......)。事实上,该项目仍然是生成的。它确实使用了我在上面评论中描述的“从构建中排除”属性!
    • 我已经为这个问题(“假错误”)提出了问题:gitlab.kitware.com/cmake/cmake/issues/18233.