I'm refactoring a CMakeLists.txt file in a project of mine, and I'm often in need of writing code like this:
if (<some_condition>)
set(var x)
else()
set(var y)
endif()
I find this a bit too verbose for my tastes, especially if repeated often, so I went searching for a one-liner in the spirit of C's ternary operator.
The macro here seems perfect, but apparently I have a conceptual misunderstanding of how macro expansion works, as I tried to use it a way that's (to me) self-evidently correct yet it gives bizarre results. Consider the following MRE:
cmake_minimum_required(VERSION 3.0)
project(macro_test)
macro(ternary var boolean value1 value2)
if(${boolean})
set(${var} ${value1})
else()
set(${var} ${value2})
endif()
endmacro()
foreach (x par1 par2)
ternary(my_var "${x} STREQUAL par1" 1 2)
message(STATUS "x = ${x} my_var = ${my_var}")
endforeach()
I would expect this to print x = par1 my_var = 1
followed by x = par2 my_var = 2
. Instead, I get:
-- x = par1 my_var = 2
-- x = par2 my_var = 2
It gets even weirder when I run this through cmake --trace-expand
:
/.../CMakeLists.txt(5): macro(ternary var boolean value1 value2 )
/.../CMakeLists.txt(13): foreach(x par1 par2 )
/.../CMakeLists.txt(14): ternary(my_var par1 STREQUAL par1 1 2 )
/.../CMakeLists.txt(6): if(par1 STREQUAL par1 )
/.../CMakeLists.txt(8): else()
/.../CMakeLists.txt(9): set(my_var 2 )
/.../CMakeLists.txt(15): message(STATUS x = par1 my_var = 2 )
-- x = par1 my_var = 2
/.../CMakeLists.txt(14): ternary(my_var par2 STREQUAL par1 1 2 )
/.../CMakeLists.txt(6): if(par2 STREQUAL par1 )
/.../CMakeLists.txt(8): else()
/.../CMakeLists.txt(9): set(my_var 2 )
/.../CMakeLists.txt(15): message(STATUS x = par2 my_var = 2 )
-- x = par2 my_var = 2
In particular, it makes no sense to me that if(par1 STREQUAL par1 )
would evaluate the else
clause.
Can anyone explain what I'm misunderstanding here, and also how to fix this so that it works as I would expect to (to recap: print x = par1 my_var = 1
followed by x = par2 my_var = 2
)?
EDIT: I've read the question linked as a possible duplicate and its answers, but it hasn't helped me solve my problem yet. I tried every variation I could think of (some suggested by @Tsyvarev), but none solved my problem, i.e., they still print my_var = 2
always, regardless of the value of x
. These are the variations I tried:
ternary(my_var "\"${x}\" STREQUAL par1" 1 2)
ternary(my_var "\"x\" STREQUAL par1" 1 2)
ternary(my_var "x STREQUAL par1" 1 2)
ternary(my_var "\"${x}\" STREQUAL \"par1\"" 1 2)
ternary(my_var "\"x\" STREQUAL \"par1\"" 1 2)
ternary(my_var "x STREQUAL \"par1\"" 1 2)
For each of these, I also tried to add cmake_policy(SET CMP0054 OLD)
to see whether it made any difference, and it didn't. I'm using CMake 3.26.3 so the default policy should be new (I changed the cmake_minimum_required
version to e.g. 3.18 to make sure of this).