Modern CMake is all about targets and properties. The whole point is to avoid using CMake plain variables and apply the same best practices we apply to our source code. Using the targets and properties approach comes packed with additional machinery that makes identifying errors and exporting projects way easier.
An additional argument to stop using variables in CMake is that plain variables are just replaced as strings which are error prone. For example, if we commit a typo, the variable is just translated into a an empty string, which might be quite difficult to debug. Working with this new CMake approach is really easy.
First we define our targets. There are to options:
add_executable
add_library
I will use as example a new project I’m working on: a C++ chip8 interpreter. The project structure looks like this:
build
cmake
depends
docs
include
src/
main.cpp
other source files
test/
test source files
CMakelists.txt
README.md
We basically have 4 targets in this project. The main executable, the rest of the sources (which I refer to as the core library), the test executable, and the test case sources.
This division allows us to easily tune the properties of each target appropriately. For example, we will use different compile flags for test and the core files.
In the cmake folder we will store the additional required files:
cmake/
chip8_core_lib.cmake
chip8_test_lib.cmake
scripts/
functions and macros common among the project
Our project cmake files should be prefixed with the project name, which will act as a namespace and will reduce the risk of collision with other projects’ scripts.
chip8_core_lib.cmake
will define the core lib target. We will use an object library, which will compile the files but not link them.
add_library(CHIP8_CORE_LIB
OBJECT
src/Interpreter.cpp
src/io/Speaker.cpp
src/io/Keypad.cpp
src/io/KeyMap.cpp
src/io/display/PixelArray.cpp
src/io/display/Renderer.cpp
src/details/memory.cpp
src/details/audio.cpp
src/registers/DataRegister.cpp
src/registers/IRegister.cpp
)
target_include_directories(CHIP8_CORE_LIB
PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_features(CHIP8_CORE_LIB PRIVATE cxx_std_17)
And our test object library:
add_library(CHIP8_TESTS_LIB
OBJECT
test/rom_test.cpp
test/timer_test.cpp
test/speaker_test.cpp
test/keypad_test.cpp
test/display_test.cpp
test/register_test.cpp
test/random_test.cpp
)
target_include_directories(CHIP8_TESTS_LIB PRIVATE include)
target_compile_features(CHIP8_TESTS_LIB PRIVATE cxx_std_17)
Our main CMake file can make use of these two object libraries to build the executable targets:
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(chip8_cpp VERSION 1.0.0)
list(APPEND CMAKE_MODULE_PATH
"${PROJECT_SOURCE_DIR}/cmake"
"${PROJECT_SOURCE_DIR}/cmake/modules"
"${PROJECT_SOURCE_DIR}/cmake/scripts"
)
set(MAIN_EXECUTABLE ${PROJECT_NAME}_interpreter)
include(chip8_core_lib)
add_executable(${MAIN_EXECUTABLE} src/main.cpp
$<TARGET_OBJECTS:CHIP8_CORE_LIB>
)
if(BUILD_TESTS)
enable_testing()
set(TESTS_EXECUTABLE ${PROJECT_NAME}_tests)
include(chip8_tests_lib)
add_executable(${TESTS_EXECUTABLE} test/test_main.cpp
$<TARGET_OBJECTS:CHIP8_CORE_LIB>
$<TARGET_OBJECTS:CHIP8_TESTS_LIB>
)
endif()
This structure allows as to have full control over the targets. Let’s say we want to add clang-tidy and set different flags for the different targets. We will be using this for all targets, so lets create a script:
function(target_add_clang_tidy TARGET_NAME_ARG CLANG_TIDY_CHECKS)
if(${CLANG_TIDY})
find_program(
CLANG_TIDY_PATH
NAMES "clang-tidy"
DOC "Path to clang-tidy executable"
)
if(NOT CLANG_TIDY_PATH)
message(FATAL_ERROR "Clang-tidy not found.")
endif()
execute_process (
COMMAND ${CLANG_TIDY_PATH} --version
OUTPUT_VARIABLE CLANG_TIDY_VERSION
)
# if clang-tidy version > 8.0.0
if(${CLANG_TIDY_VERSION} MATCHES "([1-9]+[0-9]|[8-9])\.([0-9]+)\.([0-9]+)")
message(STATUS "Clang-tidy found: ${CLANG_TIDY_PATH}")
set(CLANG_TIDY_PATH_AND_OPTIONS "${CLANG_TIDY_PATH}" "${CLANG_TIDY_CHECKS}")
set_target_properties(
${TARGET_NAME_ARG} PROPERTIES
CXX_CLANG_TIDY "${CLANG_TIDY_PATH_AND_OPTIONS}"
)
else()
message(FATAL_ERROR
"Minimum clang-tidy version is 8.0.0\n
Current version:\n ${CLANG_TIDY_VERSION}\n
Please upgrade."
)
endif()
endif()
endfunction(target_add_clang_tidy)
Now we can set clang tidy flags for our core libs, and different ones for the test files:
include(chip8_clang_tidy)
string(CONCAT CHIP8_CORE_CLANG_TIDY_CHECKS "-checks=*,"
# Disabled checks must be marked with a slash prefix
"-fuchsia-*" ","
"-google-readability-namespace-comments" ","
"-google-readability-todo" ","
"-llvm-namespace-comment" ","
"-hicpp-uppercase-literal-suffix" ","
"-readability-uppercase-literal-suffix"
)
target_add_clang_tidy(CHIP8_CORE_LIB ${CHIP8_CORE_CLANG_TIDY_CHECKS})
include(chip8_clang_tidy)
string(CONCAT CHIP8_TESTS_LIB_CLANG_TIDY_CHECKS "-checks=*,"
# Disabled checks must be marked with a slash prefix
"-fuchsia-*" ","
"-google-readability-namespace-comments" ","
"-google-readability-todo" ","
"-llvm-namespace-comment" ","
"-hicpp-uppercase-literal-suffix" ","
"-readability-uppercase-literal-suffix" ","
"-cppcoreguidelines-special-member-functions" ","
"-hicpp-special-member-functions" ","
"-cert-err58-cpp" ","
"-cppcoreguidelines-owning-memory" ","
"-cppcoreguidelines-avoid-goto" ","
"-hicpp-avoid-goto" ","
"-cppcoreguidelines-avoid-magic-numbers" ","
"-readability-magic-numbers" ","
"-cppcoreguidelines-pro-type-vararg" ","
"-hicpp-vararg"
)
target_add_clang_tidy(CHIP8_TESTS_LIB ${CHIP8_TESTS_LIB_CLANG_TIDY_CHECKS})
That’s just an example of what we can easily do! If you are struggling grasping all this concepts, do check the magnificent talk from Daniel Pfeifer:
Long time supporter, and thought I’d drop a comment.
Your wordpress site is very sleek – hope you don’t mind
me asking what theme you’re using? (and don’t mind if I steal it?
:P)
I just launched my site –also built in wordpress like yours– but the theme slows (!) the
site down quite a bit.
In case you have a minute, you can find it by searching for “royal cbd” on Google (would
appreciate any feedback) – it’s still in the works.
Keep up the good work– and hope you all take care of yourself during
the coronavirus scare!
Hi, Justin thank you for your support! The theme is OceanWP Theme by Nick. Have a good one!
My spouse and I stumbled over here from a different web address
and thought I should check things out. I like what I see so now i’m following you.
Look forward to looking over your web page for a
second time.
Hey there! Would you mind if I share your blog with my myspace group?
There’s a lot of people that I think would really appreciate your
content. Please let me know. Cheers
Wow, amazing blog layout! How long have you been blogging for?
you made blogging look easy. The overall look of your web site is magnificent, let alone the content!
wonderful points altogether, you simply won a new reader.
What might you suggest about your submit that you just made
some days ago? Any certain?