makefile
SkillProductivityBest practices for authoring clean, maintainable, and portable GNU Make Makefiles. Use when the task involves `creating or editing Makefiles`, `Makefile`, `makefile`, `*.mk`, `GNUmakefile`, `GNU Make patterns`, `build automation`, or `makefile troubleshooting`.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the makefile skill
What this skill tells your AI
The instructions your AI receives, as published by dallay/agentsync in .agents/skills/makefile/SKILL.md and read by ahel’s review.
When to Use
- Creating a new
Makefilefor any project (C/C++, Go, Node.js, etc.) - Reviewing or refactoring existing makefiles for correctness and maintainability
- Debugging build failures related to make rules, prerequisites, or variables
- Setting up build automation with GNU Make (pattern rules, automatic dependencies, parallel builds)
- Ensuring portability across platforms or compliance with GNU Make conventions
Critical Patterns
- Tabs, Not Spaces: Every recipe line MUST start with a tab character. Spaces will cause a syntax error.
- Declare
.PHONY: Always mark non-file targets (e.g.,clean,all,test,install) with.PHONYto avoid conflicts with files of the same name. - Use
:=Over=When Possible: Prefer simple expansion (:=) for variables to avoid unexpected recursive expansion and improve performance. - Automatic Variables: Use
$@(target),$<(first prerequisite),$^(all prerequisites),$?(newer prerequisites), and$*(stem) in recipes to keep rules generic and reusable. - Use
$(wildcard ...)Not$(shell ls ...): Never shell out for file lists; use Make's built-in$(wildcard *.c)function instead. - Auto-Generate Dependencies: Use compiler flags like
-MMD -MPto generate.dfiles rather than maintaining header dependencies manually. - Clean Target: Always provide a
cleantarget and prefix destructive commands with-to ignore errors if files don't exist. - Variables First, Rules Second: Define all variables at the top, followed by the default goal, then build rules, then phony targets.
- Avoid Recursive Make: Don't use
$(MAKE) -C subdirunless absolutely necessary; prefer includes or non-recursive approaches.
File Structure
# -------------------------------------------------------
# Project: <name>
#
# Usage:
# make - Build the project
# make clean - Remove generated files
# make install - Install to $(PREFIX)
# make test - Run tests
#
# Variables:
# CC - C compiler (default: gcc)
# PREFIX - Installation prefix (default: /usr/local)
# -------------------------------------------------------
# --- Variables ---
CC ?= gcc
CFLAGS := -Wall -Wextra -O2
PREFIX ?= /usr/local
sources := $(wildcard src/*.c)
objects := $(sources:.c=.o)
deps := $(objects:.o=.d)
# --- Default Goal ---
all: program
# --- Build Rules ---
program: $(objects)
$(CC) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
-include $(deps)
# --- Phony Targets ---
.PHONY: all clean install test
clean:
-rm -f program $(objects) $(deps)
install: program
install -d $(PREFIX)/bin
install -m 755 program $(PREFIX)/bin
test: program
./run-tests.sh
Code Examples
Variables and Substitution
# Simple expansion (evaluates immediately) - PREFERRED
CC := gcc
# Recursive expansion (evaluates when used)
CFLAGS = -Wall $(EXTRA_FLAGS)
# Conditional assignment (set default, allow override)
PREFIX ?= /usr/local
# Append to variable
CFLAGS += -g
# Always reference with $() - not bare $VAR
objects := $(sources:.c=.o)
Pattern Rules and Implicit Rules
# Custom pattern rule for generic transformations
%.pdf: %.md
pandoc $< -o $@
# Compile with auto-dependency generation
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
Order-Only Prerequisites
# The directory is created before compiling but does NOT
# trigger recompilation when its timestamp changes.
obj/%.o: %.c | obj
$(CC) $(CFLAGS) -c $< -o $@
obj:
mkdir -p obj
Conditional Directives
ifeq ($(OS),Windows_NT)
EXE_EXT := .exe
else
EXE_EXT :=
endif
program: main.o
$(CC) -o program$(EXE_EXT) main.o
Splitting Long Lines
# Use backslash-newline to split for readability
sources := main.c \
utils.c \
parser.c \
handler.c
# In recipes, backslash-newline continues the shell command
install: program
install -d $(PREFIX)/bin && \
install -m 755 program $(PREFIX)/bin
Including Other Makefiles
# Include shared configuration
include config.mk
# Include optional local overrides (no error if missing)
-include local.mk
Error Handling and Validation
# Validate required tools
ifeq ($(shell which gcc 2>/dev/null),)
$(error gcc is not installed or not in PATH)
endif
# Validate required variables
ifndef VERSION
$(error VERSION is not defined. Usage: make VERSION=1.2.3)
endif
Special Targets
# Delete targets if recipe fails (recommended)
.DELETE_ON_ERROR:
# Don't delete intermediate files
.SECONDARY:
# Preserve specific intermediate files
.PRECIOUS: %.o
Managing Multiple Programs
programs := prog1 prog2 prog3
.PHONY: all clean
all: $(programs)
prog1: prog1.o common.o
$(CC) -o $@ $^
prog2: prog2.o common.o
$(CC) -o $@ $^
clean:
-rm -f $(programs) *.o
Anti-Patterns to Avoid
| Anti-Pattern | Why It's Bad | Do Instead |
|---|---|---|
| Spaces instead of tabs in recipes | Syntax error, hard to debug | Use tab characters |
$(shell ls *.c) for file lists | Creates subprocess, fragile | $(wildcard *.c) |
Missing .PHONY declarations | clean won't run if a file named clean exists | .PHONY: clean |
| Hardcoded file lists | Maintenance burden, easy to forget new files | $(wildcard ...) or auto-discovery |
| Complex shell scripts in recipes | Hard to read and debug | Move to a separate .sh script |
Recursive make $(MAKE) -C subdir | Breaks dependency tracking across dirs | include or non-recursive make |
Using = everywhere | Unexpected recursive expansion, slower | Use := for simple values |
| Circular dependencies | Infinite loop, build failure | Restructure dependency graph |
Commands
# Dry run - see what would execute without running
make -n
# Force rebuild all targets
make -B
# Parallel build (use number of CPU cores)
make -j$(nproc)
# Print the database of rules and variables (debugging)
make -p
# Show which makefile and line number each rule comes from
make -d
# Override a variable from the command line
make CC=clang CFLAGS="-O3"
Resources
- GNU Make Manual - The authoritative reference
Signals
- GitHub stars
- 57
- Forks
- 6
- Last commit
- Sep 2026
Others that do the same job
Advanced
- Catalog kind
- skill
- Gateway key
makefile- Source
- github.com/dallay/agentsync