📋 Cheat Sheets

Makefile Cheat Sheet — Targets, Variables, and Common Patterns


Click any item to expand the explanation and examples.

📐 Basics

Targets and recipes basics
⚠️ Recipes MUST use tabs, not spaces. This is the #1 Makefile gotcha.
# Makefile

target: dependencies

recipe (MUST be a tab, not spaces)

build: npm run build

test: npm test

Run with: make build, make test

First target is the default

make (with no args) runs the first target

.PHONY — non-file targets basics
# Without .PHONY, if a file named "build" exists,
# make thinks the target is up-to-date and skips it

.PHONY: build test clean deploy

build: npm run build

test: npm test

clean: rm -rf dist node_modules

deploy: ./deploy.sh

Always declare .PHONY for targets that don’t produce files.

Dependencies basics
# deploy depends on test, which depends on build
deploy: test
	./deploy.sh

test: build npm test

build: npm run build

make deploy → runs build, then test, then deploy

📝 Variables

Variables and assignment vars
# Simple assignment
APP_NAME = myapp
PORT = 3000

Use with $(VAR)

run: docker run -p $(PORT):$(PORT) $(APP_NAME)

Override from command line

make run PORT=8080

Environment variable (export to subprocesses)

export NODE_ENV = production

Conditional (set only if not already set)

PORT ?= 3000

Shell command output

GIT_SHA := $(shell git rev-parse —short HEAD) DATE := $(shell date +%Y-%m-%d)

build: docker build -t $(APP_NAME):$(GIT_SHA) .

= is lazy (evaluated when used). := is immediate (evaluated when defined). Use := for shell commands.

⚡ Common Patterns

Project automation Makefile pattern
.PHONY: help install dev build test lint clean deploy

Default target — show help

help: @echo “Available targets:” @echo ” install — Install dependencies” @echo ” dev — Start dev server” @echo ” build — Build for production” @echo ” test — Run tests” @echo ” lint — Run linter” @echo ” clean — Remove build artifacts” @echo ” deploy — Deploy to production”

install: npm ci

dev: npm run dev

build: npm run build

test: npm test

lint: npx eslint src/

clean: rm -rf dist node_modules .next

deploy: build npx vercel —prod

The @ prefix suppresses printing the command itself (only shows output).

Docker project Makefile pattern
APP_NAME := myapp
TAG := $(shell git rev-parse --short HEAD)

.PHONY: build run stop logs shell

build: docker build -t $(APP_NAME):$(TAG) .

run: docker compose up -d

stop: docker compose down

logs: docker compose logs -f

shell: docker compose exec app bash

clean: docker compose down -v docker image prune -f

Multi-line commands and conditionals advanced
# Each line runs in a separate shell. Use \ for multi-line.
setup:
	@echo "Setting up..." && \
	npm install && \
	cp .env.example .env && \
	echo "Done!"

Conditionals

deploy: ifeq ($(ENV),production) @echo “Deploying to production…” ./deploy-prod.sh else @echo “Deploying to staging…” ./deploy-staging.sh endif

Ignore errors with -

clean: -rm -rf dist -docker compose down