如何在CMake中使用CONFIG生成器表达式提取复杂配置的部分 [英] How to extract parts of complex configuration with CONFIG generator expression in CMake

查看:202
本文介绍了如何在CMake中使用CONFIG生成器表达式提取复杂配置的部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们的项目中,由于各种各样的目标硬件类型乘以几种模式而产生了大量配置。



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




  • < hw> 是以下之一: A B C

  • < mode> 是以下之一: 1 2 3



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



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






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



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



如果我们使用 CMAKE_BUILD_TYPE 。我们可以使用正则表达式( string(REGEX MATCH )并且每个部分都有变量,然后简单地 如果 可以完成工作。



但是,例如这种方法对多配置生成器并不友好(目前似乎只有Visual Studio和Xcode)。要与多配置生成器AFAIK完美配合,我们必须使用生成器表达式






但是,问题是,我看不到要在生成器表达式中提取配置( CONFIG )部分的方法。 / p>

例如,我可以这样做:

  add_executable(my_prog 
source_1.cpp
#...
s ource_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>

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






有没有更好的解决方案?

解决方案

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



在该示例代码中将类似于:

  cmake_minimum_required(VERSION 3.0)

项目(TestConfigRegEx)

函数(my_add_sources_by_config_regex _target _regex)
foreach(_config在列表中CMAKE_CONFIGURATION_TYPES CMAKE_BUILD_TYPE)
如果(_config MATCHES $ {_ regex )
target_sources($ {_ target} PRIVATE $< $< CONFIG:$ {_ config}>:$ {ARGN}>)
endif()
endforeach()
endfunction()

file(WRITE main.cpp int main(){返回0; })
文件(写模式Release.cpp)

add_executable(my_target main.cpp)

my_add_sources_by_config_regex(my_target发布模式Release.cpp)

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

 目标 my_target的源文件因配置而异,即
Visual Studio 15 2017生成器不支持。

Config调试:

... / main.cpp

Config发布:

... / main.cpp
... / modeRelease.cpp

足够奇怪,它仍然可以生成解决方案。






替代方案




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


  • 您不区分每个配置,而是附加配置目标(例如 my_target my_target_2



In our project, we have a large number of configurations stemming from a large variety of target hardware types multiplied by few modes.

To avoid unneeded details let's just assume that the configurations have form <hw>_<mode> were

  • <hw> is one of: A, B or C,
  • <mode> is one of: 1, 2 or 3.

Furthermore, to remain close to actual case let's assume that A_3 and C_1 are unsupported exceptions. (However, I don't think it matters here.)

Which leaves us with 3 x 3 - 2 = 7 supported configurations.


Now, we would like to make settings (amongst others also the path to compiler and sysroot) depend on the configuration. Also, some sources should be included only in some configurations. And we would prefer to do it based on parts of the configuration.

For example, we would like to use /this/g++ for all A_* configurations and /that/g++ for all other. Or we would like to add mode2.cpp file for all *_2 configurations but not others.

It is a simple task if we use CMAKE_BUILD_TYPE. We can split it with regex (string(REGEX MATCH) and have variables with each part. Then simple if does the job.

However, such approach is not friendly with multi-config generators (it seems currently those are only Visual Studio and Xcode). To play nicely with multi-config generators, AFAIK, we would have to use generator expressions.


The problem is, however, that I see no way to extract parts for the configuration (CONFIG) in the generator expressions.

For example, I can do this:

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>
)

but this doesn't look like a maintainable approach considering that sooner or later we will be adding new hardware types (or removing obsolete ones).

Is there any way to do some form of matching in generator expression?

The only workaround I found out so far is to use an approach like this:

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>
)

which at least allows centralizing those expressions and when new hardware type is added there is a single place to update. However, still, there are many variables to update.


Is there any better solution?

解决方案

With target_sources() command and a function() you could still use a regex to match your configurations.

This would look something like in this example code:

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)

But that gives me an error from CMake version 3.11.1 Visual Studio 15 2017 generator side:

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

Strange enough it still generates the solution.


Alternatives

  • The classic one would be adding a define containing the configuration and handle the differences in the C/C++ code with #if checks

  • You differentiate not per configuration but with additional targets (like my_target and my_target_2)

这篇关于如何在CMake中使用CONFIG生成器表达式提取复杂配置的部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