from here: https://github.com/mortennobel/CMake-Cheatsheet

Quick example

Assume following source tree, where the source files in Features are to be compiled into a static library, which in turn is required by Application:

.
+-- Features
|   +-- feature1.cpp
|   +-- feature1.hpp
|   +-- feature2.cpp
|   `-- feature2.hpp
`-- Application
    `-- main.cpp

Then, create two CMake script files:

Features/CMakeLists.txt:

cmake_minimum_required (VERSION 3.8)

project(Features)

file(GLOB sourcefiles feature1.* feature2.*)

add_library(Features STATIC ${sourcefiles})

install(TARGETS Features DESTINATION ${CMAKE_CURRENT_LIST_DIR}/lib)

Application/CMakeLists.txt:

cmake_minimum_required (VERSION 3.8)

project(Application)

set(Features_include ${CMAKE_CURRENT_LIST_DIR}/..)
set(Features_libpath ${CMAKE_CURRENT_LIST_DIR}/../Features/lib)

include_directories(${Features_include})
link_directories(${Features_libpath})

add_executable(Application main.cpp)
target_link_libraries(Application Features)

install(TARGETS Application DESTINATION ${CMAKE_CURRENT_LIST_DIR}/bin)

Finally, cd into Features and do:

mkdir .build
cd .build
cmake ..
cmake --build . --target install

Then, cd into Application and do the same.

This can be automated with a Makefile:

all: build

.build/CMakeCache.txt: CMakeLists.txt
	cmake -E make_directory .build
	cmake -E remove .build/CMakeCache.txt
	cmake -E chdir .build cmake ..

build: .build/CMakeCache.txt
	cmake -E chdir .build cmake --build . --target install

clean:
	cmake -E remove_directory .build
	cmake -E remove_directory bin
	cmake -E remove_directory lib

help:
	@cmake -E echo "usage:"
	@cmake -E echo
	@cmake -E echo "make [all]   Creates CMake configuration and builds the project."
	@cmake -E echo "make clean   Removes build directory and installation targets."

.PHONY: all build clean help

Makefile: ;

CMakeLists.txt: ;

A more advanced example

Here we have a library with multiple targets: the library itself and two example applications that use the library.

.
+-- CMakeLists.txt
`-- src
    +-- lib
    |   +-- feature1.cpp
    |   +-- feature1.hpp
    |   +-- feature2.cpp
    |   `-- feature2.hpp
    `-- apps
        +-- example1.cpp
        `-- example2.cpp

All targets in one CMake script:

cmake_minimum_required (VERSION 3.17)

set(project-name project)
project(${project-name})

set(CMAKE_CXX_STANDARD 17)

include_directories(src/lib)

# library

set(library-name ${project-name}-2.0)

file(GLOB header-files src/lib/*.h)
file(GLOB source-files src/lib/*.cc src/lib/*.h)

add_library(${library-name} STATIC ${source-files})

install(TARGETS ${library-name} DESTINATION lib)
install(FILES ${header-files} DESTINATION include/${project-name})

# applications

foreach(app-name example1 example2)

    add_executable(${app-name} src/apps/${app-name}.cc)
    target_link_libraries(${app-name} ${library-name})

    install(TARGETS ${app-name} DESTINATION bin)

endforeach()

Advanced Makefile

CONF=Release

CMAKE_CONF = -DCMAKE_BUILD_TYPE=$(CONF)

ifeq ($(CONF), Release)
BUILD_DIR = .build-release
else ifeq ($(CONF), Debug)
BUILD_DIR = .build-debug
else
$(error unknown configuration "$(CONF)")
endif

all: install

$(BUILD_DIR)/CMakeCache.txt: CMakeLists.txt
	rm -f $@
	cmake -S. -B$(BUILD_DIR) $(CMAKE_CONF)

build: $(BUILD_DIR)/CMakeCache.txt
	make -C $(BUILD_DIR)

install: build
	make -C $(BUILD_DIR) install

clean:
	rm -rf $(BUILD_DIR)

help:
	@echo "usage:"
	@echo
	@echo "make [CONF={Release|Debug}] [all]      Same as make install."
	@echo
	@echo "make [CONF={Release|Debug}] install    Generates build system,"
	@echo "                                       then builds and installs project."
	@echo
	@echo "make [CONF={Release|Debug}] build      Generates build system,"
	@echo "                                       then builds project."
	@echo
	@echo "make [CONF={Release|Debug}] clean      Removes build system."
	@echo "                                       Does NOT uninstall project."
	@echo
	@echo "Default CONF is Release."

.PHONY: all build install clean help

Makefile: ;

CMakeLists.txt: ;

solution CMakeLists.txt

cmake_minimum_required (VERSION 3.16)
project(project-name)

## each subdirectory has its own CMake script;
#
# cmake derives the order automatically from the dependence among targets
# (which is defined by the libraries passed to the linker)

# library projects

add_subdirectory(lib1)
add_subdirectory(lib2)

# executable projects

add_subdirectory(app1)
add_subdirectory(app2)

reference

dependence between targets

add_dependencies(<TARGET_A> <TARGET_A_DEPENDS_ON> ...)

execute pre-build commands

add_custom_target(info-message COMMAND echo "Build started: target ${target-lib}")
add_dependencies(${target-lib} info-message)

execute post-build commands

add_custom_command(TARGET ${target-lib} POST_BUILD COMMAND echo "Build successful: target ${target-lib}")

for Qt projects

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 REQUIRED COMPONENTS Widgets Core Gui)

add_library(lib-name STATIC <FILES> <QT-RESOURCE>.qrc)
add_executable(app-name <FILES> <QT-RESOURCE>.qrc)