CMake Best Practices

In a Nutshell

  • Declare modules with ADD_LIBRARY or ADD_EXECUTABLE

  • Declare build flags with TARGET_xxx()

  • Declare dependencies with TARGET_LINK_LIBRARIES

  • Specify which is PUBLIC and which is PRIVATE

Boilderplate

Headers

cmake_minimum_required(VERSION 3.0)

# declare top-level flags
if (MSVC)
  add_compile_options(/W3 /WX)
else()
  add_compile_options(-W -Wall -Werror)
endif()

Add Libraries

add_library(mylib
  src/file1.c
  src/file2.c
  ...)
    

Declare Flags

target_include_directories(mylib PUBLIC include)
target_include_directories(mylib PRIVATE src)

if (SOME_SETTING)
  target_compile_definitions(mylib
    PUBLIC WITH_SOME_SETTINGS)
endif()

Declare Dependencies

# for public (interface) dependencies
target_link_libraries(mylib PUBLIC abc)

# for private (implementation) dependencies
target_link_libraries(mylib PRIVATE xyz)

Header-only libraries

add_library(mylib INTERFACE)

target_include_directories(mylib INTERFACE include)

target_link_libraries(mylib INTERFACE Boost::Boost)

INTERFACE basically indicates that we don't need to build anything

DO NOT

  1. Don't use macros that affect all targets:

    • INCLUDE_DIRECTORIES()

    • ADD_DEFINITIONS()

    • LINK_LIBRARIES

  2. Dont' use TARGET_INCLUDE_LIBRARIES() with path outside your own module

  3. Don't use TARGET_LINK_LIBRARIES() without specifying scope (PUBLIC, PRIVATE, or INTERFACE)

  4. Don't use TARGET_COMPILE_OPTIONS() to set flags that affect the ABI

Last updated