Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Write Makefiles for any project type. Use when setting up build automation, defining multi-target builds, managing dependencies between tasks, creating project task runners, or using Make for non-C projects (Go, Python, Docker, Node.js). Also covers Just and Task as modern alternatives.
Write Makefiles for any project type. Use when setting up build automation, defining multi-target builds, managing dependencies between tasks, creating project task runners, or using Make for non-C projects (Go, Python, Docker, Node.js). Also covers Just and Task as modern alternatives.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Write Makefiles for project automation across any language. Covers targets, dependencies, variables, pattern rules, phony targets, and using Make for Go, Python, Docker, and Node.js projects. Includes Just and Task as modern alternatives.
Automating build, test, lint, deploy commands Defining dependencies between tasks (build before test) Creating a project-level task runner (consistent across team) Replacing long CLI commands with short memorable targets Managing multi-step build processes Any project that needs a make build && make test && make deploy workflow
# target: prerequisites # recipe (MUST be indented with TAB, not spaces) build: src/main.go go build -o bin/app src/main.go test: build go test ./... clean: rm -rf bin/ # First target is the default (runs with bare `make`)
# Simple assignment CC = gcc CFLAGS = -Wall -O2 # Deferred assignment (expanded when used) FILES = $(wildcard src/*.go) # Immediate assignment (expanded when defined) VERSION := $(shell git describe --tags --always) # Conditional assignment (only if not already set) PORT ?= 8080 # Use variables build: $(CC) $(CFLAGS) -o app main.c @echo "Version: $(VERSION)"
# $@ = target name # $< = first prerequisite # $^ = all prerequisites # $* = stem (pattern match) # $(@D) = directory of target # $(@F) = filename of target bin/app: src/main.go src/util.go go build -o $@ $^ # $@ = bin/app # $^ = src/main.go src/util.go # $< = src/main.go # Pattern rule %.o: %.c $(CC) -c -o $@ $< # For foo.o: $@ = foo.o, $< = foo.c, $* = foo
# Without .PHONY, if a file named "clean" exists, `make clean` does nothing .PHONY: build test clean lint fmt help build: go build -o bin/app ./cmd/app test: go test ./... clean: rm -rf bin/ dist/ # List all targets help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := help build: ## Build the application go build -o bin/app ./cmd/app test: ## Run all tests go test -v ./... lint: ## Run linters golangci-lint run clean: ## Remove build artifacts rm -rf bin/ dist/ help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
BINARY_NAME := myapp VERSION := $(shell git describe --tags --always --dirty) LDFLAGS := -ldflags "-X main.version=$(VERSION)" GOFILES := $(shell find . -name '*.go' -not -path './vendor/*') .PHONY: all build test lint clean run all: lint test build build: ## Build binary CGO_ENABLED=0 go build $(LDFLAGS) -o bin/$(BINARY_NAME) ./cmd/$(BINARY_NAME) test: ## Run tests go test -race -coverprofile=coverage.out ./... test-coverage: test ## Show coverage report go tool cover -html=coverage.out lint: ## Run linters golangci-lint run ./... fmt: ## Format code gofmt -w $(GOFILES) run: build ## Build and run ./bin/$(BINARY_NAME) clean: ## Clean build artifacts rm -rf bin/ coverage.out # Cross-compilation build-linux: ## Build for Linux GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME) build-all: ## Build for all platforms GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME) GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-darwin-arm64 ./cmd/$(BINARY_NAME) GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-windows-amd64.exe ./cmd/$(BINARY_NAME)
PYTHON := python3 VENV := .venv BIN := $(VENV)/bin .PHONY: all install test lint fmt clean run all: install lint test $(VENV)/bin/activate: $(PYTHON) -m venv $(VENV) $(BIN)/pip install --upgrade pip install: $(VENV)/bin/activate ## Install dependencies $(BIN)/pip install -r requirements.txt $(BIN)/pip install -r requirements-dev.txt test: ## Run tests $(BIN)/pytest -v --cov=src --cov-report=term-missing lint: ## Run linters $(BIN)/ruff check src/ tests/ $(BIN)/mypy src/ fmt: ## Format code $(BIN)/ruff format src/ tests/ run: ## Run application $(BIN)/python -m src.main clean: ## Remove venv and caches rm -rf $(VENV) __pycache__ .pytest_cache .mypy_cache .ruff_cache find . -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true
.PHONY: all install build test lint clean dev all: install lint test build node_modules: package.json npm install @touch node_modules install: node_modules ## Install dependencies build: node_modules ## Build TypeScript npx tsc test: node_modules ## Run tests npx vitest run test-watch: node_modules ## Run tests in watch mode npx vitest lint: node_modules ## Lint code npx eslint src/ --ext .ts,.tsx npx tsc --noEmit fmt: node_modules ## Format code npx prettier --write 'src/**/*.{ts,tsx}' dev: node_modules ## Run in development mode npx tsx watch src/index.ts clean: ## Clean build artifacts rm -rf dist/ node_modules/.cache
IMAGE_NAME := myapp VERSION := $(shell git describe --tags --always) REGISTRY := ghcr.io/myorg .PHONY: build push run stop clean build: ## Build Docker image docker build -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):latest . push: build ## Push to registry docker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):$(VERSION) docker tag $(IMAGE_NAME):latest $(REGISTRY)/$(IMAGE_NAME):latest docker push $(REGISTRY)/$(IMAGE_NAME):$(VERSION) docker push $(REGISTRY)/$(IMAGE_NAME):latest run: ## Run container docker run --rm -p 8080:8080 --name $(IMAGE_NAME) $(IMAGE_NAME):latest stop: ## Stop container docker stop $(IMAGE_NAME) 2>/dev/null || true clean: ## Remove images docker rmi $(IMAGE_NAME):$(VERSION) $(IMAGE_NAME):latest 2>/dev/null || true compose-up: ## Start with docker compose docker compose up -d --build compose-down: ## Stop compose docker compose down compose-logs: ## Follow compose logs docker compose logs -f
# OS detection UNAME := $(shell uname -s) ifeq ($(UNAME),Darwin) SED := sed -i '' else SED := sed -i endif # Environment-based config ENV ?= development ifeq ($(ENV),production) CFLAGS += -O2 LDFLAGS += -s -w else CFLAGS += -g -O0 endif # Check if command exists HAS_DOCKER := $(shell command -v docker 2>/dev/null) docker-build: ifndef HAS_DOCKER $(error "docker is not installed") endif docker build -t myapp .
SERVICES := api worker scheduler .PHONY: build-all test-all $(SERVICES) build-all: $(SERVICES) $(SERVICES): $(MAKE) -C services/$@ build test-all: @for svc in $(SERVICES); do \ echo "Testing $$svc..."; \ $(MAKE) -C services/$$svc test || exit 1; \ done
# Split large Makefile into modules include mk/docker.mk include mk/test.mk include mk/deploy.mk # Optional include (no error if missing) -include .env.mk
# @ suppresses command echo install: @echo "Installing dependencies..." @npm install # .SILENT for entire targets .SILENT: help clean # Make less verbose globally MAKEFLAGS += --no-print-directory
# justfile โ simpler than Make, no TAB requirement # Set shell set shell := ["bash", "-euo", "pipefail", "-c"] # Variables version := `git describe --tags --always` default_port := "8080" # Default recipe (first one) default: lint test build # Recipes build: ## Build the application go build -ldflags "-X main.version={{version}}" -o bin/app ./cmd/app test: ## Run tests go test -race ./... lint: ## Run linters golangci-lint run run port=default_port: build ## Run with optional port ./bin/app --port {{port}} clean: ## Clean artifacts rm -rf bin/ dist/ # Recipes with dependencies deploy: build test ./scripts/deploy.sh # OS-specific [linux] install-deps: sudo apt install -y build-essential [macos] install-deps: brew install go golangci-lint # List recipes help: @just --list # Install: https://github.com/casey/just # Run: just # Default recipe just build # Specific recipe just run 9090 # With argument just --list # List all recipes
# Taskfile.yml version: '3' vars: VERSION: sh: git describe --tags --always BINARY: myapp tasks: default: deps: [lint, test, build] build: desc: Build the application cmds: - go build -ldflags "-X main.version={{.VERSION}}" -o bin/{{.BINARY}} ./cmd/{{.BINARY}} sources: - ./**/*.go generates: - bin/{{.BINARY}} test: desc: Run tests cmds: - go test -race ./... lint: desc: Run linters cmds: - golangci-lint run run: desc: Build and run deps: [build] cmds: - ./bin/{{.BINARY}} {{.CLI_ARGS}} clean: desc: Clean artifacts cmds: - rm -rf bin/ dist/ docker:build: desc: Build Docker image cmds: - docker build -t {{.BINARY}}:{{.VERSION}} . # Task with preconditions deploy: desc: Deploy to production preconditions: - sh: test -f bin/{{.BINARY}} msg: "Build first: task build" - sh: git diff --quiet msg: "Uncommitted changes detected" cmds: - ./scripts/deploy.sh # Install: https://taskfile.dev/installation/ # Run: task # Default task task build # Specific task task --list # List all tasks
FeatureMakeJustTaskConfig formatMakefile (TAB-sensitive)justfileTaskfile.ymlDependenciesFile-based + phonyRecipe-basedTask-basedFile change detectionBuilt-inNosources/generatesVariablesYes (complex)Yes (simple)Yes (YAML)Cross-platformNeeds make installedSingle binarySingle binaryLearning curveHighLowLowBest forC/C++ builds, complex depsTask runner replacementYAML-native projects
The number one Makefile bug: using spaces instead of tabs for indentation. Make requires literal TAB characters in recipes. .PHONY every target that isn't a real file. Without it, make clean won't run if a file named clean exists. Use @ prefix to suppress command echo for cleaner output: @echo "Building..." prints only "Building...", not the echo command itself. The self-documenting help target (with ## comments) is worth adding to every Makefile. make help becomes the project's command reference. Make is overkill for simple task running. If you just want named commands, Just or Task are simpler and don't have the TAB footgun. Use ?= for variables users might want to override: PORT ?= 8080 lets PORT=9090 make run work. For polyglot projects (Go + Python + Docker), a Makefile at the root that delegates to language-specific tools is a clean pattern. Make's file-based dependency tracking is genuinely powerful for build systems. If your project compiles files, Make's target: prerequisites model avoids unnecessary rebuilds.
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.