Modern CMake

May 8, 2018 at 18:00

From Stephen Kelly’s YouTube video and blog link, a quick overview of modern, reusable CMake: what we should use, and what we should not, for composable and terse CMake.

Note: we should define minimum version to ensure ‘Modern CMake’, and understand what CMake policies this might invoke.

Targets

Use target_*:

  • target_include_directories with PUBLIC and PRIVATE (-I/foo/bar)
  • target_compile_definitions (-DSOME_DEF=1)
  • target_compile_options (-O2 -fPIC)
  • target_link_libraries (-l/path/to/lib)

Don’t use

  • include_directories
  • add_definitions

as they have problems with scope and transitivity.

Variables

Avoid unnecessary variables:

  • can leak
  • not checked for correctness or content

Generator Expressions

Calculated at generation time.

Generators and conditions:

  • could use target_sources to append conditionally (e.g. platform specific file), don’t use a variable and append to it as a list.
  • could use generator:
add_executable(hello
    main.cpp
    $<$<BOOL:${WIN32}>:windows.cpp>
    $<$<NOT:$<BOOL:${WIN32}>>:windows.cpp>
)

Don’t use CMAKE_BUILD_TYPE (e.g. CMAKE_BUILD_TYPE STREQUAL DEBUG).

Use:

add_executable(hello
    main.cpp
    $<$<CONFIG:Debug>:debug_only.cpp>
    $<$<NOT:$<CONFIG:Debug>>:release_only.cpp>
)

Properties

Prefer:

#  Evaluated at 'generation time'
#  compile with 'USE_THREADS' if 'WITH_THREADS' is on.
target_compile_definitions(hello PRIVATE
 $<$<TARGET_PROPERTY:WITH_THREADS>:USE_THREADS>)

#  Set at 'configuration time'
set_property(TARGET hello PROPERTY WITH_THREADS ON)

Aliases

Can be helpful for readability, e.g.:

add_library(boost::mpl
    ALIAS boost_mpl
)

# consumed as
target_link_libraries(my_library
    boost::mpl
)

Dependencies

Legacy pattern was:

find_package(...)
add_definitions(...)
include_directories(...)
set(CMAKE_CXX_FLAGS ...)
add_executable(...)
target_link_libraries(...)

but that is for old-style (external) packages that populate variables. We may still need to use this.

Modern CMake define IMPORTED targets, so we just do:

find_package(foo REQUIRED)
add_executable(hello main.cpp)
target_link_libraries(hello
  Foo::Core
)

Creating Packages

See the video here

Install ‘rich’ targets and export packages.