georanto
georanto

Reputation: 131

Variable reference in string does not get evaluated by "set"

I want the same as in How to load variables in a "bar=foo" syntax in CMake?

with the difference that my "value" entries contain references to the "keys" (prospective variables).

Eg the BASEPATH variable:

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

I have modified my CMakeLists.txt script to extract the [Key/value] pair from each line to two variables(see code below) and end up with pairs like (note that the value still contains a reference to some variable's name which is usually defined at the beginning of the file):

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

The code I have written is the following:

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

I expect that when I define a Key (Name) as a variable (using the set command) and set its value to the Value counterpart of the Key, the reference inside the string will be replaced with the current value of the referenced variable.

However this is not the case. e.g. for the give input, the message command before the end of the loop returns:

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

even though BASEPATH has already been defined.

To verify my expectation, I wrote the following simple code to simulate the behaviour in the loop:

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

and in this case the ${BASE} reference gets correctly replaced and

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

is returned as expected.

What could I been getting wrong?

Upvotes: 1

Views: 701

Answers (1)

KamilCuk
KamilCuk

Reputation: 140880

Since CMake 3.18 there is eval in CMake - cmake_language(EVAL CODE. See https://cmake.org/cmake/help/latest/command/cmake_language.html?highlight=eval .

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

There is no eval in cmake. The popular and error-prone way around it is to create a script and then include it:

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

This creates the script script2.cmake with the content:

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

and then includes it. Including such script would re-evaulate expressions thus resolve references.

Upvotes: 1

Related Questions