Claude-skill-registry-data make
git clone https://github.com/majiayu000/claude-skill-registry-data
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/make" ~/.claude/skills/majiayu000-claude-skill-registry-data-make && rm -rf "$T"
data/make/SKILL.mdABOUTME: Make skill for idiomatic, maintainable Makefiles with modular patterns
ABOUTME: Emphasizes safety, self-documentation, modularity, and orchestration focus
Make Skill
Quick Reference
| Rule | Enforcement |
|---|---|
| Safety First | Always set , , |
| Self-Documenting | Every target has ; use for groups |
| .PHONY | Declare ALL non-file targets as phony |
| Explicit | No magic; clear variable names and dependencies |
| Modular | Split into files; use |
| Orchestration | Make orchestrates; scripts do complex logic |
| Naming | Hyphen-case; verb-noun prefixes (e.g., ) |
| Help Default | |
🛑 FILE OPERATION CHECKPOINT (BLOCKING)
Before EVERY
or Write
tool call on a Edit
or Makefile
file:*.mk
╔══════════════════════════════════════════════════════════════════╗ ║ 🛑 STOP - MAKE SKILL CHECK ║ ║ ║ ║ You are about to modify a Makefile. ║ ║ ║ ║ QUESTION: Is /make skill currently active? ║ ║ ║ ║ If YES → Proceed with the edit ║ ║ If NO → STOP! Invoke /make FIRST, then edit ║ ║ ║ ║ This check applies to: ║ ║ ✗ Write tool with file_path containing "Makefile" ║ ║ ✗ Edit tool with file_path containing "Makefile" ║ ║ ✗ Write/Edit with file_path ending in .mk ║ ║ ✗ ANY Makefile, regardless of conversation topic ║ ║ ║ ║ Examples that REQUIRE this skill: ║ ║ - "add a build target" (edits Makefile) ║ ║ - "update the docker targets" (edits make/docker.mk) ║ ║ - "fix the help target" (edits any Makefile) ║ ╚══════════════════════════════════════════════════════════════════╝
Why this matters: Makefiles without safety headers can fail silently or produce corrupt builds. The skill ensures
.DELETE_ON_ERROR and proper .PHONY.
🔄 RESUMED SESSION CHECKPOINT
When a session is resumed from context compaction, verify Makefile development state:
┌─────────────────────────────────────────────────────────────┐ │ SESSION RESUMED - MAKE SKILL VERIFICATION │ │ │ │ Before continuing Makefile implementation: │ │ │ │ 1. Was I in the middle of writing Makefiles? │ │ → Check summary for "Makefile", "make target", ".mk" │ │ │ │ 2. Did I follow all Make skill guidelines? │ │ → Safety headers (SHELL, .SHELLFLAGS, .DELETE_ON_ERROR) │ │ → .PHONY declarations for non-file targets │ │ → Self-documenting help target │ │ → ABOUTME headers on new files │ │ │ │ 3. Check Makefile quality before continuing: │ │ → Run: make -n <target> (dry run) │ │ → Verify help target works: make help │ │ │ │ If implementation was in progress: │ │ → Review the partial Makefile for completeness │ │ → Ensure safety headers are present │ │ → Verify no recipes exceed 5 lines (move to scripts) │ │ → Re-invoke /make if skill context was lost │ └─────────────────────────────────────────────────────────────┘
When to Use Make
Use Make for orchestration and build tasks:
- Build automation (compile, link, bundle)
- Development workflow commands (start, stop, test, lint)
- Multi-service orchestration (Docker, Terraform)
- CI/CD pipeline steps
- Task runners with dependencies
Do NOT use Make for:
- Complex business logic (use Python, Go, etc.)
- Scripts requiring conditionals/loops (use Bash scripts)
- Anything requiring error recovery
- Configuration management (use dedicated tools)
Size guideline: If a Makefile exceeds ~300 lines, split into modular
*.mk files.
Recipe rule: If a recipe exceeds 5 lines or contains
if/else, move it to a script and invoke the /bash skill.
Core Principles
1. Safety Headers
Every Makefile starts with strict settings (like Bash's
set -euo pipefail):
SHELL := bash .SHELLFLAGS := -eu -o pipefail -c .DELETE_ON_ERROR: MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules
| Setting | Purpose |
|---|---|
| Use Bash instead of |
| exit on error, error on undefined, |
| Remove target if recipe fails (prevents corrupt state) |
| Catch typos in variable names |
| Disable implicit rules for clarity |
2. Self-Documenting Help System
Every Makefile must have a help target as default:
.DEFAULT_GOAL := help ##@ General .PHONY: help help: ## Display this help @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \ /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \ /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
Documentation syntax:
##@ Section Header # Creates bold section in help output target: ## Description # Documents the target
3. Phony Targets
Declare ALL non-file-producing targets:
.PHONY: help build test clean deploy .PHONY: docker-up docker-down .PHONY: logs-% # Pattern rules too
Why: Prevents conflicts with files named
build, test, etc.
4. Explicit Over Implicit
- Use
not$(VARIABLE)$VARIABLE - Quote paths:
"$(PATH_VAR)" - Name variables clearly:
notDOCKER_COMPOSE_FILESDCF - Avoid automatic variables in complex contexts
5. Modular Design
Split large Makefiles into focused modules:
# Root Makefile include make/common.mk include make/docker.mk include make/test.mk # Optional includes (won't fail if missing) -include make/local.mk
See
resources/common.mk for a reusable library pattern.
6. Orchestration Focus
Make orchestrates; it doesn't implement:
# GOOD: Make orchestrates test: ## Run tests @./scripts/run-tests.sh # BAD: Complex logic in Make test: @if [ -f .env ]; then \ source .env && \ for dir in $(TEST_DIRS); do \ cd $$dir && npm test || exit 1; \ done \ fi
7. Consistent Naming
Prefixes for semantic grouping:
| Prefix | Purpose | Example |
|---|---|---|
| Full application stack | , |
| Infrastructure only | , |
| Docker operations | , |
| Test execution | , |
or | Database operations | , |
Verbs:
| Verb | Meaning |
|---|---|
/ | Create and run |
/ | Stop and remove |
| Stop then start |
| Build only |
| Build and start |
| Stream logs |
| Show current state |
Standard Template
Use
resources/template.mk as a starter:
cp ~/.claude/skills/make/resources/template.mk ./Makefile
The template includes:
- Safety headers
- Color definitions
- Logging macros
- Help system
- Example targets
Variable Patterns
Path Resolution (Portable)
# Get directory containing this Makefile MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) # Project root (if Makefile is in project root) PROJECT_ROOT := $(MAKEFILE_DIR) # Relative paths from Makefile location SRC_DIR := $(PROJECT_ROOT)src BUILD_DIR := $(PROJECT_ROOT)build
Default Values
# Use ?= for overridable defaults COMPOSE_PROJECT_NAME ?= myproject PORT ?= 8080 # Use := for computed values TIMESTAMP := $(shell date +%Y%m%d-%H%M%S) GIT_SHA := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
Environment Exports
# Export for child processes export DOCKER_BUILDKIT := 1 export COMPOSE_DOCKER_CLI_BUILD := 1 # Pass through from environment export API_KEY export DATABASE_URL
Conditional Variables
# Makefile conditionals (evaluated at parse time) ifeq ($(CI),true) VERBOSE := 1 endif ifdef DEBUG BUILD_FLAGS += -v endif ifndef REQUIRED_VAR $(error REQUIRED_VAR is not set) endif
Macros (define/endef)
Logging Macros
# Color codes CYAN := \033[0;36m GREEN := \033[0;32m YELLOW := \033[1;33m RED := \033[0;31m BOLD := \033[1m NC := \033[0m define log_info @printf "$(CYAN)[INFO]$(NC) %s\n" "$(1)" endef define log_success @printf "$(GREEN)[OK]$(NC) %s\n" "$(1)" endef define log_warn @printf "$(YELLOW)[WARN]$(NC) %s\n" "$(1)" endef define log_error @printf "$(RED)[ERROR]$(NC) %s\n" "$(1)" endef define log_step @printf "$(BOLD)>>> %s$(NC)\n" "$(1)" endef # Usage build: $(call log_step,Building project) @go build ./... $(call log_success,Build completed)
Utility Macros
# Check if command exists define check_cmd @command -v $(1) >/dev/null 2>&1 || { \ printf "$(RED)[ERROR]$(NC) $(1) is required but not installed\n"; \ exit 1; \ } endef # Ensure directory exists define ensure_dir @mkdir -p $(1) endef # Confirm dangerous action define confirm @read -p "Are you sure? [y/N] " ans && [ "$${ans:-N}" = "y" ] endef # Usage deploy: ## Deploy to production $(call check_cmd,kubectl) $(call confirm) $(call log_step,Deploying to production) @kubectl apply -f manifests/
Pattern Rules
Dynamic Targets with %
# logs-<service> - tail logs for any service .PHONY: logs-% logs-%: ## Tail logs for a specific service docker compose logs -f $* # docker-exec-<service> - exec into any container .PHONY: exec-% exec-%: ## Execute shell in a container docker compose exec $* sh # build-<component> - build specific component .PHONY: build-% build-%: $(call log_step,Building $*) @cd $* && make build
Automatic variables:
| Variable | Meaning |
|---|---|
| Target name |
| First prerequisite |
| All prerequisites |
| Stem matched by |
No-op Targets for Arguments
# Allow: make logs backend # These are filter arguments, not real targets .PHONY: backend frontend api worker backend frontend api worker: @: .PHONY: logs logs: ## Tail logs (usage: make logs <service>) @component="$(filter-out $@,$(MAKECMDGOALS))"; \ if [ -n "$$component" ]; then \ docker compose logs -f $$component; \ else \ docker compose logs -f; \ fi
Modular Library Pattern
Structure
project/ ├── Makefile # Root: includes modules, defines high-level targets └── make/ ├── common.mk # Shared: variables, colors, logging macros ├── docker.mk # Docker Compose operations ├── test.mk # Test orchestration └── app-python.mk # Language-specific targets
Root Makefile
# Include order matters: common first, then specific include make/common.mk include make/docker.mk include make/test.mk # Optional local overrides -include make/local.mk ##@ Stack Management .PHONY: up up: docker-up ## Start all services $(call log_success,Stack is running) .PHONY: down down: docker-down ## Stop all services $(call log_success,Stack stopped)
Consumer Pattern (Multi-Repo)
For projects that consume a shared Makefile library:
# Define path to shared infra INFRA_DIR ?= $(HOME)/projects/shared-infra # Clone if missing $(if $(wildcard $(INFRA_DIR)),,$(shell git clone git@github.com:org/shared-infra.git $(INFRA_DIR))) # Include shared modules -include $(INFRA_DIR)/make/common.mk -include $(INFRA_DIR)/make/app.mk -include $(INFRA_DIR)/make/app-python.mk
See
resources/common.mk for a complete reusable library.
Dependency Management
Prerequisite Chains
# Direct dependencies deploy: build test ## Deploy (requires build and test) @./scripts/deploy.sh # Chain dependencies clean-all: clean-build clean-test clean-docker # Order-only prerequisites (directory must exist, but changes don't trigger rebuild) $(BUILD_DIR)/output: source.c | $(BUILD_DIR) gcc -o $@ $< $(BUILD_DIR): mkdir -p $@
Conditional Dependencies
.PHONY: start start: ## Start services (optionally with migrations) ifeq ($(MIGRATE),1) $(call log_info,Running migrations first) @$(MAKE) db-migrate endif @docker compose up -d
Sentinel Files (Complex Dependencies)
For dependencies that don't produce predictable files (like
npm install):
# Sentinel file tracks when install was last run .stamps: @mkdir -p .stamps .stamps/npm-install: package.json package-lock.json | .stamps npm ci @touch $@ .stamps/pip-install: requirements.txt | .stamps pip install -r requirements.txt @touch $@ # Depend on sentinel, not directory build: .stamps/npm-install npm run build clean: rm -rf .stamps node_modules
Integration Patterns
Docker Compose
# Compose file configuration COMPOSE_FILES := -f docker-compose.yml ifdef CI COMPOSE_FILES += -f docker-compose.ci.yml endif COMPOSE := docker compose $(COMPOSE_FILES) .PHONY: docker-up docker-up: ## Start Docker services $(COMPOSE) up -d .PHONY: docker-down docker-down: ## Stop Docker services $(COMPOSE) down
Terraform
TF_DIR := terraform .PHONY: tf-init tf-init: ## Initialize Terraform cd $(TF_DIR) && terraform init .PHONY: tf-plan tf-plan: ## Plan Terraform changes cd $(TF_DIR) && terraform plan -out=tfplan .PHONY: tf-apply tf-apply: ## Apply Terraform changes $(call confirm) cd $(TF_DIR) && terraform apply tfplan
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Spaces instead of tabs | Recipes won't run | Use tabs for recipe lines |
| Missing .PHONY | Target skipped if file exists | Declare all non-file targets |
Bare without | Fails if file missing | Use or |
| Ignoring errors silently | hides failures | Handle errors explicitly |
| Complex shell logic | Unmaintainable, error-prone | Move to script; invoke |
| Recursive make without $(MAKE) | Breaks parallelism, options | Use |
| Hardcoded paths | Not portable | Use variables with defaults |
| No help target | Users don't know available commands | Always provide |
Excessive silencing | Can't debug issues | Only silence noise, not errors |
Validation
Run the validation script to check Makefile quality:
~/.claude/skills/make/scripts/validate_makefile.sh [Makefile]
The script checks:
- Safety headers present
- Help target exists
declarations.PHONY- Forbidden patterns (complex shell logic, missing error handling)
- Uses
if availablecheckmake
Checklist
Before committing a Makefile:
- Safety headers present (
,SHELL
,.SHELLFLAGS
).DELETE_ON_ERROR -
set.DEFAULT_GOAL := help -
target with awk-based documentation parserhelp - All non-file targets declared
.PHONY - Variables use
syntax (not$(VAR)
)$VAR - Paths are quoted where needed
- No recipes exceed 5 lines (moved to scripts)
- No complex shell logic (if/else, loops) in recipes
- Logging macros used for user feedback
- Dependencies declared correctly
- Runs
without errorsvalidate_makefile.sh - Tested with
(dry run)make -n <target>