如何在CMake

时间:2018-05-24 09:27:14

标签: cmake

在我们的项目中,我们有大量的配置源于各种目标硬件类型乘以少数模式。

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

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

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

这使我们得到3 x 3 - 2 = 7支持的配置。

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

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

如果我们使用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>
)

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

有没有更好的解决方案?

1 个答案:

答案 0 :(得分: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