Agent Skills: Makefile Skill

GNU Make automation and build system guidance

UncategorizedID: itechmeat/llm-code/makefile

Install this agent skill to your local

pnpm dlx add-skill https://github.com/itechmeat/llm-code/tree/HEAD/skills/makefile

Skill Files

Browse the full folder contents for makefile.

Download Skill

Loading file tree…

skills/makefile/SKILL.md

Skill Metadata

Name
makefile
Description
GNU Make automation and build system guidance

Makefile Skill

Guidance for creating and maintaining GNU Make build automation.

Quick Navigation

| Topic | Reference | | ----------------------------- | --------------------------------------- | | Rules, prerequisites, targets | syntax.md | | Variable types and assignment | variables.md | | Built-in functions | functions.md | | Special and phony targets | targets.md | | Recipe execution, parallel | recipes.md | | Implicit and pattern rules | implicit.md | | Common practical patterns | patterns.md |


Core Concepts

Rule Structure

target: prerequisites
        recipe

Critical: Recipe lines MUST start with TAB character.

File vs Phony Targets

# File target - creates/updates a file
build/app.o: src/app.c
        $(CC) -c $< -o $@

# Phony target - action, not a file
.PHONY: clean test install

clean:
        rm -rf build/

Variable Assignment

| Operator | Name | When Expanded | | -------- | ----------- | ----------------------- | | := | Simple | Once, at definition | | ?= | Conditional | If not already set | | = | Recursive | Each use (late binding) | | += | Append | Adds to existing value |

CC := gcc              # Immediate
CFLAGS ?= -O2          # Default, overridable
DEBUG = $(VERBOSE)     # Late binding
CFLAGS += -Wall        # Append

Automatic Variables

| Variable | Meaning | | -------- | ------------------------------- | | $@ | Target | | $< | First prerequisite | | $^ | All prerequisites (unique) | | $? | Prerequisites newer than target | | $* | Stem in pattern rules |


Essential Patterns

Self-Documenting Help

.DEFAULT_GOAL := help

help: ## Show available targets
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "  %-15s %s\n", $$1, $$2}'

install: ## Install dependencies
	uv sync

test: ## Run tests
	uv run pytest

Platform Detection

UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Darwin)
    OPEN := open
else ifeq ($(UNAME_S),Linux)
    OPEN := xdg-open
endif

Build Directory

BUILDDIR := build
SOURCES := $(wildcard src/*.c)
OBJECTS := $(patsubst src/%.c,$(BUILDDIR)/%.o,$(SOURCES))

$(BUILDDIR)/%.o: src/%.c | $(BUILDDIR)
	$(CC) -c $< -o $@

$(BUILDDIR):
	mkdir -p $@

Environment Export

export PYTHONPATH := $(PWD)/src
export DATABASE_URL

test:
	pytest tests/  # sees exported variables

Common Targets

Quality Checks

.PHONY: lint format check test

lint: ## Run linters
	ruff check .
	mypy src/

format: ## Format code
	ruff format .

check: format lint test ## All quality checks

Cleanup

.PHONY: clean clean-all

clean: ## Remove build artifacts
	rm -rf build/ dist/ *.egg-info
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true

clean-all: clean ## Remove all generated files
	rm -rf .venv .pytest_cache .mypy_cache

Docker Integration

IMAGE := myapp
VERSION := $(shell git describe --tags --always)

docker-build: ## Build Docker image
	docker build -t $(IMAGE):$(VERSION) .

docker-run: ## Run container
	docker run -d -p 8000:8000 $(IMAGE):$(VERSION)

Recipe Execution

Each Line = Separate Shell

# Won't work - cd lost between lines
bad:
	cd subdir
	pwd           # Still in original dir!

# Correct - combine commands
good:
	cd subdir && pwd

# Or use line continuation
also-good:
	cd subdir && \
	pwd && \
	make

Silent and Error Handling

target:
	@echo "@ suppresses command echo"
	-rm -f maybe.txt    # - ignores errors

Parallel Execution

make -j4              # 4 parallel jobs
make -j4 lint test    # Run lint and test in parallel

Output Discipline

One line in, one line out. Avoid echo spam.

# ❌ Too chatty
start:
	@echo "Starting services..."
	docker compose up -d
	@echo "Waiting..."
	@sleep 3
	@echo "Done!"

# ✅ Concise
start: ## Start services
	@echo "Starting at http://localhost:8000 ..."
	@docker compose up -d
	@echo "Logs: docker compose logs -f"

Conditionals

DEBUG ?= 0

ifeq ($(DEBUG),1)
    CFLAGS += -g -O0
else
    CFLAGS += -O2
endif

ifdef CI
    TEST_FLAGS := --ci
endif

Including Files

# Required include (error if missing)
include config.mk

# Optional include (silent if missing)
-include local.mk
-include .env

Common Pitfalls

| Pitfall | Problem | Solution | | --------------------- | --------------------------------------- | ------------------------ | | Spaces in recipes | Recipes need TAB | Use actual TAB character | | Missing .PHONY | make test fails if test file exists | Declare .PHONY: test | | cd in recipes | Each line is new shell | Use cd dir && command | | = vs := confusion | Unexpected late expansion | Use := by default | | Unexported vars | Subprocesses don't see vars | export VAR | | Complex shell in make | Hard to maintain | Move to external script |


Quick Reference

# Makefile Template
.DEFAULT_GOAL := help
SHELL := /bin/bash
.SHELLFLAGS := -ec

.PHONY: help install test lint format clean

help: ## Show this help
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "  %-15s %s\n", $$1, $$2}'

install: ## Install dependencies
	uv sync --extra dev

test: ## Run tests
	uv run pytest tests/ -v

lint: ## Run linters
	uv run ruff check .

format: ## Format code
	uv run ruff format .

clean: ## Clean artifacts
	rm -rf build/ dist/ .pytest_cache

Links

See Also