Reputation: 131
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
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 include
s it. Including such script would re-evaulate expressions thus resolve references.
Upvotes: 1