首页 > 解决方案 > 字符串中的变量引用不会被“set”评估

问题描述

我想要与 如何在 CMake 中以“bar = foo”语法加载变量相同?

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

例如 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命令)并将其值设置为键的对应值时,字符串中的引用将被引用变量的当前值替换。

然而,这种情况并非如此。例如对于给定输入,循环结束前的消息命令返回:

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

按预期返回。

我可能做错了什么?

标签: cmake

解决方案


从 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})")

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

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它。包括这样的脚本将重新评估表达式,从而解决引用。


推荐阅读