Skip to content

C CMake — Cross-Platform Build System Configuration

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about C CMake. We cover key concepts, practical examples, and best practices to help you master this topic.

CMake is a cross-platform build system generator that uses CMakeLists.txt files to define targets (executables and libraries), dependencies, and build configuration, generating native build files (Makefiles, Ninja, Visual Studio solutions) for any platform.

What You Will Learn

  • Writing CMakeLists.txt for C projects
  • Defining targets with add_executable and add_library
  • Managing dependencies with target_link_libraries
  • Finding external packages with find_package
  • Setting compiler flags and build types
  • Using generator expressions
  • Organizing projects with subdirectories

Why It Matters

Makefiles are platform-specific and tedious to maintain. CMake abstracts away platform differences: the same CMakeLists.txt works on Linux (Makefiles), macOS (Xcode), and Windows (Visual Studio). It handles compiler detection, library discovery, and installation rules automatically. Durga Antivirus Pro uses CMake to build on Linux (apt packages), Windows (MSI installer), and macOS (DMG), all from one source tree.

Real-World Use

A scientific computing library needs to support GCC on Linux, Clang on macOS, and MSVC on Windows. The CMakeLists.txt uses find_package(OpenMP) for parallel loops, find_package(MPI) for distributed computing, and find_package(CUDA) for GPU acceleration. Each developer runs cmake .. && make regardless of their platform.

Learning Path

flowchart LR
  A[Libraries] --> B[CMake\nYou are here]
  B --> C[File I/O]
  style B fill:#f90,color:#fff

Minimal CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(Hello C)

add_executable(hello main.c)

Build:

mkdir build && cd build
cmake ..
make
./hello

Library with CMake

cmake_minimum_required(VERSION 3.16)
project(Calculator VERSION 1.0.0 LANGUAGES C)

# Set C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# Library
add_library(calc STATIC
    src/calc.c
    src/math_ops.c
)
target_include_directories(calc PUBLIC include)

# Executable
add_executable(calculator
    src/main.c
)
target_link_libraries(calculator PRIVATE calc)

Multiple Build Types

cmake_minimum_required(VERSION 3.16)
project(MyApp C)

# Configure build types
set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG -Wall")
set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG -march=native")

add_executable(myapp main.c)

# Platform-specific settings
if(WIN32)
    target_compile_definitions(myapp PRIVATE PLATFORM_WINDOWS)
    target_link_libraries(myapp PRIVATE ws2_32)
elseif(APPLE)
    target_compile_definitions(myapp PRIVATE PLATFORM_MACOS)
else()
    target_compile_definitions(myapp PRIVATE PLATFORM_LINUX)
endif()

Build with different configurations:

cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_BUILD_TYPE=Release ..

Finding and Using Packages

cmake_minimum_required(VERSION 3.16)
project(HttpServer C)

# Find required packages
find_package(OpenSSL REQUIRED)
find_package(ZLIB REQUIRED)
find_package(CURL QUIET)

add_executable(server
    src/server.c
    src/tls.c
    src/compress.c
)

target_link_libraries(server PRIVATE
    OpenSSL::SSL
    OpenSSL::Crypto
    ZLIB::ZLIB
)

if(CURL_FOUND)
    target_link_libraries(server PRIVATE CURL::libcurl)
    target_compile_definitions(server PRIVATE HAVE_CURL)
endif()

Subdirectories and Organization

# CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.16)
project(TextEditor C)

