Creating Makefiles
Target: $ARGUMENTS
Creates or reviews a rigorous, consistent Makefile following org conventions.
References
- Required structure + patterns:
references/makefile-conventions.md
- CI linter script:
references/lint-makefile.sh
- Language-specific recipe templates:
references/scaffold-*.md
Required Structure (all project types)
Every Makefile must have these elements in order:
# Header comment describing the project
# Run `make` to see all available recipes.
# GNU Make version guard
ifeq ($(filter oneshell,$(.FEATURES)),)
$(error GNU Make >= 3.82 required (.ONESHELL). macOS ships 3.81 — install via: brew install make, then use gmake)
endif
.SILENT:
.ONESHELL:
.PHONY: \
recipe_a recipe_b \
help
.DEFAULT_GOAL := help
# -- config --
VERBOSE ?= 0
ifeq ($(VERBOSE),0)
# quiet flags per tool
endif
Conventions
- snake_case for all recipe names
## description comment on every public recipe (for help output)
# MARK: SECTION to group related recipes
setup_* prefix for installation recipes (never bare install)
validate chains lint + test (when both exist)
- Idempotent setup:
command -v X >/dev/null || install X
- No hardcoded paths — use variables at top of file
VERBOSE ?= 0 with conditional quiet flags per tool
- Zero-sudo installs — use
~/.local/bin or ~/.local/share/<tool>, never sudo apt/dnf
export PATH=... inside recipes that invoke tools installed to a non-default prefix (e.g. user-local npm)
Approach
- Detect what kind of project this is — look at build files, dependency manifests, and source file types in the working directory. If
$ARGUMENTS names a type, use that instead.
- Find a scaffold — list
references/scaffold-*.md in this skill's directory. If one matches the detected project type, read it for language-specific recipe templates. If none matches, build recipes from first principles using the conventions above.
- Merge the scaffold's recipes with the required structure (version guard, .SILENT, .ONESHELL, .PHONY, help, config variables). The scaffold provides the MARK sections and recipe bodies; the required structure wraps them.
- Check if a Makefile already exists — if yes, review it against conventions and the scaffold. If no, create one from the merged template.
- Lint with
references/lint-makefile.sh — fix any failures.
- Verify
make help renders all recipes, make validate (or make lint) runs clean.
The scaffold files shipped with this skill are starting points, not an exhaustive list. For a project type without a matching scaffold, apply the same conventions and recipe structure — the patterns (setup, dev, help) are universal.
Help Recipe (include in every Makefile)
help: ## Show available recipes grouped by section
echo "Usage: make [recipe]"
echo ""
awk '/^# MARK:/ { \
section = substr($$0, index($$0, ":")+2); \
printf "\n\033[1m%s\033[0m\n", section \
} \
/^[a-zA-Z0-9_-]+:.*?##/ { \
helpMessage = match($$0, /## (.*)/); \
if (helpMessage) { \
recipe = $$1; \
sub(/:/, "", recipe); \
printf " \033[36m%-22s\033[0m %s\n", recipe, substr($$0, RSTART + 3, RLENGTH) \
} \
}' $(MAKEFILE_LIST)
Quality Gates
1---2name: creating-makefile3description: Scaffold or review Makefiles following org conventions. Use when creating a new Makefile, auditing an existing one, or adding recipes to a project. Auto-detects project type from the working directory.4---56# Creating Makefiles78**Target**: $ARGUMENTS910Creates or reviews a **rigorous, consistent Makefile** following org conventions.1112## References1314- Required structure + patterns: `references/makefile-conventions.md`15- CI linter script: `references/lint-makefile.sh`16- Language-specific recipe templates: `references/scaffold-*.md`1718## Required Structure (all project types)1920Every Makefile must have these elements in order:2122```makefile23# Header comment describing the project24# Run `make` to see all available recipes.2526# GNU Make version guard27ifeq ($(filter oneshell,$(.FEATURES)),)28$(error GNU Make >= 3.82 required (.ONESHELL). macOS ships 3.81 — install via: brew install make, then use gmake)29endif3031.SILENT:32.ONESHELL:33.PHONY: \34 recipe_a recipe_b \35 help36.DEFAULT_GOAL := help3738# -- config --39VERBOSE ?= 040ifeq ($(VERBOSE),0)41 # quiet flags per tool42endif43```4445## Conventions4647- **snake_case** for all recipe names48- **`## description`** comment on every public recipe (for help output)49- **`# MARK: SECTION`** to group related recipes50- **`setup_*`** prefix for installation recipes (never bare `install`)51- **`validate`** chains `lint` + `test` (when both exist)52- **Idempotent setup**: `command -v X >/dev/null || install X`53- **No hardcoded paths** — use variables at top of file54- **`VERBOSE ?= 0`** with conditional quiet flags per tool55- **Zero-sudo installs** — use `~/.local/bin` or `~/.local/share/<tool>`, never `sudo apt/dnf`56- **`export PATH=...`** inside recipes that invoke tools installed to a non-default prefix (e.g. user-local npm)5758## Approach59601. **Detect** what kind of project this is — look at build files, dependency manifests, and source file types in the working directory. If `$ARGUMENTS` names a type, use that instead.612. **Find a scaffold** — list `references/scaffold-*.md` in this skill's directory. If one matches the detected project type, read it for language-specific recipe templates. If none matches, build recipes from first principles using the conventions above.623. **Merge** the scaffold's recipes with the required structure (version guard, .SILENT, .ONESHELL, .PHONY, help, config variables). The scaffold provides the MARK sections and recipe bodies; the required structure wraps them.634. **Check** if a Makefile already exists — if yes, review it against conventions and the scaffold. If no, create one from the merged template.645. **Lint** with `references/lint-makefile.sh` — fix any failures.656. **Verify** `make help` renders all recipes, `make validate` (or `make lint`) runs clean.6667The scaffold files shipped with this skill are **starting points, not an exhaustive list**. For a project type without a matching scaffold, apply the same conventions and recipe structure — the patterns (setup, dev, help) are universal.6869## Help Recipe (include in every Makefile)7071```makefile72help: ## Show available recipes grouped by section73 echo "Usage: make [recipe]"74 echo ""75 awk '/^# MARK:/ { \76 section = substr($$0, index($$0, ":")+2); \77 printf "\n\033[1m%s\033[0m\n", section \78 } \79 /^[a-zA-Z0-9_-]+:.*?##/ { \80 helpMessage = match($$0, /## (.*)/); \81 if (helpMessage) { \82 recipe = $$1; \83 sub(/:/, "", recipe); \84 printf " \033[36m%-22s\033[0m %s\n", recipe, substr($$0, RSTART + 3, RLENGTH) \85 } \86 }' $(MAKEFILE_LIST)87```8889## Quality Gates9091- [ ] GNU Make version guard present92- [ ] `.SILENT`, `.ONESHELL`, `.DEFAULT_GOAL := help` set93- [ ] Full `.PHONY` list up front94- [ ] Every recipe has `## description` comment95- [ ] `# MARK:` sections group related recipes96- [ ] `snake_case` recipe names only97- [ ] `help` recipe with standard awk pattern98- [ ] `VERBOSE ?= 0` with quiet mode support99- [ ] No `sudo` in setup recipes (user-local installs only)100- [ ] `lint-makefile.sh` passes