Cmake string 从包含以冒号分隔的键值的字符串列表中获取键值对

Cmake string to get key value pairs from a string list containing key values separated by colon

我有一个字符串列表(输入):

set(MY_LIST "A:1;B:2;C:3")

我想使用 foreach 获取键值并将它们设置为 cmake 常量。类似于:

foreach(ITEM ${MY_LIST})
   SET(<ITEM_A> <value_ofA>)
endforeach()

所以基本上我想要最终结果,但这应该使用 forloop 来实现:

SET(A "1")
SET(B "2")
SET(C "3")

如何使用 foreach 导航列表中的每个字符串并将键值对设置为 cmake 常量来实现此目的?

还不错:

cmake_minimum_required(VERSION 3.21)

set(MY_LIST "A:1;B:2;C:3;D:foo:bar")
foreach (pair IN LISTS MY_LIST)
  string(FIND "${pair}" ":" pos)
  if (pos LESS 1)
    message(WARNING "Skipping malformed pair (no var name): ${pair}")
  else ()
    string(SUBSTRING "${pair}" 0 "${pos}" var)
    math(EXPR pos "${pos} + 1")  # Skip the separator
    string(SUBSTRING "${pair}" "${pos}" -1 val)
    set("${var}" "${val}")
  endif ()
endforeach ()

message(STATUS "${A}")
message(STATUS "${B}")
message(STATUS "${C}")
message(STATUS "${D}")

输出为:

$ cmake -P test.cmake
-- 1
-- 2
-- 3
-- foo:bar

也可能适用于早期版本。我会及时更新。

只需使用正则表达式来拆分值。下面的代码将功能包装在一个函数中。除了创建这些变量的列表之外,它还提供添加前缀以避免保留变量名称出现问题的功能。 (对于省略了额外逻辑的简化版本,请参阅我的回答的最后一个代码片段。)

function(my_parse_values OUT_PREFIX)
    set(VAR_LIST)
    foreach(_PAIR IN LISTS ARGN)
        if (_PAIR MATCHES "^([^:]+):(.*)$")
            set("${OUT_PREFIX}${CMAKE_MATCH_1}" ${CMAKE_MATCH_2} PARENT_SCOPE)
            list(APPEND VAR_LIST ${CMAKE_MATCH_1})
        else()
            message(FATAL_ERROR "Invalid pair: ${_PAIR}")
        endif()
    endforeach()
    list(REMOVE_DUPLICATES VAR_LIST)
    set(${OUT_PREFIX} ${VAR_LIST} PARENT_SCOPE)
endfunction()

使用示例:

set(MY_LIST "A:1;B:2;C:3")
my_parse_values(VARS ${MY_LIST})

# VARS_A with value 1,
# VARS_B with value 2,
# VARS_C with value 3 and
# list VARS with elements A, B and C
# are defined now...

foreach(_VAR IN LISTS VARS)
    message("${_VAR} = ${VARS_${_VAR}}")
endforeach()

如果您不需要变量列表,并且想按原样设置变量,代码可以缩短为:

function(my_parse_values)
    foreach(_PAIR IN LISTS ARGN)
        if (_PAIR MATCHES "^([^:]+):(.*)$")
            set(${CMAKE_MATCH_1} ${CMAKE_MATCH_2} PARENT_SCOPE)
        else()
            message(FATAL_ERROR "Invalid pair: ${_PAIR}")
        endif()
    endforeach()
endfunction()

set(MY_LIST "A:1;B:2;C:3")
my_parse_values(${MY_LIST})

message("A = ${A}")
message("B = ${B}")
message("C = ${C}")

我多次对在 CMake 中迭代多个数组元素感到沮丧。所以我创建了一个函数。以下相当长的文件可从 here 获得:

function(foreach_count_items fci_foreach_count_items_loopvar_name
         fci_foreach_count_items_statevar_name)
  if("${ARGC}" LESS 1)
    message(FATAL_ERROR "foreach_count_items: wrong number of arguments")
  endif()

  set(fci_foreach_count_items_statevar
      "${${fci_foreach_count_items_statevar_name}}")
  set(fci_foreach_count_items_loopvar
      "${${fci_foreach_count_items_loopvar_name}}")
  # message(STATUS "fci_foreach_count_items_loopvar=${fci_foreach_count_items_loopvar}")
  # message(STATUS "fci_foreach_count_items_statevar=${fci_foreach_count_items_statevar}")

  if("${fci_foreach_count_items_statevar}" STREQUAL "")
    set(fci_foreach_count_items_statevar 1)
  endif()

  math(EXPR count "${ARGC} - 2")
  math(EXPR argc_less_one "${ARGC} - 1")
  math(EXPR argc_less_two "${ARGC} - 2")

  math(EXPR lastidx "${ARGC} - 1")
  # message(STATUS "set(${ARGV${lastidx}} ${fci_foreach_count_items_loopvar}
  # PARENT_SCOPE)")
  set("${ARGV${lastidx}}" "${fci_foreach_count_items_loopvar}")
  set("${ARGV${lastidx}}"
      "${fci_foreach_count_items_loopvar}"
      PARENT_SCOPE)
  # message(STATUS "lastidx=${lastidx}") message(STATUS
  # "ARGV${lastidx}=${ARGV${lastidx}}=${${ARGV${lastidx}}}")

  if(fci_foreach_count_items_statevar LESS count)
    foreach(i RANGE 2 ${argc_less_two} 1)
      math(EXPR j "${i} + 1")
      # message(STATUS "${i} ${j} | ${ARGV${i}}=${${ARGV${i}}}
      # ${ARGV${j}}=${${ARGV${j}}}")
      set("${ARGV${i}}" "${${ARGV${j}}}")
      set("${ARGV${i}}" "${${ARGV${j}}}" PARENT_SCOPE)
    endforeach()

    # foreach(i RANGE ${argc_less_one}) message(STATUS "ARGV${i} = ${ARGV${i}} =
    # ${${ARGV${i}}}") ndforeach()

    math(EXPR fci_foreach_count_items_statevar
         "${fci_foreach_count_items_statevar} + 1")
    set(fci_foreach_count_items_loopvar YES)
  else()
    set(fci_foreach_count_items_statevar 1)
    set(fci_foreach_count_items_loopvar NO)
  endif()

  set("${fci_foreach_count_items_statevar_name}" "${fci_foreach_count_items_statevar}" PARENT_SCOPE)
  set("${fci_foreach_count_items_loopvar_name}" "${fci_foreach_count_items_loopvar}" PARENT_SCOPE)
endfunction()

允许像这样使用它:

set(mylist "A:1;B:2;C:3")
foreach(ii IN LISTS mylist)
   foreach_count_items(tmp item value)
   #                       ^^^^^^^^^^  - variable list
   #                   ^^^             - temporary iterator
   if(tmp)  # when iterator is set, ignore that loop, wait for the next one
     continue()
   enidf()

   # use item and value here
   set(${item} ${value})
endforeach()