add_subdirectory(src/libeditor)
add_subdirectory(src/cli)
add_subdirectory(src/tests)
# src/libeditor/CMakeLists.txt
add_library(editor
    buffer.c
    cursor.c
    syntax.c
)
target_include_directories(editor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# src/cli/CMakeLists.txt
add_executable(cli main.c)
target_link_libraries(cli PRIVATE editor)

Generator Expressions

Generator expressions evaluate at build time for platform-specific behavior:

add_executable(myapp main.c)

# Different source files per platform
target_sources(myapp PRIVATE
    $<$<PLATFORM_ID:Linux>:linux_input.c>
    $<$<PLATFORM_ID:Windows>:win_input.c>
    $<$<PLATFORM_ID:Darwin>:mac_input.c>
)

# Different compile options per compiler
target_compile_options(myapp PRIVATE
    $<$<C_COMPILER_ID:GNU>:-Wall -Wextra>
    $<$<C_COMPILER_ID:Clang>:-Weverything>
    $<$<C_COMPILER_ID:MSVC>:/W4>
)

Installing and Packaging

cmake_minimum_required(VERSION 3.16)
project(MyLib VERSION 2.1.0 LANGUAGES C)

add_library(mylib SHARED src/mylib.c)
target_include_directories(mylib PUBLIC include)

# Install rules
install(TARGETS mylib
    LIBRARY DESTINATION lib
    PUBLIC_HEADER DESTINATION include
)

install(FILES include/mylib.h DESTINATION include)

# CPack for packaging
include(CPack)

Common Mistakes

  1. Not creating a separate build directory: Running cmake in the source directory creates clutter. Always use mkdir build && cd build && cmake ...

  2. Forgetting cmake_minimum_required: CMake policy changes between versions. Without this line, CMake uses the latest policies, which may break on older CMake versions.

  3. Mixing PUBLIC, PRIVATE, and INTERFACE incorrectly: PUBLIC: used by the target and its dependents. PRIVATE: used only by the target. INTERFACE: used only by dependents (for header-only libraries).

  4. Hardcoding paths instead of using generator expressions: target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) is correct. Hardcoding /home/user/project/include breaks on other machines.

  5. Not setting C++_STANDARD if using C++: CMake defaults to no standard. Set set(CMAKE_C_STANDARD 11) for C or set(CMAKE_CXX_STANDARD 17) for C++ projects.

Practice Questions

  1. What is the purpose of cmake_minimum_required?
  2. What is the difference between add_executable and add_library?
  3. How does find_package locate and configure external libraries?
  4. What is a generator expression and when would you use one?
  5. Challenge: Write a CMakeLists.txt for a project that builds a shared library (libgeometry.so) for shape calculations, a unit test executable, and a main application. The library uses math functions from libm. The test executable links against libgeometry and a testing framework (find_package or FetchContent). Support both Debug and Release builds. Install the library and headers.

Mini Project

Build a complete C project with CMake:

  • Project name: DataProcessor
  • Structure: src/, include/, tests/, examples/, tools/
  • Library target: dataproc (shared library) in src/
  • Functions: csv_parse, json_parse, stats_mean, stats_stddev, filter, sort
  • Test target: test_dataproc linking against dataproc and a simple test framework
  • Example target: example using dataproc to parse a CSV and compute statistics
  • Install target: install the library, headers, and examples
  • CPack: generate a .deb package and a tarball
  • Support: BUILD_SHARED_LIBS option, BUILD_TESTING option
  • Use: generator expressions for platform-specific flags

FAQ

Should I use Make or CMake for my project?

CMake is the modern standard. It generates Makefiles (or Ninja files) automatically, handles cross-platform issues, and integrates with IDEs. Use CMake even for small projects.

What is the difference between CMake and a Makefile?

CMake is a meta-build system that generates build files (including Makefiles). Makefiles are the build files themselves. CMake is portable; Makefiles are platform-specific.

{{< faq "How do I add a compiler flag?" "Use target_compile_options(target PRIVATE -Wall) for per-target flags, or set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wall\") for global flags." >}}
What is the FetchContent module?

It downloads and integrates external dependencies at configure time. Example: FetchContent_Declare(cjson GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git).

How do I build a static version of my library?

Set BUILD_SHARED_LIBS to OFF or use add_library(mylib STATIC ...). You can also build both: add_library(mylib SHARED ...) and add_library(mylib_static STATIC ...).

What is Next

Proceed to File I/O to learn about reading and writing files in C. Then explore Standard Library for commonly used libc functions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro

Home Browse C