34

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.

Daniel Wolf
  • 12,855
  • 13
  • 54
  • 80
  • 4
    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](https://cmake.org/Bug/view.php?id=15585) you could support. – Florian Dec 21 '15 at 10:44
  • 3
    For the moment being you could define a macro, and in fact limit your call to `your_macro(Debug,/MTd,/MT)` – Antonio Dec 21 '15 at 16:23
  • I provided a macro definition in my answer. – thiagowfx Dec 10 '16 at 19:37
  • 2
    The `$` operator was just added with [this commit](https://gitlab.kitware.com/cmake/cmake/commit/895f7f16a79428689a263ba5cd9a72647dc8e912). – Florian Jan 29 '17 at 19:54

3 Answers3

29

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

Gelldur
  • 11,187
  • 7
  • 57
  • 68
Lance E.T. Compte
  • 932
  • 1
  • 11
  • 33
  • 2
    Nice find! It's documented here: https://cmake.org/cmake/help/v3.8/manual/cmake-generator-expressions.7.html#id3 – Daniel Wolf May 16 '19 at 08:06
  • 1
    Thanks, but documentation does not give a single example for `$ – vulcan raven Mar 08 '20 at 01:59
  • @vulcanraven Indeed the cmake documentation is very short on helpful examples. Does the example that Dawid Drozd added help you enough? – Lance E.T. Compte Mar 09 '20 at 20:17
  • @LanceE.T.Compte, wasn't looking _just_ for an example :) I saw a code somewhere of the form `$, trueValue>`, without needing to specify false. I was searching for documentation on `$ – vulcan raven Mar 10 '20 at 02:37
  • 1
    @vulcanraven $ is documented here: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html Omitting the falseValue would substitute an empty string. I actually do that when adding debug flags, etc. – Lance E.T. Compte Mar 10 '20 at 16:42
  • Exactly what I was trying to find. Thanks a lot! :) – vulcan raven Mar 10 '20 at 17:19
6

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;

thiagowfx
  • 4,832
  • 6
  • 37
  • 51
  • 2
    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. – Florian Jan 03 '17 at 21:07
  • @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. – silvioprog Feb 09 '18 at 05:05
0

Let's make it more explicit:

add_custom_command(TARGET myProject PRE_BUILD
    COMMAND cd \"D:/projects/$<IF:$<CONFIG:Debug>,Debug,Release>\"
    COMMAND call prebuild.bat)

Then in generated Visual Studio Project, in the property window of project "myProject".

When build configuration is "Debug", the evaluation process is:

  1. $<IF:$<CONFIG:Debug>,Debug,Release>
  2. $<IF:1,Debug,Release>
  3. Debug

When build configuration is "Release", the evaluation process is:

  1. $<IF:$<CONFIG:Debug>,Debug,Release>
  2. $<IF:0,Debug,Release>
  3. Release

zwcloud
  • 4,546
  • 3
  • 40
  • 69