CMake

Build ToolC/C++Cross-platformProject ManagementConfiguration Generator

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

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