0

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

swineone
  • 2,296
  • 1
  • 18
  • 32
  • "it makes no sense to me that `if(par1 STREQUAL par1 )` would evaluate the `else` clause" - But this is exactly what you obtain would you enter this command manually, without intermediate macro. CMake automatically expands the first parameter to STREQUAL as a variable. You may either do not dereference x by yourself - `ternary(my_var "x STREQUAL par1" 1 2)` - so CMake will dereference it inside the macro. Or you may wrap the `${x}` into double quotes, so CMake won't dereference its result again. (In you case wrapping would require escaping: `ternary(my_var "\"${x}\" STREQUAL par1" 1 2)`. – Tsyvarev Apr 23 '23 at 14:39
  • @Tsyvarev thank you for your comment. However, I tried both of your suggestions and they made no difference: both still print `my_var = 2` whether `x = par1` or `x = par2`. I'm on CMake 3.26.3 under macOS Ventura, if that makes a difference. – swineone Apr 23 '23 at 15:03
  • Oh, a quoted **space** separated **string** - `"${x} STREQUAL par1"` - will never work as a **list** of arguments, which should be **semicolon-separated** instead. Actually, both `"${x};STREQUAL;par1"` and `x;STREQUAL;par1` work. See e.g. [this question](https://stackoverflow.com/questions/27733824/pass-multiple-list-to-cmake-without-a-splitting-keyword) or [that one](https://stackoverflow.com/questions/31633583/how-to-pass-a-list-variable-to-another-cmake-call). – Tsyvarev Apr 23 '23 at 16:54
  • @Tsyvarev thanks, that worked! If the question is reopened, please post this as an answer and I'll accept it. – swineone Apr 23 '23 at 17:30
  • I have modified list of duplicate questions. – Tsyvarev Apr 23 '23 at 17:36

0 Answers0