Minimal CMakeLists.txt with Essential Project Directives
A self-contained CMakeLists.txt for a small C++ project typically looks like this:
cmake_minimum_required(VERSION 3.10)
set(REPO_ROOT "/home/dev/project/source")
project("sample")
include_directories(${REPO_ROOT}/headers)
link_directories(${REPO_ROOT}/external)
aux_source_directory(${REPO_ROOT}/core APP_SOURCES)
add_executable(app ${APP_SOURCES})
target_link_libraries(app libhelper.a)
include_directories adds paths to the compiler’s header search list so that directivse such as #include "module.h" resolve correctly. It does not copy files; it only affects the preprocessor.
link_directories registers folders where the linker should look for libraries. By itself, it does not pull any binary into the final executable. The actual linking is performed by target_link_libraries. For example:
target_link_libraries(app libhelper.a)
When a directory is already listed via link_directories, the statement above is effectively equivalent to supplying the full path. For shared libraries on Windows, the corresponding .dll must be placed in the same directory as the generated .exe or in a location listed in the system PATH.
Several CMake features are optional and are often unnecessary for small builds.
Converting Qt Designer forms:
set(UI_FILES "dialog.ui")
qt5_wrap_ui(GENERATED_UI ${UI_FILES})
add_executable(app ${GENERATED_UI} ...)
This generates C++ headers from .ui files before compilation begins.
Custom path variables:
set(LIB_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/out/lib")
CMAKE_CURRENT_SOURCE_DIR expands to the directory containing the current CMakeLists.txt.
Managing child projects:
add_subdirectory(components/engine)
This instructs CMake to descend into the specified folder and process its own CMakeLists.txt.
Explicit dependency ordering:
add_library(engine STATIC engine.cpp)
add_executable(editor editor.cpp)
target_link_libraries(editor engine)
add_dependencies(editor engine)
CMake usually infers build order automatically from target_link_libraries. add_dependencies is only required when one target must finish building before another target that does not directly link against it.
Detecting the host platform:
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Target is Linux")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
if(CMAKE_CL_64)
message(STATUS "Target is Windows x64")
else()
message(STATUS "Target is Windows x86")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
message(STATUS "Target is macOS")
else()
message(STATUS "Unknown target platform")
endif()
The message command supports several severity levels:
| Mode | Effect |
|---|---|
(none) |
Prints important information |
STATUS |
Prints a low-priority note |
WARNING |
Emits a warning and continues configuration |
AUTHOR_WARNING |
Emits a developer-mode warning and continues |
SEND_ERROR |
Reports a error, skips generation, but keeps processing |
FATAL_ERROR |
Reports an error and stops immediateyl |