CMake Makefile

CMake has plenty of advantages, I don’t need to re-iterate them here. However, if you’re like me, who lives on the command line and has ./configure and make seemingly hard-wired into my fingers, it seems a bit cumbersome to have to type mkdir build, cd build, and (for example) cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON each time you want to build something.

It got me thinking, and I haven’t come across anyone else suggesting it (I’m sure plenty of people have though)… why not drive CMake through a Makefile? Sure you can script it up easily enough, but it seems a bit single-purpose each time I do it, and my fingers are just itching to type `make`.

It seems a weird concept, because of course CMake is, amongst other things, a Makefile generator. But here’s my take on it. I love being able to type make or make release:

Makefile:
# Usage:
#   make          -> debug build
#   make release  -> release build
#   make test     -> debug build + run tests
#   make clean    -> remove all build directories and symlink

APP_NAME := <<SET BINARY NAME HERE>>

BUILD_DIR_DEBUG   := build/Debug
BUILD_DIR_RELEASE := build/Release

MAKEFLAGS += --no-print-directory

CMAKE       := cmake
CMAKE_FLAGS :=
JOBS        := $(shell nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)

.PHONY: all debug release test clean

# -----------------------
# Default target -> debug
# -----------------------
all: debug

# -----------
# Debug build
# -----------
debug: $(BUILD_DIR_DEBUG)/Makefile
	$(CMAKE) --build $(BUILD_DIR_DEBUG) -- -j$(JOBS)

$(BUILD_DIR_DEBUG)/Makefile:
	@mkdir -p $(BUILD_DIR_DEBUG)
	$(CMAKE) -S . -B $(BUILD_DIR_DEBUG) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug $(CMAKE_FLAGS)

# -------------
# Release build
# -------------
release: $(BUILD_DIR_RELEASE)/Makefile
	$(CMAKE) --build $(BUILD_DIR_RELEASE) -- -j$(JOBS)

$(BUILD_DIR_RELEASE)/Makefile:
	@mkdir -p $(BUILD_DIR_RELEASE)
	$(CMAKE) -S . -B $(BUILD_DIR_RELEASE) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Release $(CMAKE_FLAGS)

# ------------------------
# Test (debug build + run)
# ------------------------
test: $(BUILD_DIR_DEBUG)/Makefile
	$(CMAKE) --build $(BUILD_DIR_DEBUG) --target $(APP_NAME)_tests -- -j$(JOBS)
	@$(BUILD_DIR_DEBUG)/$(APP_NAME)_tests --reporter console || true

# -----
# Clean
# -----
clean:
	@rm -rf build $(APP_NAME)
	@echo "-> build directories and symlink removed"

Set APP_NAME to the same as your CMake add_executable() name. This assumes you have a unit test framework and compiles a test suite in <APP_NAME>_tests (runnable with make test). It also symlinks the normal binary into your main project directory for easy running from the command line. Verbose building is as simple as typing VERBOSE=1 make. Tweak as you see fit.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.