CMake
Build Tool
CMake
Overview
CMake is a cross-platform, free and open-source family of software development tools for managing the build process, testing, and packaging in a compiler-independent manner. Originally developed by Bill Hoffman and others starting in 2000, it has become the de facto standard for build system generation in C/C++ projects. It supports various native build tools including Make, Ninja, Visual Studio, and Xcode, handling everything from complex project configurations to large-scale enterprise applications.
Details
Key Features
- Cross-platform Support: Works on Windows, Linux, macOS, and Unix-like systems
- Build System Generation: Generates Make, Ninja, Visual Studio, and Xcode project files
- Multi-language Support: Supports C, C++, Fortran, C#, Java, Python, and more
- Dependency Management: Automatic library detection and dependency resolution
- Test Integration: Comprehensive testing framework through CTest
- Packaging: Cross-platform installer creation through CPack
- IDE Integration: Excellent support in major IDEs (Visual Studio, Qt Creator, CLion, etc.)
Architecture
Generates native build scripts for target platforms from declarative CMakeLists files. Operates in three phases: configure, generate, and build, with support for out-of-source builds.
Ecosystem
Rich library support through find_package system, integration with package managers like vcpkg and Conan, standard usage in GitHub Actions and GitLab CI.
Pros and Cons
Pros
- Industry Standard: Most widely used build tool in C/C++ development
- Cross-platform: Single configuration works across multiple platforms
- Rich IDE Integration: Excellent support and IntelliSense in major IDEs
- Powerful Library Detection: Automatic library detection via find_package
- Mature Ecosystem: 20+ years of development history with extensive documentation
- Enterprise Adoption: Used by large projects like KDE, LLVM, OpenCV, Qt
- CI/CD Integration: Excellent integration with CI/CD pipelines
Cons
- Learning Curve: Requires understanding of unique syntax and concepts
- Configuration Complexity: Settings can become complex in large projects
- Debugging Difficulty: Troubleshooting generated build scripts can be challenging
- Performance: Configuration time can be lengthy for very large projects
- Version Compatibility: Compatibility issues with older versions
- Competition from Modern Tools: Rise of language-specific tools like Rust's Cargo, Go's Modules
Reference Links
- CMake Official Site
- CMake Documentation
- CMake Tutorial
- CMake GitHub Repository
- CMake Community Wiki
- Modern CMake
Usage Examples
Installation and Basic Setup
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install cmake
# CentOS/RHEL/Fedora
sudo yum install cmake
# or
sudo dnf install cmake
# macOS (Homebrew)
brew install cmake
# Windows (Chocolatey)
choco install cmake
# Version check
cmake --version
# Basic project creation
mkdir my-project
cd my-project
mkdir build
Basic CMakeLists.txt
# Specify minimum CMake version
cmake_minimum_required(VERSION 3.20)
# Project name and languages
project(MyProject
VERSION 1.0.0
DESCRIPTION "Sample C++ project with CMake"
LANGUAGES CXX
)
# C++ standard settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Create executable
add_executable(my_app
src/main.cpp
src/utils.cpp
)
# Specify header file directories
target_include_directories(my_app PRIVATE
include
)
# Build and install
# mkdir build && cd build
# cmake ..
# cmake --build .
# or make
Library Creation and Usage
cmake_minimum_required(VERSION 3.20)
project(MathLibrary VERSION 2.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Create static library
add_library(MathFunctions STATIC
src/MathFunctions.cpp
src/mysqrt.cpp
)
# Library header files
target_include_directories(MathFunctions
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
)
# Create executable
add_executable(calculator
src/main.cpp
)
# Link library
target_link_libraries(calculator PRIVATE MathFunctions)
# Add optional features
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# Add subdirectory
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
target_link_libraries(calculator PUBLIC ${EXTRA_LIBS})
External Library Detection and Usage
cmake_minimum_required(VERSION 3.20)
project(NetworkApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
# Find required packages
find_package(PkgConfig REQUIRED)
find_package(Threads REQUIRED)
# Find OpenSSL
find_package(OpenSSL REQUIRED)
if(OPENSSL_FOUND)
message(STATUS "OpenSSL found: ${OPENSSL_VERSION}")
endif()
# Find Boost libraries
find_package(Boost 1.70 REQUIRED
COMPONENTS
system
filesystem
network
program_options
)
# Find curl library (using pkg-config)
pkg_check_modules(CURL REQUIRED libcurl)
# Create executable
add_executable(network_client
src/main.cpp
src/http_client.cpp
src/ssl_utils.cpp
)
# Specify include directories
target_include_directories(network_client PRIVATE
include
${Boost_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
)
# Link libraries
target_link_libraries(network_client
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
Boost::system
Boost::filesystem
Boost::program_options
${CURL_LIBRARIES}
Threads::Threads
)
# Compiler-specific settings
target_compile_options(network_client PRIVATE ${CURL_CFLAGS_OTHER})
Modern CMake Best Practices
cmake_minimum_required(VERSION 3.20)
project(ModernProject
VERSION 1.2.3
DESCRIPTION "Modern CMake project example"
HOMEPAGE_URL "https://github.com/username/project"
LANGUAGES CXX
)
# Project settings
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Build type settings
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Compiler settings
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
# Interface libraries (header-only)
add_library(project_options INTERFACE)
add_library(project_warnings INTERFACE)
# Compiler warnings settings
target_compile_options(project_warnings
INTERFACE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Feature testing
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-std=c++20" COMPILER_SUPPORTS_CXX20)
if(NOT COMPILER_SUPPORTS_CXX20)
message(FATAL_ERROR "Compiler does not support C++20")
endif()
# Create library target
add_library(core_lib
src/core/engine.cpp
src/core/renderer.cpp
src/core/input.cpp
)
# Set target properties
set_target_properties(core_lib PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE ON
)
# Modern target-based configuration
target_include_directories(core_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
)
target_link_libraries(core_lib
PUBLIC
project_options
project_warnings
)
# Create executable
add_executable(main_app src/main.cpp)
target_link_libraries(main_app
PRIVATE
core_lib
project_options
project_warnings
)
# Install configuration
include(GNUInstallDirs)
install(TARGETS main_app core_lib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
Testing and CTest Integration
# Enable testing
enable_testing()
# Find and configure Google Test
find_package(GTest REQUIRED)
# Create test executable
add_executable(unit_tests
tests/test_main.cpp
tests/test_math.cpp
tests/test_utils.cpp
)
target_link_libraries(unit_tests
PRIVATE
core_lib
GTest::gtest
GTest::gtest_main
GTest::gmock
)
# Add tests
add_test(NAME unit_tests COMMAND unit_tests)
# Add custom tests
add_test(NAME quick_test
COMMAND main_app --test-mode
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Set environment variables for tests
set_tests_properties(unit_tests PROPERTIES
ENVIRONMENT "DATA_DIR=${CMAKE_SOURCE_DIR}/test_data"
)
# Coverage measurement (GCC/Clang)
option(ENABLE_COVERAGE "Enable coverage reporting" OFF)
if(ENABLE_COVERAGE)
target_compile_options(core_lib PRIVATE --coverage)
target_link_libraries(core_lib PRIVATE --coverage)
endif()
# Run tests
# cmake --build . --target test
# or ctest
# ctest --verbose # verbose output
# ctest -j 4 # parallel execution
Cross-platform Configuration
cmake_minimum_required(VERSION 3.20)
project(CrossPlatformApp LANGUAGES CXX)
# Platform detection
if(WIN32)
message(STATUS "Building for Windows")
set(PLATFORM_LIBS ws2_32 wsock32)
set(PLATFORM_DEFINITIONS WIN32_LEAN_AND_MEAN NOMINMAX)
elseif(APPLE)
message(STATUS "Building for macOS")
set(PLATFORM_LIBS "-framework Foundation" "-framework CoreFoundation")
set(PLATFORM_DEFINITIONS MACOS_BUILD)
elseif(UNIX)
message(STATUS "Building for Linux/Unix")
set(PLATFORM_LIBS pthread dl)
set(PLATFORM_DEFINITIONS LINUX_BUILD)
endif()
# Architecture detection
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(STATUS "64-bit build")
set(ARCH_DEFINITIONS ARCH_64)
else()
message(STATUS "32-bit build")
set(ARCH_DEFINITIONS ARCH_32)
endif()
# Create executable
add_executable(cross_app
src/main.cpp
src/platform_utils.cpp
)
# Platform-specific settings
target_compile_definitions(cross_app
PRIVATE
${PLATFORM_DEFINITIONS}
${ARCH_DEFINITIONS}
)
target_link_libraries(cross_app
PRIVATE
${PLATFORM_LIBS}
)
# Windows-specific settings
if(WIN32)
set_target_properties(cross_app PROPERTIES
WIN32_EXECUTABLE TRUE
)
endif()
# Add resource files (Windows)
if(WIN32)
target_sources(cross_app PRIVATE resources/app.rc)
endif()
Packaging (CPack)
# CPack configuration
include(CPack)
set(CPACK_PACKAGE_NAME "MyApplication")
set(CPACK_PACKAGE_VENDOR "Company Name")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My application description")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
# Installer configuration
if(WIN32)
set(CPACK_GENERATOR "NSIS;ZIP")
set(CPACK_NSIS_DISPLAY_NAME "My Application")
set(CPACK_NSIS_PACKAGE_NAME "MyApp")
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/app.ico")
elseif(APPLE)
set(CPACK_GENERATOR "DragNDrop;TGZ")
set(CPACK_DMG_FORMAT "UDZO")
set(CPACK_DMG_VOLUME_NAME "MyApplication")
else()
set(CPACK_GENERATOR "DEB;RPM;TGZ")
# DEB package settings
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "[email protected]")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6")
# RPM package settings
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Productivity")
endif()
# Create package
# cmake --build . --target package