Back to skills
SkillHub ClubShip Full StackFull Stack

makefile

GNU Make automation and build system guidance

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
10
Hot score
84
Updated
March 20, 2026
Overall rating
C1.9
Composite score
1.9
Best-practice grade
C62.8

Install command

npx @skill-hub/cli install itechmeat-llm-code-makefile
automationbuild-systemmakefilecodingproductivity

Repository

itechmeat/llm-code

Skill path: skills/makefile

GNU Make automation and build system guidance

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: itechmeat.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install makefile into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/itechmeat/llm-code before adding makefile to shared team environments
  • Use makefile for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: makefile
description: GNU Make automation and build system guidance
version: 2.0.0
release_date: "2023-02-26"
---

# Makefile Skill

Guidance for creating and maintaining GNU Make build automation.

## Quick Navigation

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

---

## Core Concepts

### Rule Structure

```makefile
target: prerequisites
        recipe
```

**Critical:** Recipe lines MUST start with TAB character.

### File vs Phony Targets

```makefile
# 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  |

```makefile
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

```makefile
.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

```makefile
UNAME_S := $(shell uname -s)

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

### Build Directory

```makefile
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

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

test:
	pytest tests/  # sees exported variables
```

---

## Common Targets

### Quality Checks

```makefile
.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

```makefile
.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

```makefile
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

```makefile
# 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

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

### Parallel Execution

```bash
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.

```makefile
# ❌ 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

```makefile
DEBUG ?= 0

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

ifdef CI
    TEST_FLAGS := --ci
endif
```

---

## Including Files

```makefile
# 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
# 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
```

---

## See Also

