The build system for a project is generated by CMake in two phases: a configuration phase, where CMakeLists.txt is parsed, and a generation phase, where the build environment is actually generated. Generator expressions are evaluated in this second phase and can be used to tweak the build system with information that can only be known at generation time. Generator expressions are thus particularly useful when cross-compiling, where some of the information is only available after the CMakeLists.txt has been parsed, or in multi-configuration projects, where the build system is generated at once for all the different configurations the project can have, such as Debug and Release.
In our case, we will use generator expressions to conditionally set a link dependency and compile definition. For this, we can focus on these two expressions:
target_link_libraries(example
PUBLIC
$<$<BOOL:${MPI_FOUND}>:MPI::MPI_CXX>
)
target_compile_definitions(example
PRIVATE
$<$<BOOL:${MPI_FOUND}>:HAVE_MPI>
)
If MPI_FOUND is true, then $<BOOL:${MPI_FOUND}> will evaluate to 1. In this case, $<$<BOOL:${MPI_FOUND}>:MPI::MPI_CXX> will evaluate to MPI::MPI_CXX and the second generator expression will evaluate to HAVE_MPI. If we set USE_MPI to OFF, MPI_FOUND is false and both generator expressions evaluate to empty strings, and thus no link dependency is introduced and no preprocessor definition is set.
We could have achieved the same effect by introducing an if-statement:
if(MPI_FOUND)
target_link_libraries(example
PUBLIC
MPI::MPI_CXX
)
target_compile_definitions(example
PRIVATE
HAVE_MPI
)
endif()
This solution is a bit less compact but possibly a bit more readable. We can often re-express if-statements using generator expressions and the choice is often a matter of taste. However, generator expressions particularly shine when we need to access or manipulate explicit file paths, since these can be difficult to construct using variables and if-clauses, and in this case we clearly favor generator expressions for readability. This was the case in Chapter 4, Creating and Running Tests, where we used generator expressions to resolve the file path of a particular target. We will also appreciate generator expressions in Chapter 11, Packaging Projects.