brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
makefileImplements Makefile best practices for build automation including phony targets, pattern rules, variable scoping, and cross-platform compatibility to streamline software build processes.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: makefile description: Implements Makefile best practices for build automation including phony targets, pattern rules, variable scoping, and cross-platform compatibility to streamline software build processes. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: makefile, make, build automation, phony targets, pattern rules, build system, make command, cross-platform make archetypes:
Build system engineer implementing reliable, maintainable Makefiles that automate compilation, testing, and deployment workflows. Every Makefile should be idempotent, portable, and self-documenting — treating build logic with the same rigor as application code.
.PHONY:= for immediate assignment, ?= for defaults, += for appending%.o: %.c) instead of repeating commands$@, lt;, $^, $*) instead of hardcoding filenameshelp target listing all available commandsUse this skill when:
make init, make build, make testAvoid this skill for:
CMake, Bazel, or Meson insteadCargo for Rust, go build for Go, npm scripts for Node.js)NMake or MSBuild instead)Define Project Structure — Identify source directories, build output directories, test directories, and artifact locations.
Checkpoint: Ensure all paths are relative to the Makefile location ($(CURDIR)), not the working directory.
Set Up Variables with Sensible Defaults — Declare compiler, flags, and tool paths using := for strict defaults and ?= for user override.
Checkpoint: $(CC) and $(CFLAGS) should work out of the box but be overridable via make CC=gcc-12.
Declare Phony Targets — List every target that does not produce a file with .PHONY.
Checkpoint: Forgetting .PHONY causes a file named clean or test to silently break the build.
Implement Pattern Rules — Replace repeated file-specific rules with %.o: %.c pattern rules.
Checkpoint: Pattern rules must account for headers; a change to utils.h should rebuild all dependent .o files.
Add High-Level Workflow Targets — Create build, test, clean, lint, and help as composite targets.
Checkpoint: make help should output a formatted summary of all user-facing targets with descriptions.
Validate Portability — Test the Makefile on both GNU Make (Linux) and BSD Make (macOS).
Checkpoint: Avoid GNU-specific functions ($(wildcard ...), $(patsubst ...)) without fallbacks or version guards.
# ❌ BAD — no .PHONY, uses = instead of :=, hardcoded values
clean:
rm -rf build/
gcc -o build/main src/main.c
build = gcc
CFLAGS = -Wall -O2
# ✅ GOOD — .PHONY declared, := for strict assignment, parameterized compiler
CC ?= gcc
CFLAGS ?= -Wall -Wextra -O2
LDFLAGS ?=
SRCDIR := src
BUILDDIR := build
TARGET := $(BUILDDIR)/app
.PHONY: all clean test help build
all: build
build: $(TARGET)
$(TARGET): $(SRCDIR)/main.c
@mkdir -p $(BUILDDIR)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lt;
clean:
rm -rf $(BUILDDIR)
test: build
./$(TARGET) --test
help:
@echo "Available targets:"
@echo " make build - Compile the project"
@echo " make test - Run tests"
@echo " make clean - Remove build artifacts"
@echo " make help - Show this help message"
# ❌ BAD — repeating the same rule for every source file, no dependency tracking
$(BUILDDIR)/main.o: src/main.c
$(CC) $(CFLAGS) -c lt; -o $@
$(BUILDDIR)/utils.o: src/utils.c
$(CC) $(CFLAGS) -c lt; -o $@
$(BUILDDIR)/parser.o: src/parser.c
$(CC) $(CFLAGS) -c lt; -o $@
# ✅ GOOD — single pattern rule, automatic variables, header dependency generation
SRCDIR := src
BUILDDIR := build
CC ?= gcc
CFLAGS ?= -Wall -Wextra -O2
LDFLAGS ?=
SRCS := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS))
DEPS := $(OBJS:.o=.d)
$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR)
@echo " CC lt;"
$(CC) $(CFLAGS) -MMD -MP -c lt; -o $@
$(BUILDDIR):
@mkdir -p $@
all: $(BUILDDIR)/app
$(BUILDDIR)/app: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
-include $(DEPS)
clean:
rm -rf $(BUILDDIR)
test: all
./$(BUILDDIR)/app --test
help:
@echo "Available targets:"
@echo " make all - Build all targets (default)"
@echo " make clean - Remove build artifacts"
@echo " make test - Run tests"
@echo " make help - Show this help message"
# ❌ BAD — Linux-specific commands, no macOS fallback
clean:
rm -rf $(BUILDDIR)
find . -name "*.o" -delete
.PHONY: clean
# ✅ GOOD — portable commands using uname detection and variable command selection
SRCDIR := src
BUILDDIR := build
IS_MACOS := $(filter Darwin,$(shell uname -s))
ifeq ($(IS_MACOS),Darwin)
RM_CMD = rm -rf
FIND_FLAGS = -maxdepth 3
else
RM_CMD = rm -rf
FIND_FLAGS =
endif
clean:
$(RM_CMD) $(BUILDDIR)
find $(FIND_FLAGS) . -name "*.o" -delete
.PHONY: clean
# ✅ GOOD — composite targets for polyglot projects with per-language sub-makes
PROJECT_ROOT := $(CURDIR)
SUBDIRS := lib/ core/ tools/
.PHONY: all clean test help $(SUBDIRS)
all:
@for dir in $(SUBDIRS); do \
$(MAKE) -C $dir build || exit 1; \
done
clean:
@for dir in $(SUBDIRS); do \
$(MAKE) -C $dir clean; \
done
test:
@for dir in $(SUBDIRS); do \
$(MAKE) -C $dir test || exit 1; \
done
help:
@echo "Multi-language project build system"
@echo "Sub-projects: $(SUBDIRS)"
@echo ""
@echo "Top-level targets:"
@echo " make all - Build all sub-projects"
@echo " make clean - Clean all sub-projects"
@echo " make test - Run tests across all sub-projects"
@echo " make help - Show this help message"
.PHONY:= for definitive values, ?= for configurable defaults, += for list accumulation$@, lt;, $^, $*) instead of hardcoding filenames in recipes.d) via -MMD -MP to catch header changes automaticallyhelp target that documents all user-facing commands with inline @echo descriptions@ to suppress command echoing during normal builds= (recursive assignment) for variables that reference other variables — it causes infinite loops and hard-to-debug expansion issues$(CURDIR) or the Makefile's directory.PHONY — a file matching the target name silently breaks subsequent buildssystem() or os.system() in recipes when Make functions ($(wildcard ...), $(filter ...)) can achieve the same result.env / .mk include file$(shell ...) or Make functions are cleaner alternativesWhen implementing or reviewing a Makefile, produce:
CC, CFLAGS, paths, and tool configurations using := or ?=.PHONY: declaration for every non-file target%.o: %.c rules using automatic variables and dependency trackingbuild, test, clean, help) that orchestrate lower-level rulesmake help confirming usability| Skill | Purpose |
|---|---|
shell-scripting | Shell scripts for complex pre/post build logic that Make cannot express |
docker-compose | Containerized build environments that eliminate local dependency drift |
ci-cd-pipelines | Integrating Makefile targets into GitHub Actions, GitLab CI, or Jenkins |
Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.