Ternary operator in CMake's generator expressions

10,542

Solution 1

Note that cmake 3.8 added exactly what you want to generator expressions ...

$<IF:?,true-value...,false-value...>
true-value... if ? is 1, false-value... if ? is 0

Example usage:

target_link_libraries(MyLib PUBLIC
    $<IF:$<CONFIG:Debug>,cppzmq,cppzmq-static>
    )

Where cppzmq is shared library used in Debug build and cppzmq-static is static library used in other case e.g. Release

Solution 2

Here's a working example, with a macro:

cmake_minimum_required(VERSION 2.8.12)

macro(ternary var boolean value1 value2)
    set(${var} $<${${boolean}}:${value1}>$<$<NOT:${${boolean}}>:${value2}>)
endmacro()

set(mybool 0)
ternary(myvar mybool hello world)

add_custom_target(print
    ${CMAKE_COMMAND} -E echo ${myvar}
    )

Create a CMakeLists.txt file and run cmake . && make print (generator expressions are only evaluated at build time).

Try changing the value of mybool to 0 or 1 and see what happens.

The following definition also works, and it is clearer:

cmake_minimum_required(VERSION 2.8.12)

macro(ternary var boolean value1 value2)
    if(${boolean})
        set(${var} ${value1})
    else()
        set(${var} ${value2})
    endif()
endmacro()

set(mybool 0)
ternary(myvar mybool hello world)

add_custom_target(print
    ${CMAKE_COMMAND} -E echo ${myvar}
    )

TL;DR

ternary(var boolean value1 value2)

means, comparing to C/C++:

int var = boolean ? value1 : value2;

Share:
10,542
Daniel Wolf
Author by

Daniel Wolf

I'm a software developer in Munich, Germany. Professionally, I mostly do front-end development in TypeScript. In my spare time, I work on a tool for automatic lip-sync for computer games.

Updated on June 05, 2022

Comments

  • Daniel Wolf
    Daniel Wolf almost 2 years

    Cmake's generator expressions allow me to use logical expressions within certain function calls. For instance, if I want to add the /MTd compiler flag in Debug mode, I can say

    add_compile_options($<$<CONFIG:Debug>:/MTd>)
    

    If CONFIG equals "Debug", this will call add_compile_options with the value "/MTd", otherwise with an empty string.

    But usually, I don't want to decide between a value and the empty string, but between two values. In the example above, if CONFIG is not "Debug", I want to pass /MT (without the trailing d). I'd love to have a syntax like this:

    add_compile_options($<$<CONFIG:Debug>:/MTd:/MT>)
    

    Note that the above is not valid code according to the CMake specs. The best I have come up with that actually works is this:

    add_compile_options($<$<CONFIG:Debug>:/MTd>$<$<NOT:$<CONFIG:Debug>>:/MT>)
    

    This seems awfully redundant to me. Is there a shorter, more readable way to decide between two values?

    Note: I realize that in this special case, I could write this:

    add_compile_options(/MT$<$<CONFIG:Debug>:d>)
    

    But this seems rather hacky to me and only works in those cases where one option is a substring of the other.

    • Florian
      Florian over 8 years
      I don't think it's currently possible (as for CMake version 3.4.1), but there is already a feature request 0015585: Add support for the if / else construct to generator expressions you could support.
    • Antonio
      Antonio over 8 years
      For the moment being you could define a macro, and in fact limit your call to your_macro(Debug,/MTd,/MT)
    • thiagowfx
      thiagowfx over 7 years
      I provided a macro definition in my answer.
    • Florian
      Florian about 7 years
      The $<IF> operator was just added with this commit.
  • Florian
    Florian over 7 years
    Just a hint: I think neither the first (dereferencing the variable's content) nor the second definition (if checking the variables content) would work, if boolean contains a generator expression.
  • silvioprog
    silvioprog about 6 years
    @Florian you are right. I've tried to fix it, however, no success. But IMHO it would be nice to have some CMake ternary module or at least an IIF()-style function.
  • Daniel Wolf
    Daniel Wolf almost 5 years
    Nice find! It's documented here: cmake.org/cmake/help/v3.8/manual/…
  • vulcan raven
    vulcan raven about 4 years
    Thanks, but documentation does not give a single example for $<IF.. construct which was the reason of my confusion.
  • Lance E.T. Compte
    Lance E.T. Compte about 4 years
    @vulcanraven Indeed the cmake documentation is very short on helpful examples. Does the example that Dawid Drozd added help you enough?
  • vulcan raven
    vulcan raven about 4 years
    @LanceE.T.Compte, wasn't looking just for an example :) I saw a code somewhere of the form $<IF: $<cond>, trueValue>, without needing to specify false. I was searching for documentation on $<IF construct and couldn't find one. To me, $<IF sounds like a very fundamental thing to have as part of documentation in any platform.
  • Lance E.T. Compte
    Lance E.T. Compte about 4 years
    @vulcanraven $<IF ...> is documented here: cmake.org/cmake/help/latest/manual/… Omitting the falseValue would substitute an empty string. I actually do that when adding debug flags, etc.
  • vulcan raven
    vulcan raven about 4 years
    Exactly what I was trying to find. Thanks a lot! :)