- [GNU Make Manual](https://www.gnu.org/software/make/manual/make.html)
- [patterns.md](references/patterns.md) - Extended patterns and recipes


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/syntax.md

```markdown
# Makefile Syntax Reference

> Source: [GNU Make Manual - Writing Rules](https://www.gnu.org/software/make/manual/make.html#Rules)

## Rule Structure

Basic rule format:

```makefile
target … : prerequisites …
        recipe
        …
```

Alternative with semicolon:

```makefile
target : prerequisites ; recipe
        recipe
        …
```

**Critical:** Recipe lines MUST start with a TAB character, not spaces.

## Targets

- Usually file names to be generated (e.g., `main.o`, `program`)
- Can be action names (e.g., `clean`, `install`)
- First target in makefile becomes default goal
- Targets starting with `.` are not default unless contain `/`

Multiple targets in one rule:

```makefile
# Independent targets (each built separately)
prog1 prog2 : utils.o
        cc -o $@ $^

# Grouped targets (recipe creates all at once)
foo.h foo.c &: foo.y
        bison -d $<
```

## Prerequisites

**Normal prerequisites** - trigger rebuild if newer than target:

```makefile
main.o : main.c defs.h
```

**Order-only prerequisites** - must exist, but don't trigger rebuild:

```makefile
$(OBJDIR)/%.o : %.c | $(OBJDIR)
        $(CC) -c $< -o $@

$(OBJDIR):
        mkdir -p $@
```

Syntax: `target : normal-prereqs | order-only-prereqs`

## Prerequisite Types Summary

| Type              | Triggers Rebuild? | Use Case                 |
| ----------------- | ----------------- | ------------------------ |
| Normal (`:`)      | Yes, if newer     | Source files, headers    |
| Order-only (`\|`) | No                | Directories, setup tasks |

## Wildcards

Wildcard characters: `*`, `?`, `[…]`, `~`

```makefile
# In targets and prerequisites - expanded by Make
clean:
        rm -f *.o

print: *.c
        lpr -p $?
        touch print

# In variables - NOT expanded automatically
objects = *.o           # Literal "*.o"
objects := $(wildcard *.o)  # Expanded list
```

## VPATH - Directory Search

Search directories for prerequisites:

```makefile
# Search all prerequisites in these dirs
VPATH = src:../headers

# Pattern-specific search
vpath %.c src
vpath %.h ../headers
vpath %   foo:bar      # Match anything
vpath %.c              # Clear pattern search
vpath                  # Clear all vpath
```

## Double-Colon Rules

Independent rules for same target:

```makefile
newfile:: file1
        recipe1

newfile:: file2
        recipe2
```

Each rule executed if its prerequisites are newer.

## Static Pattern Rules

Apply pattern to explicit list:

```makefile
objects = foo.o bar.o

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@
```

Format: `targets : target-pattern: prereq-pattern`

## Multiple Rules for One Target

Prerequisites merge; only one recipe allowed:

```makefile
# OK - prerequisites merge
objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h

# ERROR - multiple recipes
foo.o : foo.c
        $(CC) -c foo.c
foo.o : foo.c         # Warning: overriding recipe
        gcc -c foo.c
```

## Automatic Variables

| Variable         | Meaning                             |
| ---------------- | ----------------------------------- |
| `$@`             | Target file name                    |
| `$<`             | First prerequisite                  |
| `$^`             | All prerequisites (deduplicated)    |
| `$+`             | All prerequisites (with duplicates) |
| `$?`             | Prerequisites newer than target     |
| `$*`             | Stem in pattern rules               |
| `$%`             | Archive member name                 |
| `$(@D)`, `$(@F)` | Directory and file parts of `$@`    |

## Line Continuation

```makefile
# Long lines
objects = main.o foo.o bar.o \
          baz.o qux.o

# In recipes (each logical line runs in separate shell)
target:
        cd subdir && \
        $(MAKE)
```

## Comments

```makefile
# This is a comment
target: prereq  # End-of-line comment

# Comments in recipes are passed to shell
recipe:
        # Shell sees this comment
        echo "hello"
```

## Escaping Special Characters

```makefile
# Dollar sign
price = $$100           # Becomes "$100"
shell_var = $$HOME      # Shell variable, not Make variable

# Percent in pattern
files = a%b             # Literal % in file name
%.txt: $$(files)        # Use secondary expansion

# Hash/pound sign in variable value
hash := \#              # Must escape
comment := text$(hash)more
```

## Secondary Expansion

Enable late prerequisite expansion:

```makefile
.SECONDEXPANSION:

main_SRCS := main.c utils.c
lib_SRCS := lib.c api.c

main lib: $$(patsubst %.c,%.o,$$($$@_SRCS))
```

## Best Practices

1. **First target = default goal** - Make it `all` or `help`
2. **Use variables for file lists** - Easier maintenance
3. **Explicit prerequisites** - Don't rely on implicit rules alone
4. **Group related rules** - Organize by feature/component
5. **Document with comments** - Especially non-obvious dependencies

```

### references/variables.md

```markdown
# Makefile Variables Reference

> Source: [GNU Make Manual - How to Use Variables](https://www.gnu.org/software/make/manual/make.html#Using-Variables)

## Variable Reference Syntax

```makefile
$(variable)     # Standard form
${variable}     # Also valid
$x              # Single-character variable (discouraged)
```

## Assignment Operators

### Recursive (`=`) - Lazy Evaluation

Expanded each time referenced:

```makefile
foo = $(bar)
bar = hello
# $(foo) expands to "hello"

bar = world
# $(foo) now expands to "world"
```

**Use for:** Dynamic values, computed at use time.

**Warning:** Can cause infinite loops:

```makefile
CFLAGS = $(CFLAGS) -Wall  # ERROR: infinite recursion
```

### Simply Expanded (`:=` or `::=`) - Immediate Evaluation

Expanded once at definition:

```makefile
x := foo
y := $(x) bar
# y is "foo bar"

x := later
# y is still "foo bar"
```

**Use for:** Constants, one-time computations, avoiding recursion.

### Immediately Expanded (`:::=`) - GNU Make 4.4+

Like `:=` but in POSIX mode:

```makefile
x :::= $(shell date)
```

### Conditional (`?=`) - Set If Undefined

Only sets if variable has no value:

```makefile
CC ?= gcc        # Use gcc unless CC already set
DEBUG ?= 0       # Default to 0
```

**Use for:** Defaults, allowing command-line overrides.

### Append (`+=`)

Add to existing value:

```makefile
CFLAGS := -Wall
CFLAGS += -O2
# CFLAGS is "-Wall -O2"
```

Behavior depends on original assignment type:

- If originally `=`, append uses `=`
- If originally `:=`, append uses `:=`

### Shell Assignment (`!=`)

Assign command output:

```makefile
hash != git rev-parse --short HEAD
# Same as: hash := $(shell git rev-parse --short HEAD)
```

## Expansion Timing Summary

| Operator | When Expanded         | Typical Use                     |
| -------- | --------------------- | ------------------------------- |
| `=`      | Each use              | Dynamic values, late binding    |
| `:=`     | Definition time       | Constants, one-time shell calls |
| `?=`     | Definition (if unset) | Overridable defaults            |
| `+=`     | Depends on original   | Building up lists               |
| `!=`     | Definition time       | Shell command output            |

## Multi-Line Variables

```makefile
define run-tests =
echo "Running tests..."
pytest tests/
echo "Tests complete"
endef

test:
        $(run-tests)
```

To prevent expansion, use `$$`:

```makefile
define script
for i in 1 2 3; do \
    echo $$i; \
done
endef
```

## Undefining Variables

```makefile
foo := bar
undefine foo
# $(foo) is now empty
```

## Variable Scope

### Global Variables

Default scope - accessible everywhere:

```makefile
CC := gcc
```

### Target-Specific Variables

Set for one target and its prerequisites:

```makefile
debug: CFLAGS += -g -DDEBUG
debug: all

release: CFLAGS += -O3
release: all
```

### Pattern-Specific Variables

Set for targets matching pattern:

```makefile
%.o: CFLAGS += -MMD
```

### Private Variables

Don't inherit to prerequisites:

```makefile
all: private CFLAGS := -Wall
all: prog
# prog does NOT see CFLAGS from all
```

## Environment Variables

Make reads environment variables as defaults:

```makefile
# $HOME from environment if not set in Makefile
homedir := $(HOME)

# Override environment (use -e to reverse)
PATH := /custom/bin:$(PATH)
```

Command-line overrides everything:

```bash
make CFLAGS="-O3"  # Overrides Makefile and environment
```

## Exporting Variables

Pass to sub-makes and shell commands:

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

# Export all variables
.EXPORT_ALL_VARIABLES:
```

Unexport:

```makefile
unexport CDPATH
```

## Override Directive

Allow Makefile to override command-line:

```makefile
override CFLAGS += -Wall  # Add even if CFLAGS set on command line
```

## Substitution References

Replace suffix in variable:

```makefile
sources := foo.c bar.c
objects := $(sources:.c=.o)
# objects is "foo.o bar.o"

# Pattern form
$(sources:%.c=%.o)
```

## Computed Variable Names

Variable name from variable:

```makefile
prog_srcs := main.c utils.c
lib_srcs := lib.c api.c

# Single indirection
sources := $($(target)_srcs)

# Example usage
target := prog
# $(sources) expands to main.c utils.c
```

## Special Variables

| Variable        | Meaning                           |
| --------------- | --------------------------------- |
| `MAKEFILE_LIST` | List of makefiles being parsed    |
| `MAKEFLAGS`     | Flags passed to make              |
| `.DEFAULT_GOAL` | Target if none specified          |
| `.RECIPEPREFIX` | Recipe line prefix (default: TAB) |
| `.VARIABLES`    | All defined variable names        |
| `.FEATURES`     | List of make features             |
| `CURDIR`        | Current working directory         |
| `MAKE`          | Path to make executable           |
| `MAKELEVEL`     | Recursion depth                   |
| `MAKECMDGOALS`  | Command line goals                |

## Automatic Variables

See [syntax.md](syntax.md#automatic-variables) for complete list.

## Best Practices

1. **Use `:=` by default** - Predictable, efficient
2. **Use `?=` for overridable settings** - CLI flexibility
3. **Avoid recursive `=` unless needed** - Prevents surprises
4. **Export only what's needed** - Don't pollute subprocess environment
5. **Document non-obvious variables** - Especially computed ones
6. **Use UPPERCASE for configuration** - `DEBUG`, `VERBOSE`
7. **Use lowercase for internal** - `sources`, `objects`

```

### references/functions.md

```markdown
# Makefile Functions Reference

> Source: [GNU Make Manual - Functions for Transforming Text](https://www.gnu.org/software/make/manual/make.html#Functions)

## Function Call Syntax

```makefile
$(function arguments)
${function arguments}
```

Arguments separated by commas. Spaces after commas are significant.

## String Functions

### subst - Simple Substitution

```makefile
$(subst from,to,text)

# Example
$(subst ee,EE,feet on the street)
# Result: "fEEt on the strEEt"
```

### patsubst - Pattern Substitution

```makefile
$(patsubst pattern,replacement,text)

# Example
$(patsubst %.c,%.o,foo.c bar.c)
# Result: "foo.o bar.o"

# Shorthand for variables
$(var:pattern=replacement)
$(sources:.c=.o)
```

### strip - Remove Whitespace

```makefile
$(strip string)

# Example
$(strip   a  b   c   )
# Result: "a b c"
```

### findstring - Find Substring

```makefile
$(findstring find,in)

# Returns find if found, empty otherwise
$(findstring a,abc)      # "a"
$(findstring x,abc)      # ""
```

### filter - Select Matching Words

```makefile
$(filter pattern...,text)

# Example
sources := foo.c bar.c baz.s qux.h
c_sources := $(filter %.c,$(sources))
# Result: "foo.c bar.c"
```

### filter-out - Remove Matching Words

```makefile
$(filter-out pattern...,text)

# Example
objects := main.o foo.o test.o
prod_objects := $(filter-out test.o,$(objects))
# Result: "main.o foo.o"
```

### sort - Sort and Deduplicate

```makefile
$(sort list)

# Example
$(sort foo bar baz foo bar)
# Result: "bar baz foo"
```

### word - Select Nth Word

```makefile
$(word n,text)

# Example
$(word 2,foo bar baz)
# Result: "bar"
```

### wordlist - Select Word Range

```makefile
$(wordlist start,end,text)

# Example
$(wordlist 2,4,a b c d e f)
# Result: "b c d"
```

### words - Count Words

```makefile
$(words text)

# Example
$(words foo bar baz)
# Result: "3"
```

### firstword / lastword

```makefile
$(firstword text)
$(lastword text)

# Example
$(firstword foo bar baz)  # "foo"
$(lastword foo bar baz)   # "baz"
```

## File Name Functions

### dir - Directory Part

```makefile
$(dir names...)

# Example
$(dir src/foo.c hacks)
# Result: "src/ ./"
```

### notdir - File Name Part

```makefile
$(notdir names...)

# Example
$(notdir src/foo.c hacks)
# Result: "foo.c hacks"
```

### suffix - File Suffix

```makefile
$(suffix names...)

# Example
$(suffix src/foo.c src-1.0/bar.c hacks)
# Result: ".c .c"
```

### basename - Remove Suffix

```makefile
$(basename names...)

# Example
$(basename src/foo.c src-1.0/bar.c hacks)
# Result: "src/foo src-1.0/bar hacks"
```

### addsuffix - Add Suffix

```makefile
$(addsuffix suffix,names...)

# Example
$(addsuffix .c,foo bar)
# Result: "foo.c bar.c"
```

### addprefix - Add Prefix

```makefile
$(addprefix prefix,names...)

# Example
$(addprefix src/,foo bar)
# Result: "src/foo src/bar"
```

### join - Pairwise Join

```makefile
$(join list1,list2)

# Example
$(join a b,1 2 3)
# Result: "a1 b2 3"
```

### wildcard - Glob Expansion

```makefile
$(wildcard pattern)

# Example
$(wildcard *.c)          # All .c files
$(wildcard src/*.c)      # All .c in src/
$(wildcard src/*/*.c)    # .c in src subdirs
```

### realpath / abspath

```makefile
$(realpath names...)     # Canonical path (resolves symlinks)
$(abspath names...)      # Absolute path (no symlink resolution)
```

## Conditional Functions

### if - Conditional

```makefile
$(if condition,then-part)
$(if condition,then-part,else-part)

# Example
$(if $(DEBUG),debug-flags,release-flags)
```

### or - First Non-Empty

```makefile
$(or condition1,condition2,...)

# Example
CC := $(or $(CC),gcc)  # Use CC if set, else gcc
```

### and - All Non-Empty

```makefile
$(and condition1,condition2,...)

# Example
$(and $(CC),$(CFLAGS),ready)
# "ready" only if both CC and CFLAGS set
```

## Loop Functions

### foreach - Iterate Over List

```makefile
$(foreach var,list,text)

# Example
dirs := a b c
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*.c))
```

### call - User-Defined Functions

```makefile
$(call function,arg1,arg2,...)

# Define function
reverse = $(2) $(1)

# Use it
$(call reverse,a,b)
# Result: "b a"
```

Inside function: `$(0)` is function name, `$(1)`, `$(2)` are arguments.

### let - Local Variables (GNU Make 4.4+)

```makefile
$(let var1 var2 ...,value1 value2 ...,text)

# Example
$(let a b,1 2,$(a) and $(b))
# Result: "1 and 2"
```

## Shell and Control Functions

### shell - Execute Command

```makefile
$(shell command)

# Example
git_hash := $(shell git rev-parse --short HEAD)
today := $(shell date +%Y-%m-%d)
```

### eval - Parse as Makefile

```makefile
$(eval text)

# Example - generate rules dynamically
define PROGRAM_template
$(1): $$($(1)_OBJS)
	$$(CC) -o $$@ $$^
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
```

### value - Unexpanded Value

```makefile
$(value variable)

# Example
FOO = $$PATH
# $(FOO) expands $PATH
# $(value FOO) returns "$$PATH"
```

### origin - Variable Origin

```makefile
$(origin variable)

# Returns: undefined, default, environment, environment override,
#          file, command line, override, automatic
```

### flavor - Variable Type

```makefile
$(flavor variable)

# Returns: undefined, recursive, simple
```

## Control Functions

### error - Abort with Message

```makefile
$(error text)

# Example
ifndef JAVA_HOME
$(error JAVA_HOME is not set)
endif
```

### warning - Print Warning

```makefile
$(warning text)

# Example
ifeq ($(DEBUG),1)
$(warning Building in debug mode)
endif
```

### info - Print Message

```makefile
$(info text)

# Example
$(info Building version $(VERSION))
```

## File Function

Read/write files directly:

```makefile
# Read file
$(file <filename)

# Write to file (overwrite)
$(file >filename,text)

# Append to file
$(file >>filename,text)
```

## Common Patterns

### Building file lists

```makefile
sources := $(wildcard src/*.c)
objects := $(patsubst src/%.c,build/%.o,$(sources))
```

### Conditional compilation flags

```makefile
CFLAGS := -Wall
CFLAGS += $(if $(DEBUG),-g -O0,-O2)
```

### Dynamic rule generation

```makefile
PROGRAMS := prog1 prog2

define make-program
$(1): $($(1)_OBJS)
	$$(CC) -o $$@ $$^
endef

$(foreach prog,$(PROGRAMS),$(eval $(call make-program,$(prog))))
```

### Safe file existence check

```makefile
ifneq ($(wildcard config.mk),)
include config.mk
endif
```

```

### references/targets.md

```markdown
# Makefile Special Targets Reference

> Source: [GNU Make Manual - Special Built-in Target Names](https://www.gnu.org/software/make/manual/make.html#Special-Targets)

## Phony Targets

Targets that don't represent files:

```makefile
.PHONY: clean test install all

clean:
        rm -rf build/

test:
        pytest tests/
```

**Why use .PHONY:**

1. Prevents confusion with actual files named `clean`, `test`, etc.
2. Improves performance (skips file existence check)
3. Recipe always runs when target is requested

```makefile
# Without .PHONY, if file named "clean" exists,
# "make clean" says "clean is up to date"

# With .PHONY: clean, recipe always runs
```

### Phony Target Dependencies

```makefile
.PHONY: all clean test

# Phony with dependencies
all: prog1 prog2 prog3

# Chained phony targets
cleanall: cleanobj cleandiff
        rm program

cleanobj:
        rm *.o

cleandiff:
        rm *.diff
```

**Note:** Phoniness is NOT inherited. Prerequisites of phony targets are not automatically phony.

## Special Built-in Targets

### .PHONY

```makefile
.PHONY: target1 target2 ...
```

Declare targets that are not files.

### .SUFFIXES

```makefile
.SUFFIXES:            # Clear default suffixes
.SUFFIXES: .c .o .h   # Set custom suffix list
```

Controls old-style suffix rules.

### .DEFAULT

```makefile
.DEFAULT:
        @echo "No rule for $@"
```

Recipe for targets with no rules.

### .PRECIOUS

```makefile
.PRECIOUS: %.o intermediate.txt
```

Don't delete these targets on interrupt or as intermediates.

### .INTERMEDIATE

```makefile
.INTERMEDIATE: intermediate.o
```

Mark as intermediate (delete after use).

### .NOTINTERMEDIATE

```makefile
.NOTINTERMEDIATE: important.o
.NOTINTERMEDIATE:    # All targets not intermediate
```

Prevent intermediate file deletion.

### .SECONDARY

```makefile
.SECONDARY: generated.c
.SECONDARY:          # All targets are secondary
```

Like intermediate but never auto-deleted.

### .SECONDEXPANSION

```makefile
.SECONDEXPANSION:

prog: $$(prog_OBJS)
```

Enable secondary expansion of prerequisites.

### .DELETE_ON_ERROR

```makefile
.DELETE_ON_ERROR:
```

Delete target if recipe fails.

### .IGNORE

```makefile
.IGNORE: cleanup      # Ignore errors in cleanup target
.IGNORE:              # Ignore all errors (discouraged)
```

### .LOW_RESOLUTION_TIME

```makefile
.LOW_RESOLUTION_TIME: dst
dst: src
        cp -p src dst
```

For commands that can't preserve sub-second timestamps.

### .SILENT

```makefile
.SILENT: install      # Don't echo install recipe
.SILENT:              # Silent mode for all (discouraged)
```

### .EXPORT_ALL_VARIABLES

```makefile
.EXPORT_ALL_VARIABLES:
```

Export all variables to sub-processes.

### .NOTPARALLEL

```makefile
.NOTPARALLEL:         # Disable parallel for entire make
.NOTPARALLEL: target  # Disable parallel for target's prereqs
```

### .ONESHELL

```makefile
.ONESHELL:

target:
        cd subdir
        pwd           # Still in subdir!
        make
```

Run entire recipe in single shell invocation.

### .POSIX

```makefile
.POSIX:
```

Enable POSIX-conforming behavior.

### .WAIT

```makefile
# Force sequential execution in parallel builds
all: dep1 .WAIT dep2 .WAIT dep3
```

## Pattern Rules

Define how to build file types:

```makefile
# Implicit rule: .c to .o
%.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

# Multiple targets (grouped)
%.tab.c %.tab.h: %.y
        bison -d $<
```

### Pattern Rule Automatic Variables

| Variable | Meaning             |
| -------- | ------------------- |
| `$@`     | Target              |
| `$<`     | First prerequisite  |
| `$^`     | All prerequisites   |
| `$*`     | Stem (matched by %) |

### Canceling Implicit Rules

```makefile
# Cancel built-in .c.o rule
%.o: %.c
```

Empty recipe with pattern cancels that implicit rule.

## Static Pattern Rules

Apply pattern to explicit target list:

```makefile
objects := foo.o bar.o baz.o

$(objects): %.o: %.c
        $(CC) -c $< -o $@
```

## Double-Colon Rules

Multiple independent rules for same target:

```makefile
# Each rule runs if its prereqs are newer
file.txt:: source1.txt
        cat source1.txt >> file.txt

file.txt:: source2.txt
        cat source2.txt >> file.txt
```

## Empty Targets (Timestamps)

Record when action was last performed:

```makefile
print: *.c
        lpr -p $?
        touch print
```

## Force Targets

Always out-of-date:

```makefile
clean: FORCE
        rm *.o

FORCE:

# Equivalent to:
.PHONY: clean
clean:
        rm *.o
```

## Target-Specific Variables

```makefile
debug: CFLAGS += -g -DDEBUG
debug: all

release: CFLAGS += -O3 -DNDEBUG
release: all

# Pattern-specific
%.debug.o: CFLAGS += -g
```

## Common Target Conventions

| Target      | Purpose                            |
| ----------- | ---------------------------------- |
| `all`       | Build everything (usually default) |
| `clean`     | Remove generated files             |
| `install`   | Install to system                  |
| `uninstall` | Remove from system                 |
| `test`      | Run tests                          |
| `check`     | Alias for test                     |
| `dist`      | Create distribution archive        |
| `distclean` | Clean + remove configure artifacts |
| `help`      | Show available targets             |

## Best Practices

1. **Always use .PHONY** for non-file targets
2. **Make `help` or `all` the default** (first target)
3. **Group .PHONY declarations** at top of file
4. **Use standard target names** for common operations
5. **Document targets** with `## comment` for help generation

```makefile
.PHONY: all clean test install help

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

```

### references/recipes.md

```markdown
# Makefile Recipe Execution Reference

> Source: [GNU Make Manual - Writing Recipes in Rules](https://www.gnu.org/software/make/manual/make.html#Recipes)

## Recipe Basics

Recipes are shell commands that update targets.

```makefile
target: prerequisites
        command1
        command2
```

**Critical:** Recipe lines MUST begin with TAB character (not spaces).

Alternative prefix:

```makefile
.RECIPEPREFIX := >

target:
> echo "Using > as prefix"
```

## Recipe Execution Model

Each line runs in a **separate shell**:

```makefile
# Each line is independent shell
target:
        cd subdir
        pwd          # Still in original directory!

# Combine with && or \
target:
        cd subdir && pwd

target:
        cd subdir; \
        pwd; \
        make
```

### .ONESHELL Mode

Run entire recipe in single shell:

```makefile
.ONESHELL:

target:
        cd subdir
        pwd          # Now in subdir!
        for f in *.c; do
                echo $$f
        done
```

## Recipe Echoing

By default, commands are printed before execution.

```makefile
# @ suppresses echo
target:
        @echo "This prints, but command doesn't"

# -s / --silent flag suppresses all echoing
# make -s target

# .SILENT target
.SILENT: quiet-target
quiet-target:
        echo "Command not shown"
```

## Error Handling

### Default: Stop on Error

Make stops if any command returns non-zero:

```makefile
target:
        command1     # If fails, stops here
        command2     # Never runs if command1 fails
```

### Ignore Errors

```makefile
# - prefix ignores error
clean:
        -rm -f *.o   # Continue even if no .o files

# -i flag ignores all errors
# make -i target

# .IGNORE target
.IGNORE: risky-target
```

### Continue on Error

```makefile
# -k / --keep-going: continue other targets
# make -k all
```

## Shell Selection

Default shell is `/bin/sh`:

```makefile
SHELL := /bin/bash

# Enable bash features
target:
        [[ -f file.txt ]] && echo "exists"
```

### Shell Flags

```makefile
.SHELLFLAGS := -ec

# -e: exit on first error
# -c: execute string (required)
```

## Parallel Execution

```makefile
# Run with -j flag
# make -j4          # 4 parallel jobs
# make -j           # Unlimited parallel

# Control parallelism in Makefile
.NOTPARALLEL:       # Disable for entire make
.NOTPARALLEL: target # Disable for target's prereqs

# Force order with .WAIT
all: setup .WAIT build .WAIT test
```

### Parallel Output

```makefile
# Output synchronization (GNU Make 4.0+)
# make -j4 -O       # Group output by target
# make -j4 -Oline   # Group by line
```

## Recursive Make

Call make from make:

```makefile
# Always use $(MAKE), not "make"
subsystem:
        $(MAKE) -C subdir

# Equivalent
subsystem:
        cd subdir && $(MAKE)

# Pass variables
subsystem:
        $(MAKE) -C subdir CFLAGS="$(CFLAGS)"
```

### Export Variables to Sub-make

```makefile
export CFLAGS
export CC

# Or export all
.EXPORT_ALL_VARIABLES:

subsystem:
        $(MAKE) -C subdir
```

### MAKEFLAGS

Flags automatically passed to sub-makes:

```makefile
# -j, -k, -s, etc. are inherited

# Add flags
MAKEFLAGS += --no-print-directory
```

## Canned Recipes

Reusable recipe blocks:

```makefile
define compile-cmd
$(CC) $(CFLAGS) -c $< -o $@
endef

%.o: %.c
        $(compile-cmd)
```

With multiple commands:

```makefile
define run-tests =
echo "Running tests..."
pytest tests/
echo "Done"
endef

test:
        $(run-tests)
```

## Variables in Recipes

Make variables vs shell variables:

```makefile
target:
        echo $(MAKE_VAR)      # Make variable
        echo $$SHELL_VAR      # Shell variable
        echo $$HOME           # Environment variable

# Loop with shell variable
target:
        for i in 1 2 3; do \
                echo $$i; \
        done
```

## Empty Recipes

Explicitly do nothing:

```makefile
target: ;

# Or
target:
        @:    # : is shell no-op
```

Use to:

- Prevent implicit rule search
- Override inherited recipes
- Mark file as always up-to-date

## Common Recipe Patterns

### Check command exists

```makefile
require-docker:
        @command -v docker >/dev/null 2>&1 || \
                (echo "Error: docker not found" && exit 1)
```

### Conditional execution

```makefile
target:
        @if [ -f config.mk ]; then \
                echo "Config found"; \
        else \
                echo "Using defaults"; \
        fi
```

### Loop over files

```makefile
process-all:
        @for f in $(SOURCES); do \
                echo "Processing $$f"; \
                process $$f; \
        done
```

### Create directory if missing

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

$(BUILDDIR):
        mkdir -p $@
```

### Atomic file creation

```makefile
# Write to temp, then move (safe update)
output.txt: input.txt
        process $< > [email protected]
        mv [email protected] $@
```

## Recipe Debugging

```makefile
# Dry run - show commands without executing
# make -n target

# Print database
# make -p

# Debug mode
# make -d target
# make --debug=all target
```

## Best Practices

1. **Use `@` for cosmetic echo** - Don't show echo commands
2. **Use `-` sparingly** - Only for truly optional commands
3. **Use `$(MAKE)` for recursion** - Preserves flags
4. **Combine commands with `&&`** - Fail fast
5. **Use `.ONESHELL` for complex scripts** - Or move to external script
6. **Export only needed variables** - Don't pollute environment
7. **Test recipes with `-n`** - Verify before running

```

### references/implicit.md

```markdown
# Makefile Implicit Rules Reference

> Source: [GNU Make Manual - Using Implicit Rules](https://www.gnu.org/software/make/manual/make.html#Implicit-Rules)

## What Are Implicit Rules?

Built-in rules that make knows how to build common file types:

```makefile
# You write:
prog: main.o utils.o
        $(CC) -o $@ $^

# Make automatically knows:
# main.o comes from main.c using cc -c
# utils.o comes from utils.c using cc -c
```

## Common Built-in Rules

### C Compilation

```makefile
# .c → .o
%.o: %.c
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

# .c → executable (single file)
%: %.c
        $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@
```

### C++ Compilation

```makefile
# .cc/.cpp/.C → .o
%.o: %.cc
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
```

### Other Languages

```makefile
# Fortran: .f → .o
# Pascal: .p → .o
# Assembly: .s → .o
# Yacc: .y → .c
# Lex: .l → .c
```

## Variables Used by Implicit Rules

### Program Names

| Variable | Default | Purpose      |
| -------- | ------- | ------------ |
| `CC`     | `cc`    | C compiler   |
| `CXX`    | `g++`   | C++ compiler |
| `AS`     | `as`    | Assembler    |
| `AR`     | `ar`    | Archiver     |
| `LEX`    | `lex`   | Lex          |
| `YACC`   | `yacc`  | Yacc         |
| `RM`     | `rm -f` | File removal |

### Flags

| Variable   | Purpose                       |
| ---------- | ----------------------------- |
| `CFLAGS`   | C compiler flags              |
| `CXXFLAGS` | C++ compiler flags            |
| `CPPFLAGS` | C preprocessor flags (-I, -D) |
| `LDFLAGS`  | Linker flags (-L)             |
| `LDLIBS`   | Libraries (-l)                |
| `ARFLAGS`  | Archiver flags                |

### Customize Built-in Rules

```makefile
CC := gcc
CFLAGS := -Wall -O2
CPPFLAGS := -I./include -DDEBUG
LDFLAGS := -L./lib
LDLIBS := -lm -lpthread
```

## Pattern Rules

Define custom implicit rules:

```makefile
# Pattern rule syntax
%.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

# Multiple prerequisites
%.o: %.c %.h
        $(CC) -c $(CFLAGS) $< -o $@

# Multiple targets (grouped)
%.tab.c %.tab.h: %.y
        bison -d $<
```

### Pattern Rule Variables

| Variable | Meaning                  |
| -------- | ------------------------ |
| `$@`     | Target file              |
| `$<`     | First prerequisite       |
| `$^`     | All prerequisites        |
| `$*`     | Stem (part matched by %) |
| `$(@D)`  | Directory of target      |
| `$(@F)`  | File part of target      |

### Stem Matching

```makefile
# Rule: %.o: %.c
# Target: src/foo.o
# $* = src/foo (the stem)
# Looking for: src/foo.c
```

## Static Pattern Rules

Apply pattern to explicit list of targets:

```makefile
objects := foo.o bar.o baz.o

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@
```

Advantage: More control, faster matching, no accidental matches.

### With Filter

```makefile
files := foo.c bar.c baz.s qux.h

$(filter %.o,$(files:.c=.o)): %.o: %.c
        $(CC) -c $< -o $@
```

## Suffix Rules (Old Style)

Legacy syntax, prefer pattern rules:

```makefile
# Double-suffix: .c → .o
.c.o:
        $(CC) -c $<

# Single-suffix: anything → .c
.c:
        $(CC) $< -o $@

# Define suffixes
.SUFFIXES: .c .o .h
```

## Implicit Rule Search

How Make finds applicable rules:

1. Look for explicit rule with recipe
2. Look for pattern rule matching target
3. Check if prerequisites exist or can be made
4. First applicable rule wins

### Rule Ordering

Rules are tried in order:

1. Pattern rules defined in makefile
2. Built-in rules
3. Match-anything rules (%)

### Canceling Rules

```makefile
# Cancel specific implicit rule
%.o: %.c

# Cancel all built-in rules
.SUFFIXES:
MAKEFLAGS += -r
```

## Chains of Implicit Rules

Make can chain rules:

```makefile
# Given: foo.y
# Make can: foo.y → foo.c → foo.o → foo

# Intermediate files are auto-deleted
# Mark as precious to keep:
.PRECIOUS: %.c
```

### .INTERMEDIATE and .SECONDARY

```makefile
# Explicitly mark as intermediate
.INTERMEDIATE: generated.c

# Keep intermediate (don't delete)
.SECONDARY: generated.c
.SECONDARY:              # Keep all
```

## Match-Anything Rules

Pattern with just `%`:

```makefile
# Last-resort rule
%:
        @echo "Don't know how to make $@"

# Terminal rule (can't chain further)
%:: default-recipe
```

## Automatic Prerequisites

Generate dependencies automatically:

```makefile
# Generate .d files with gcc
%.d: %.c
        $(CC) -MM $(CPPFLAGS) $< > $@

# Include generated dependencies
-include $(sources:.c=.d)
```

Modern approach:

```makefile
CFLAGS += -MMD -MP
-include $(objects:.o=.d)
```

## Common Custom Rules

### Documentation

```makefile
%.html: %.md
        pandoc -o $@ $<

%.pdf: %.tex
        pdflatex $<
```

### Archive Files

```makefile
%.tar.gz: %
        tar czf $@ $<

%.zip: %
        zip -r $@ $<
```

### Code Generation

```makefile
%.pb.go: %.proto
        protoc --go_out=. $<

%_generated.py: %.yaml
        python generate.py $< > $@
```

## Best Practices

1. **Prefer pattern rules over suffix rules** - More readable
2. **Use static patterns for explicit lists** - Faster, clearer
3. **Set implicit variables** - `CC`, `CFLAGS`, etc.
4. **Generate dependencies automatically** - `-MMD -MP`
5. **Cancel unused implicit rules** - Performance
6. **Mark precious intermediates** - If you need them

```makefile
# Optimized implicit rule setup
MAKEFLAGS += -rR          # Disable built-in rules
.SUFFIXES:                # Clear suffix list

CC := gcc
CFLAGS := -Wall -O2 -MMD -MP

%.o: %.c
        $(CC) $(CFLAGS) -c $< -o $@

-include $(objects:.o=.d)
```

```

### references/patterns.md

```markdown
# Common Makefile Patterns

> Practical patterns for modern development workflows.

## Project Setup Pattern

```makefile
# Header
.DEFAULT_GOAL := help
SHELL := /bin/bash
.SHELLFLAGS := -ec

# Phony declarations (group at top)
.PHONY: all help install test lint format clean

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

## Platform Detection

```makefile
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Linux)
    PLATFORM := linux
    OPEN_CMD := xdg-open
endif
ifeq ($(UNAME_S),Darwin)
    PLATFORM := macos
    OPEN_CMD := open
endif
ifeq ($(OS),Windows_NT)
    PLATFORM := windows
    OPEN_CMD := start
endif
```

## Tool Detection

```makefile
HAS_DOCKER := $(shell command -v docker 2>/dev/null)
HAS_UV := $(shell command -v uv 2>/dev/null)

install:
ifdef HAS_UV
	uv sync
else
	pip install -r requirements.txt
endif

require-docker:
	@command -v docker >/dev/null 2>&1 || \
		(echo "Error: docker required" && exit 1)
```

## Version Management

```makefile
# From pyproject.toml
VERSION := $(shell grep -m1 version pyproject.toml | cut -d'"' -f2)

# From git
VERSION := $(shell git describe --tags --always)

# From file
VERSION := $(shell cat VERSION)

build: ## Build version $(VERSION)
	@echo "Building $(VERSION)"
```

## Python/UV Project

```makefile
UV := uv
PYTHON := python

.PHONY: install install-dev test lint format clean

install: ## Install production dependencies
	$(UV) sync

install-dev: ## Install with dev dependencies
	$(UV) sync --extra dev

test: ## Run tests
	$(UV) run pytest tests/ -v

lint: ## Run linters
	$(UV) run ruff check .
	$(UV) run mypy src/

format: ## Format code
	$(UV) run ruff format .

clean: ## Remove artifacts
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
	rm -rf .pytest_cache .mypy_cache .ruff_cache htmlcov .coverage
```

## Node.js Project

```makefile
NPM := npm
NODE := node

.PHONY: install test lint build clean

install: node_modules ## Install dependencies

node_modules: package.json
	$(NPM) install
	touch $@

test: ## Run tests
	$(NPM) test

lint: ## Run linter
	$(NPM) run lint

build: ## Build project
	$(NPM) run build

clean: ## Clean build artifacts
	rm -rf node_modules dist
```

## Docker Patterns

```makefile
IMAGE := myapp
TAG := $(VERSION)

.PHONY: docker-build docker-run docker-stop docker-clean

docker-build: ## Build Docker image
	docker build -t $(IMAGE):$(TAG) .
	docker tag $(IMAGE):$(TAG) $(IMAGE):latest

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

docker-stop: ## Stop container
	-docker stop $(IMAGE)
	-docker rm $(IMAGE)

docker-clean: docker-stop ## Remove images
	-docker rmi $(IMAGE):$(TAG)
	-docker rmi $(IMAGE):latest
```

## Build Directory Pattern

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

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

$(BUILDDIR):
	mkdir -p $@

clean:
	rm -rf $(BUILDDIR)
```

## Multi-Environment Configs

```makefile
ENV ?= development

ifeq ($(ENV),production)
    CFLAGS := -O3 -DNDEBUG
    DEBUG := 0
else ifeq ($(ENV),test)
    CFLAGS := -O0 -g -DTEST
    DEBUG := 1
else
    CFLAGS := -O0 -g -DDEBUG
    DEBUG := 1
endif

# Usage: make build ENV=production
```

## Verbose/Quiet Mode

```makefile
V ?= 0

ifeq ($(V),0)
    Q := @
    VFLAG :=
else
    Q :=
    VFLAG := -v
endif

compile:
	$(Q)$(CC) $(CFLAGS) -c $< -o $@

# Usage: make compile V=1
```

## Parallel Quality Checks

```makefile
.PHONY: check lint test typecheck

check: ## Run all checks in parallel
	$(MAKE) -j3 lint typecheck test

lint:
	ruff check .

typecheck:
	mypy src/

test:
	pytest tests/
```

## Dependency Chains

```makefile
.PHONY: deploy build test

# Deploy requires build, which requires tests
deploy: build ## Deploy to production
	./scripts/deploy.sh

build: test ## Build application
	python -m build

test: ## Run tests
	pytest tests/
```

## Colored Output

```makefile
# Colors (may not work on all terminals)
RED := \033[31m
GREEN := \033[32m
CYAN := \033[36m
RESET := \033[0m

info:
	@echo "$(CYAN)Building...$(RESET)"

success:
	@echo "$(GREEN)Done!$(RESET)"

error:
	@echo "$(RED)Failed!$(RESET)"
```

## Guard Patterns

```makefile
# Require variable to be set
guard-%:
	@if [ -z '${${*}}' ]; then \
		echo "ERROR: $* is not set"; \
		exit 1; \
	fi

deploy: guard-AWS_REGION guard-AWS_ACCOUNT_ID
	./deploy.sh
```

## File Generation Pattern

```makefile
# Generate file if missing
.env:
	cp .env.example .env
	@echo "Created .env - please configure"

# Always regenerate
.PHONY: config
config:
	envsubst < config.template > config.yaml
```

## Include Pattern

```makefile
# Main Makefile
-include .env           # Load environment
-include local.mk       # Local overrides
-include .devcontainer/Makefile  # DevContainer targets

# Include if exists, ignore if missing
ifneq ($(wildcard config.mk),)
include config.mk
endif
```

## Archive/Release Pattern

```makefile
DIST := dist
NAME := myproject-$(VERSION)

.PHONY: dist dist-clean

dist: dist-clean ## Create release archive
	mkdir -p $(DIST)/$(NAME)
	cp -r src/ docs/ README.md $(DIST)/$(NAME)/
	cd $(DIST) && tar czf $(NAME).tar.gz $(NAME)
	cd $(DIST) && zip -r $(NAME).zip $(NAME)

dist-clean:
	rm -rf $(DIST)
```

## Watch Pattern (requires entr/fswatch)

```makefile
.PHONY: watch watch-test

watch: ## Watch and rebuild
	find src -name '*.c' | entr -c make build

watch-test: ## Watch and run tests
	find src tests -name '*.py' | entr -c make test
```

## Output Discipline

```makefile
# ❌ Too verbose
start:
	@echo "Starting services..."
	docker compose up -d
	@echo "Waiting for health..."
	sleep 5
	@echo "Services started!"
	@echo "Done!"

# ✅ One line in, one line out
start: ## Start services
	@echo "Starting at http://localhost:8000 ..."
	@docker compose up -d
	@echo "Logs: docker compose logs -f"
```

```

makefile | SkillHub