| name | dev-cmake |
| description | Write CMake and Make files like a principal engineer with decades of experience. Use when creating, refactoring, reviewing, or debugging CMakeLists.txt, Makefiles, or build system configurations for C/C++ projects. Produces elegant, minimal, modern build files that reflect deep understanding of build systems. |
CMake & Make Expert
Write build files that are elegant because the understanding is deep. Every line should have a reason. Simplicity comes from mastery, not shortcuts.
Core Philosophy
Targets are everything. Modern CMake is about targets and properties, not variables and directories. Think of targets as objects with member functions and properties.
Explicit over implicit. Always specify PRIVATE, PUBLIC, or INTERFACE. Never rely on inherited directory-level settings.
Minimal surface area. Expose only what consumers need. Default to PRIVATE; use PUBLIC only when downstream targets genuinely require it.
CMake: The Principal Engineer Approach
Project Structure
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Set standards at target level, not globally
# Use compile features, not flags
Target Definition Pattern
add_library(mylib)
add_library(MyProject::mylib ALIAS mylib)
target_sources(mylib
PRIVATE
src/impl.cpp
PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/mylib/api.h
)
target_compile_features(mylib PUBLIC cxx_std_17)
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
What to Never Do
include_directories() — use target_include_directories()
link_directories() — use full paths or targets
add_definitions() — use target_compile_definitions()
link_libraries() — use target_link_libraries()
CMAKE_CXX_FLAGS manipulation — use target_compile_options() or features
file(GLOB) for sources — list files explicitly
- Bare library names in
target_link_libraries() — use namespaced targets
Dependency Handling
For find_package dependencies:
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem)
target_link_libraries(mylib PRIVATE Boost::filesystem)
For FetchContent (prefer over ExternalProject for CMake deps):
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.1.0
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(mylib PRIVATE fmt::fmt)
Generator Expressions
Use for build/install path differences:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
Use for conditional compilation:
target_compile_definitions(mylib PRIVATE
$<$<CONFIG:Debug>:DEBUG_MODE>
)
Make: The Principal Engineer Approach
Essential Structure
CC ?= gcc
CXX ?= g++
CFLAGS ?= -Wall -Wextra -pedantic
LDFLAGS ?=
CFLAGS += -MMD -MP
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:src/%.c=build/%.o)
DEPS := $(OBJS:.o=.d)
.PHONY: all clean
all: bin/program
bin/program: $(OBJS) | bin
$(CC) $(LDFLAGS) -o $@ $^
build/%.o: src/%.c | build
$(CC) $(CFLAGS) -c -o $@ $<
bin build:
mkdir -p $@
clean:
rm -rf build bin
-include $(DEPS)
Pattern Rules — The Elegant Way
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
Automatic Variables (memorize these)
$@ — target
$< — first prerequisite
$^ — all prerequisites (no duplicates)
$+ — all prerequisites (with duplicates, for libs)
$* — stem (matched by %)
What Makes Makefiles Elegant
- Order-only prerequisites (
| dir) for directory creation
-include for optional dependency files
?= for overridable defaults, += to append
.PHONY for non-file targets
.DELETE_ON_ERROR to clean failed builds
- Consistent variable naming (UPPERCASE for user-facing)
Reference Files
- references/cmake-patterns.md — Complete modern CMake patterns (library export, install, presets, toolchains)
- references/make-patterns.md — Advanced Make patterns (multi-directory, cross-compilation, dependencies)
- references/antipatterns.md — Common mistakes and their fixes
Quality Checklist
Before finalizing any build file:
CMake
Make