【问题标题】:Variable reference in string does not get evaluated by "set"字符串中的变量引用不会被“set”评估
【发布时间】:2020-01-13 01:14:51
【问题描述】:

我想要的一样 How to load variables in a "bar=foo" syntax in CMake?

不同之处在于我的“值”条目包含对“键”(预期变量)的引用。

例如 BASEPATH 变量:

BASEPATH:=/home/SomePath
LIB_BASE?=$(BASEPATH)/lib/$(LIBREV)
ACCELMOD_BASE=$(BASEPATH)/SomeOtherPath

我已经修改了我的 CMakeLists.txt 脚本,将 [Key/value] 对从每一行提取到两个变量(请参见下面的代码)并最终得到类似的对(请注意,该值仍然包含对某个变量名称的引用通常在文件开头定义):

[BASEPATH, /home/SomePath], 
[LIB_BASE, ${BASEPATH}/lib/${LIBREV}],
[ACCELMOD_BASE, ${BASEPATH}/SomeOtherPath],

我写的代码如下:

# Part2
file(STRINGS revisions.mk ConfigContents)
foreach(NameAndValue ${ConfigContents})
    # Strip leading spaces
    string(REGEX REPLACE "^[ ]+" "" NameAndValue ${NameAndValue})
    #remove commented lines
    string(FIND ${NameAndValue} "#" commentIndex)
    if(${commentIndex} EQUAL 0)
            continue()
    endif()

    # Find variable name
    string(REGEX MATCH "^[^\\?:=]+" Name ${NameAndValue})
    # Find the value
    string(REGEX REPLACE "^(.+)=(.+)$" "\\2" Value ${NameAndValue})
    # Replace () with {} to denote a cmake's variable reference
    string(REGEX REPLACE "\\(([^)]+)\\)" "{\\1}" Value ${Value})

    # Set the variable
    set(${Name} ${Value})
    message("> Value of " ${Name} " : " ${${Name}})
endforeach()

我希望当我将键(名称)定义为变量(使用 set 命令)并将其值设置为键的对应值时,字符串中的引用将被替换与引用变量的当前值。

然而事实并非如此。 例如对于给定输入,循环结束前的 message 命令返回:

>Value of BASEPATH: /home/SomePath
>Value of LIB_BASE : ${BASEPATH}/lib/${LIBREV}
>Value of ACCELMOD_BASE: $(BASEPATH)/SomeOtherPath

即使已经定义了 BASEPATH。

为了验证我的预期,我编写了以下简单代码来模拟循环中的行为:

set(BASE 123)
set(RIGHT '${BASE}/SomePath/AA')
set(LEFT_STR "LEFT")
set(${LEFT_STR} ${RIGHT})
message(">" ${LEFT} "<>" ${${LEFT_STR}})

在这种情况下,${BASE} 引用被正确替换并且

'123/SomePath/AA'<>'123/SomePath/AA'

按预期返回。

我可能做错了什么?

【问题讨论】:

  • 您正在将 make 移植到 cmake?您可以make -f revisions.mk -f &lt;(echo -e 'print_%:\n\t@echo $($*)') print_BASEPATH 获取BASEPATH 的值。让make 处理扩展和所有极端情况。 set(RIGHT '${BASE}/SomePath/AA') - 该集合对"'123/SomePath/AA'" 是正确的。单引号在 cmake 中并不特别。你可以用set(RIGHT \${BASE}/SomePath/AA)模拟。

标签: cmake


【解决方案1】:

从 CMake 3.18 开始,CMake 中有 eval - cmake_language(EVAL CODE。见https://cmake.org/cmake/help/latest/command/cmake_language.html?highlight=eval

cmake_langauge(EVAL CODE "set(${Name} ${Value})")

cmake 中没有 eval。流行且容易出错的方法是创建一个脚本,然后包含它:

file(STRINGS revisions.mk ConfigContents)

set(varlist "")
foreach(NameAndValue ${ConfigContents})
    # Strip leading spaces
    string(REGEX REPLACE "^[ ]+" "" NameAndValue ${NameAndValue})
    #remove commented lines
    string(FIND ${NameAndValue} "#" commentIndex)
    if(${commentIndex} EQUAL 0)
            continue()
    endif()

    # Find variable name
    string(REGEX MATCH "^[^\\?:=]+" Name ${NameAndValue})
    # Find the value
    string(REGEX REPLACE "^(.+)=(.+)$" "\\2" Value ${NameAndValue})
    # Replace () with {} to denote a cmake's variable reference
    string(REGEX REPLACE "\\(([^)]+)\\)" "{\\1}" Value ${Value})

    # Set the variable
    set(${Name} ${Value})
    list(APPEND varlist ${Name})
endforeach()

set(script "")
foreach(i IN LISTS varlist)
        string(APPEND script "set(${i} \"${${i}}\")\n")
endforeach()
file(WRITE script2.cmake ${script})
include(script2.cmake)

foreach(i IN LISTS varlist)
        message(STATUS ${i}=${${i}})
endforeach()

这将创建脚本script2.cmake,其内容为:

set(BASEPATH "/home/SomePath")
set(LIB_BASE "${BASEPATH}/lib/${LIBREV}")
set(ACCELMOD_BASE "${BASEPATH}/SomeOtherPath")

然后includes 。包含此类脚本将重新评估表达式,从而解析引用。

【讨论】:

  • 这很麻烦,但它有效!。谢谢你的回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-31
  • 2011-03-30
  • 1970-01-01
  • 2016-08-28
  • 2022-01-19
相关资源
最近更新 更多