如何复制 CMake 目标?

5

我正在为我的cmake项目编写一个函数,需要从一个目标生成两个目标,并稍微修改其中一个:

option(BORG_STRIP_TEST_BINARIES OFF "Strip symbols from test binaries to reduce disk space usage" )
function(add_borg_test target)
    add_test(${target} ${target} --gtest_color=yes)
    if(BORG_STRIP_TEST_BINARIES)
        # copy target, but make it optional
        duplicate_target(FROM ${target} TO ${target}_debug )
        set_target_properties(${target}_debug PROPERTIES EXCLUDE_FROM_ALL TRUE)
        # alter
        target_link_options(${target} PRIVATE -s)
    endif()
endfunction()

所以它应该像这样工作:

  1. 我创建了一个二进制目标,它使用gtest。设置所有 target_link_libraries 和其他内容。示例名称为 example-utests
  2. 不再添加通用的 add_test,而是使用自定义的 add_borg_test
  3. 现在 example-utests 链接带有标志 -s 并包含在 all 目标中。
  4. example-utests_debug 不包括在 all 目标中,但在显式请求时链接时不带有 -s

如何根据上面的代码片段实现 duplicate_target


1
我不知道在 CMake 中复制目标的方法。你需要从头开始创建新的目标。要创建具有相似属性的两个目标,你可以创建一个宏或函数,用于创建单个目标并填充其公共属性。你可以调用这个宏两次,来创建两个目标。然后分别调整那些不同的目标的属性。 - undefined
作为对Tsyvarev提到的替代方案,您还可以使用foreach循环,例如foreach(_TARGET_NAME IN ITEMS target_foo target_bar) ...并在循环结束后分配/覆盖任何一个目标独有的属性。哪种方法最适合取决于目标之间的差异。 - undefined
1个回答

0
我的第一反应(抱歉)是质疑为什么有人想要这样做。我不是你,也不知道你的背景,所以请你自问:你确定你不能通过生成多个构建系统并为每个系统指定单独的配置选项来实现你想要的吗?毕竟,CMake的整个设计就是为了使生成构建系统变得容易。
说到这一点,如果你确信有充分的理由想要在同一构建系统中复制/克隆/复制目标定义,那么可以按照Brad King(CMake维护者之一)在这个邮件线程中的建议进行操作:

虽然没有内置的方法来实现这一点,但你可以尝试使用一个宏,从原始目标中读取目标属性,以便使用适当的命令进行复制。

使用如何获取目标的所有属性可能会有所帮助。请注意,我在那里列出了一些重要的限制。我所知道的最好的解决方案只允许读取内置到CMake的目标属性,而不允许读取你或任何其他CMake配置代码自定义的属性。因此,这个解决方案也会受到相同的限制,大致如下:

execute_process(COMMAND "${CMAKE_COMMAND}" "--help-property-list" "${CMAKE_BINARY_DIR}/--help-property-list.txt")
file(STRINGS "${CMAKE_BINARY_DIR}/--help-property-list.txt" property_list)
function(copy_target_props src_target dest_target)
  set(config_types "${CMAKE_CONFIGURATION_TYPES}")
  if(NOT DEFINED CMAKE_CONFIGURATION_TYPES)
    set(config_types "Release;Debug;RelWithDebInfo;MinSizeRel")
  endif()
  foreach(prop_name ${property_list})
    if("${prop_name}" MATCHES "(^LOCATION)|^VS_DEPLOYMENT_LOCATION$|^MACOSX_PACKAGE_LOCATION$|^CXX_MODULE_SETS$|^HEADER_SETS$|^IMPORTED_GLOBAL$|^INTERFACE_CXX_MODULE_SETS$|^INTERFACE_HEADER_SETS$|^NAME$|^TYPE$")
      continue()
    endif()
    if("${prop_name}" MATCHES "<CONFIG>")
      foreach(config ${config_types})
        string(REPLACE "<CONFIG>" "${config}" config_prop_name "${prop_name}")
        get_target_property(prop_val "${src_target}" "${config_prop_name}")
        if(NOT "${prop_val}" STREQUAL "prop_val-NOTFOUND")
          #message("${config_prop_name}: ${prop_val}")
          set_property(TARGET "${dest_target}" PROPERTY "${config_prop_name}" "${prop_val}")
        endif()
      endforeach()
    else()
      get_target_property(prop_val "${src_target}" "${prop_name}")
      if(NOT "${prop_val}" STREQUAL "prop_val-NOTFOUND")
        #message("${prop_name}: ${prop_val}")
        set_property(TARGET "${dest_target}" PROPERTY "${prop_name}" "${prop_val}")
      endif()
    endif()
  endforeach()
  set(prop_name "IMPORTED_GLOBAL")
  get_target_property(prop_val "${src_target}" "${prop_name}")
  if((NOT "${prop_val}" STREQUAL "prop_val-NOTFOUND") AND "${prop_val}")
    #message("${config_prop_name}: ${prop_val}")
    set_property(TARGET "${dest_target}" PROPERTY "${prop_name}" "${prop_val}")
  endif()
endfunction()

# example usage
add_executable(foo1 foo.c)
add_executable(foo2)
copy_target_props(foo1 foo2)

我太懒了,不想写一个真正的复制函数,你不需要手动定义第二个目标。但是你可以通过读取源目标的目标类型属性,然后在所有的add_executable/add_library变体上进行if-else判断(在首次定义目标后,无法更改目标类型)。
有另一种技术,适用性有限,不是用于复制目标,而是用于使常见目标属性设置可重用(同样,适用性有限):在接口库上定义接口属性,然后将多个相似目标链接到接口目标,以便从接口目标继承值。
最后一点:我有一个项目,我在其中使用CMake来驱动PGO的两个阶段——训练和优化构建。我将项目本身定义为ExternalProject,并为训练和使用阶段定义了两个自定义构建配置,这两个阶段需要不同的编译标志。这就是说,你采取哪种方法的适用性可能取决于上下文。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接