{
  "schemaVersion": "1.0",
  "item": {
    "slug": "makefile-build",
    "name": "Makefile & Build",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/gitgoodordietrying/makefile-build",
    "canonicalUrl": "https://clawhub.ai/gitgoodordietrying/makefile-build",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/makefile-build",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=makefile-build",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "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."
        },
        {
          "label": "Upgrade existing",
          "body": "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."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/makefile-build"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/makefile-build",
    "agentPageUrl": "https://openagent3.xyz/skills/makefile-build/agent",
    "manifestUrl": "https://openagent3.xyz/skills/makefile-build/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/makefile-build/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "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."
      },
      {
        "label": "Upgrade existing",
        "body": "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."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Makefile & Build",
        "body": "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."
      },
      {
        "title": "When to Use",
        "body": "Automating build, test, lint, deploy commands\nDefining dependencies between tasks (build before test)\nCreating a project-level task runner (consistent across team)\nReplacing long CLI commands with short memorable targets\nManaging multi-step build processes\nAny project that needs a make build && make test && make deploy workflow"
      },
      {
        "title": "Structure",
        "body": "# target: prerequisites\n#     recipe (MUST be indented with TAB, not spaces)\n\nbuild: src/main.go\n\tgo build -o bin/app src/main.go\n\ntest: build\n\tgo test ./...\n\nclean:\n\trm -rf bin/\n\n# First target is the default (runs with bare `make`)"
      },
      {
        "title": "Variables",
        "body": "# Simple assignment\nCC = gcc\nCFLAGS = -Wall -O2\n\n# Deferred assignment (expanded when used)\nFILES = $(wildcard src/*.go)\n\n# Immediate assignment (expanded when defined)\nVERSION := $(shell git describe --tags --always)\n\n# Conditional assignment (only if not already set)\nPORT ?= 8080\n\n# Use variables\nbuild:\n\t$(CC) $(CFLAGS) -o app main.c\n\t@echo \"Version: $(VERSION)\""
      },
      {
        "title": "Automatic variables",
        "body": "# $@ = target name\n# $< = first prerequisite\n# $^ = all prerequisites\n# $* = stem (pattern match)\n# $(@D) = directory of target\n# $(@F) = filename of target\n\nbin/app: src/main.go src/util.go\n\tgo build -o $@ $^\n# $@ = bin/app\n# $^ = src/main.go src/util.go\n# $< = src/main.go\n\n# Pattern rule\n%.o: %.c\n\t$(CC) -c -o $@ $<\n# For foo.o: $@ = foo.o, $< = foo.c, $* = foo"
      },
      {
        "title": "Phony targets (not files)",
        "body": "# Without .PHONY, if a file named \"clean\" exists, `make clean` does nothing\n.PHONY: build test clean lint fmt help\n\nbuild:\n\tgo build -o bin/app ./cmd/app\n\ntest:\n\tgo test ./...\n\nclean:\n\trm -rf bin/ dist/\n\n# List all targets\nhelp:\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'"
      },
      {
        "title": "Self-documenting Makefile",
        "body": ".DEFAULT_GOAL := help\n\nbuild: ## Build the application\n\tgo build -o bin/app ./cmd/app\n\ntest: ## Run all tests\n\tgo test -v ./...\n\nlint: ## Run linters\n\tgolangci-lint run\n\nclean: ## Remove build artifacts\n\trm -rf bin/ dist/\n\nhelp: ## Show this help\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'"
      },
      {
        "title": "Go",
        "body": "BINARY_NAME := myapp\nVERSION := $(shell git describe --tags --always --dirty)\nLDFLAGS := -ldflags \"-X main.version=$(VERSION)\"\nGOFILES := $(shell find . -name '*.go' -not -path './vendor/*')\n\n.PHONY: all build test lint clean run\n\nall: lint test build\n\nbuild: ## Build binary\n\tCGO_ENABLED=0 go build $(LDFLAGS) -o bin/$(BINARY_NAME) ./cmd/$(BINARY_NAME)\n\ntest: ## Run tests\n\tgo test -race -coverprofile=coverage.out ./...\n\ntest-coverage: test ## Show coverage report\n\tgo tool cover -html=coverage.out\n\nlint: ## Run linters\n\tgolangci-lint run ./...\n\nfmt: ## Format code\n\tgofmt -w $(GOFILES)\n\nrun: build ## Build and run\n\t./bin/$(BINARY_NAME)\n\nclean: ## Clean build artifacts\n\trm -rf bin/ coverage.out\n\n# Cross-compilation\nbuild-linux: ## Build for Linux\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME)\n\nbuild-all: ## Build for all platforms\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME)\n\tGOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-darwin-arm64 ./cmd/$(BINARY_NAME)\n\tGOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-windows-amd64.exe ./cmd/$(BINARY_NAME)"
      },
      {
        "title": "Python",
        "body": "PYTHON := python3\nVENV := .venv\nBIN := $(VENV)/bin\n\n.PHONY: all install test lint fmt clean run\n\nall: install lint test\n\n$(VENV)/bin/activate:\n\t$(PYTHON) -m venv $(VENV)\n\t$(BIN)/pip install --upgrade pip\n\ninstall: $(VENV)/bin/activate ## Install dependencies\n\t$(BIN)/pip install -r requirements.txt\n\t$(BIN)/pip install -r requirements-dev.txt\n\ntest: ## Run tests\n\t$(BIN)/pytest -v --cov=src --cov-report=term-missing\n\nlint: ## Run linters\n\t$(BIN)/ruff check src/ tests/\n\t$(BIN)/mypy src/\n\nfmt: ## Format code\n\t$(BIN)/ruff format src/ tests/\n\nrun: ## Run application\n\t$(BIN)/python -m src.main\n\nclean: ## Remove venv and caches\n\trm -rf $(VENV) __pycache__ .pytest_cache .mypy_cache .ruff_cache\n\tfind . -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true"
      },
      {
        "title": "Node.js / TypeScript",
        "body": ".PHONY: all install build test lint clean dev\n\nall: install lint test build\n\nnode_modules: package.json\n\tnpm install\n\t@touch node_modules\n\ninstall: node_modules ## Install dependencies\n\nbuild: node_modules ## Build TypeScript\n\tnpx tsc\n\ntest: node_modules ## Run tests\n\tnpx vitest run\n\ntest-watch: node_modules ## Run tests in watch mode\n\tnpx vitest\n\nlint: node_modules ## Lint code\n\tnpx eslint src/ --ext .ts,.tsx\n\tnpx tsc --noEmit\n\nfmt: node_modules ## Format code\n\tnpx prettier --write 'src/**/*.{ts,tsx}'\n\ndev: node_modules ## Run in development mode\n\tnpx tsx watch src/index.ts\n\nclean: ## Clean build artifacts\n\trm -rf dist/ node_modules/.cache"
      },
      {
        "title": "Docker",
        "body": "IMAGE_NAME := myapp\nVERSION := $(shell git describe --tags --always)\nREGISTRY := ghcr.io/myorg\n\n.PHONY: build push run stop clean\n\nbuild: ## Build Docker image\n\tdocker build -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):latest .\n\npush: build ## Push to registry\n\tdocker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):$(VERSION)\n\tdocker tag $(IMAGE_NAME):latest $(REGISTRY)/$(IMAGE_NAME):latest\n\tdocker push $(REGISTRY)/$(IMAGE_NAME):$(VERSION)\n\tdocker push $(REGISTRY)/$(IMAGE_NAME):latest\n\nrun: ## Run container\n\tdocker run --rm -p 8080:8080 --name $(IMAGE_NAME) $(IMAGE_NAME):latest\n\nstop: ## Stop container\n\tdocker stop $(IMAGE_NAME) 2>/dev/null || true\n\nclean: ## Remove images\n\tdocker rmi $(IMAGE_NAME):$(VERSION) $(IMAGE_NAME):latest 2>/dev/null || true\n\ncompose-up: ## Start with docker compose\n\tdocker compose up -d --build\n\ncompose-down: ## Stop compose\n\tdocker compose down\n\ncompose-logs: ## Follow compose logs\n\tdocker compose logs -f"
      },
      {
        "title": "Conditional logic",
        "body": "# OS detection\nUNAME := $(shell uname -s)\nifeq ($(UNAME),Darwin)\n    SED := sed -i ''\nelse\n    SED := sed -i\nendif\n\n# Environment-based config\nENV ?= development\nifeq ($(ENV),production)\n    CFLAGS += -O2\n    LDFLAGS += -s -w\nelse\n    CFLAGS += -g -O0\nendif\n\n# Check if command exists\nHAS_DOCKER := $(shell command -v docker 2>/dev/null)\ndocker-build:\nifndef HAS_DOCKER\n\t$(error \"docker is not installed\")\nendif\n\tdocker build -t myapp ."
      },
      {
        "title": "Multi-directory builds",
        "body": "SERVICES := api worker scheduler\n\n.PHONY: build-all test-all $(SERVICES)\n\nbuild-all: $(SERVICES)\n\n$(SERVICES):\n\t$(MAKE) -C services/$@ build\n\ntest-all:\n\t@for svc in $(SERVICES); do \\\n\t\techo \"Testing $$svc...\"; \\\n\t\t$(MAKE) -C services/$$svc test || exit 1; \\\n\tdone"
      },
      {
        "title": "Include other Makefiles",
        "body": "# Split large Makefile into modules\ninclude mk/docker.mk\ninclude mk/test.mk\ninclude mk/deploy.mk\n\n# Optional include (no error if missing)\n-include .env.mk"
      },
      {
        "title": "Silent execution and output control",
        "body": "# @ suppresses command echo\ninstall:\n\t@echo \"Installing dependencies...\"\n\t@npm install\n\n# .SILENT for entire targets\n.SILENT: help clean\n\n# Make less verbose globally\nMAKEFLAGS += --no-print-directory"
      },
      {
        "title": "Justfile syntax",
        "body": "# justfile — simpler than Make, no TAB requirement\n\n# Set shell\nset shell := [\"bash\", \"-euo\", \"pipefail\", \"-c\"]\n\n# Variables\nversion := `git describe --tags --always`\ndefault_port := \"8080\"\n\n# Default recipe (first one)\ndefault: lint test build\n\n# Recipes\nbuild: ## Build the application\n    go build -ldflags \"-X main.version={{version}}\" -o bin/app ./cmd/app\n\ntest: ## Run tests\n    go test -race ./...\n\nlint: ## Run linters\n    golangci-lint run\n\nrun port=default_port: build ## Run with optional port\n    ./bin/app --port {{port}}\n\nclean: ## Clean artifacts\n    rm -rf bin/ dist/\n\n# Recipes with dependencies\ndeploy: build test\n    ./scripts/deploy.sh\n\n# OS-specific\n[linux]\ninstall-deps:\n    sudo apt install -y build-essential\n\n[macos]\ninstall-deps:\n    brew install go golangci-lint\n\n# List recipes\nhelp:\n    @just --list\n\n# Install: https://github.com/casey/just\n# Run:\njust          # Default recipe\njust build    # Specific recipe\njust run 9090 # With argument\njust --list   # List all recipes"
      },
      {
        "title": "Taskfile.yml",
        "body": "# Taskfile.yml\nversion: '3'\n\nvars:\n  VERSION:\n    sh: git describe --tags --always\n  BINARY: myapp\n\ntasks:\n  default:\n    deps: [lint, test, build]\n\n  build:\n    desc: Build the application\n    cmds:\n      - go build -ldflags \"-X main.version={{.VERSION}}\" -o bin/{{.BINARY}} ./cmd/{{.BINARY}}\n    sources:\n      - ./**/*.go\n    generates:\n      - bin/{{.BINARY}}\n\n  test:\n    desc: Run tests\n    cmds:\n      - go test -race ./...\n\n  lint:\n    desc: Run linters\n    cmds:\n      - golangci-lint run\n\n  run:\n    desc: Build and run\n    deps: [build]\n    cmds:\n      - ./bin/{{.BINARY}} {{.CLI_ARGS}}\n\n  clean:\n    desc: Clean artifacts\n    cmds:\n      - rm -rf bin/ dist/\n\n  docker:build:\n    desc: Build Docker image\n    cmds:\n      - docker build -t {{.BINARY}}:{{.VERSION}} .\n\n  # Task with preconditions\n  deploy:\n    desc: Deploy to production\n    preconditions:\n      - sh: test -f bin/{{.BINARY}}\n        msg: \"Build first: task build\"\n      - sh: git diff --quiet\n        msg: \"Uncommitted changes detected\"\n    cmds:\n      - ./scripts/deploy.sh\n\n# Install: https://taskfile.dev/installation/\n# Run:\ntask          # Default task\ntask build    # Specific task\ntask --list   # List all tasks"
      },
      {
        "title": "Make vs Just vs Task",
        "body": "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"
      },
      {
        "title": "Tips",
        "body": "The number one Makefile bug: using spaces instead of tabs for indentation. Make requires literal TAB characters in recipes.\n.PHONY every target that isn't a real file. Without it, make clean won't run if a file named clean exists.\nUse @ prefix to suppress command echo for cleaner output: @echo \"Building...\" prints only \"Building...\", not the echo command itself.\nThe self-documenting help target (with ## comments) is worth adding to every Makefile. make help becomes the project's command reference.\nMake is overkill for simple task running. If you just want named commands, Just or Task are simpler and don't have the TAB footgun.\nUse ?= for variables users might want to override: PORT ?= 8080 lets PORT=9090 make run work.\nFor polyglot projects (Go + Python + Docker), a Makefile at the root that delegates to language-specific tools is a clean pattern.\nMake's file-based dependency tracking is genuinely powerful for build systems. If your project compiles files, Make's target: prerequisites model avoids unnecessary rebuilds."
      }
    ],
    "body": "Makefile & Build\n\nWrite 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.\n\nWhen to Use\nAutomating build, test, lint, deploy commands\nDefining dependencies between tasks (build before test)\nCreating a project-level task runner (consistent across team)\nReplacing long CLI commands with short memorable targets\nManaging multi-step build processes\nAny project that needs a make build && make test && make deploy workflow\nMakefile Basics\nStructure\n# target: prerequisites\n#     recipe (MUST be indented with TAB, not spaces)\n\nbuild: src/main.go\n\tgo build -o bin/app src/main.go\n\ntest: build\n\tgo test ./...\n\nclean:\n\trm -rf bin/\n\n# First target is the default (runs with bare `make`)\n\nVariables\n# Simple assignment\nCC = gcc\nCFLAGS = -Wall -O2\n\n# Deferred assignment (expanded when used)\nFILES = $(wildcard src/*.go)\n\n# Immediate assignment (expanded when defined)\nVERSION := $(shell git describe --tags --always)\n\n# Conditional assignment (only if not already set)\nPORT ?= 8080\n\n# Use variables\nbuild:\n\t$(CC) $(CFLAGS) -o app main.c\n\t@echo \"Version: $(VERSION)\"\n\nAutomatic variables\n# $@ = target name\n# $< = first prerequisite\n# $^ = all prerequisites\n# $* = stem (pattern match)\n# $(@D) = directory of target\n# $(@F) = filename of target\n\nbin/app: src/main.go src/util.go\n\tgo build -o $@ $^\n# $@ = bin/app\n# $^ = src/main.go src/util.go\n# $< = src/main.go\n\n# Pattern rule\n%.o: %.c\n\t$(CC) -c -o $@ $<\n# For foo.o: $@ = foo.o, $< = foo.c, $* = foo\n\nPhony targets (not files)\n# Without .PHONY, if a file named \"clean\" exists, `make clean` does nothing\n.PHONY: build test clean lint fmt help\n\nbuild:\n\tgo build -o bin/app ./cmd/app\n\ntest:\n\tgo test ./...\n\nclean:\n\trm -rf bin/ dist/\n\n# List all targets\nhelp:\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\nSelf-documenting Makefile\n.DEFAULT_GOAL := help\n\nbuild: ## Build the application\n\tgo build -o bin/app ./cmd/app\n\ntest: ## Run all tests\n\tgo test -v ./...\n\nlint: ## Run linters\n\tgolangci-lint run\n\nclean: ## Remove build artifacts\n\trm -rf bin/ dist/\n\nhelp: ## Show this help\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2}'\n\nLanguage-Specific Makefiles\nGo\nBINARY_NAME := myapp\nVERSION := $(shell git describe --tags --always --dirty)\nLDFLAGS := -ldflags \"-X main.version=$(VERSION)\"\nGOFILES := $(shell find . -name '*.go' -not -path './vendor/*')\n\n.PHONY: all build test lint clean run\n\nall: lint test build\n\nbuild: ## Build binary\n\tCGO_ENABLED=0 go build $(LDFLAGS) -o bin/$(BINARY_NAME) ./cmd/$(BINARY_NAME)\n\ntest: ## Run tests\n\tgo test -race -coverprofile=coverage.out ./...\n\ntest-coverage: test ## Show coverage report\n\tgo tool cover -html=coverage.out\n\nlint: ## Run linters\n\tgolangci-lint run ./...\n\nfmt: ## Format code\n\tgofmt -w $(GOFILES)\n\nrun: build ## Build and run\n\t./bin/$(BINARY_NAME)\n\nclean: ## Clean build artifacts\n\trm -rf bin/ coverage.out\n\n# Cross-compilation\nbuild-linux: ## Build for Linux\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME)\n\nbuild-all: ## Build for all platforms\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME)\n\tGOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-darwin-arm64 ./cmd/$(BINARY_NAME)\n\tGOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/$(BINARY_NAME)-windows-amd64.exe ./cmd/$(BINARY_NAME)\n\nPython\nPYTHON := python3\nVENV := .venv\nBIN := $(VENV)/bin\n\n.PHONY: all install test lint fmt clean run\n\nall: install lint test\n\n$(VENV)/bin/activate:\n\t$(PYTHON) -m venv $(VENV)\n\t$(BIN)/pip install --upgrade pip\n\ninstall: $(VENV)/bin/activate ## Install dependencies\n\t$(BIN)/pip install -r requirements.txt\n\t$(BIN)/pip install -r requirements-dev.txt\n\ntest: ## Run tests\n\t$(BIN)/pytest -v --cov=src --cov-report=term-missing\n\nlint: ## Run linters\n\t$(BIN)/ruff check src/ tests/\n\t$(BIN)/mypy src/\n\nfmt: ## Format code\n\t$(BIN)/ruff format src/ tests/\n\nrun: ## Run application\n\t$(BIN)/python -m src.main\n\nclean: ## Remove venv and caches\n\trm -rf $(VENV) __pycache__ .pytest_cache .mypy_cache .ruff_cache\n\tfind . -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true\n\nNode.js / TypeScript\n.PHONY: all install build test lint clean dev\n\nall: install lint test build\n\nnode_modules: package.json\n\tnpm install\n\t@touch node_modules\n\ninstall: node_modules ## Install dependencies\n\nbuild: node_modules ## Build TypeScript\n\tnpx tsc\n\ntest: node_modules ## Run tests\n\tnpx vitest run\n\ntest-watch: node_modules ## Run tests in watch mode\n\tnpx vitest\n\nlint: node_modules ## Lint code\n\tnpx eslint src/ --ext .ts,.tsx\n\tnpx tsc --noEmit\n\nfmt: node_modules ## Format code\n\tnpx prettier --write 'src/**/*.{ts,tsx}'\n\ndev: node_modules ## Run in development mode\n\tnpx tsx watch src/index.ts\n\nclean: ## Clean build artifacts\n\trm -rf dist/ node_modules/.cache\n\nDocker\nIMAGE_NAME := myapp\nVERSION := $(shell git describe --tags --always)\nREGISTRY := ghcr.io/myorg\n\n.PHONY: build push run stop clean\n\nbuild: ## Build Docker image\n\tdocker build -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):latest .\n\npush: build ## Push to registry\n\tdocker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):$(VERSION)\n\tdocker tag $(IMAGE_NAME):latest $(REGISTRY)/$(IMAGE_NAME):latest\n\tdocker push $(REGISTRY)/$(IMAGE_NAME):$(VERSION)\n\tdocker push $(REGISTRY)/$(IMAGE_NAME):latest\n\nrun: ## Run container\n\tdocker run --rm -p 8080:8080 --name $(IMAGE_NAME) $(IMAGE_NAME):latest\n\nstop: ## Stop container\n\tdocker stop $(IMAGE_NAME) 2>/dev/null || true\n\nclean: ## Remove images\n\tdocker rmi $(IMAGE_NAME):$(VERSION) $(IMAGE_NAME):latest 2>/dev/null || true\n\ncompose-up: ## Start with docker compose\n\tdocker compose up -d --build\n\ncompose-down: ## Stop compose\n\tdocker compose down\n\ncompose-logs: ## Follow compose logs\n\tdocker compose logs -f\n\nAdvanced Patterns\nConditional logic\n# OS detection\nUNAME := $(shell uname -s)\nifeq ($(UNAME),Darwin)\n    SED := sed -i ''\nelse\n    SED := sed -i\nendif\n\n# Environment-based config\nENV ?= development\nifeq ($(ENV),production)\n    CFLAGS += -O2\n    LDFLAGS += -s -w\nelse\n    CFLAGS += -g -O0\nendif\n\n# Check if command exists\nHAS_DOCKER := $(shell command -v docker 2>/dev/null)\ndocker-build:\nifndef HAS_DOCKER\n\t$(error \"docker is not installed\")\nendif\n\tdocker build -t myapp .\n\nMulti-directory builds\nSERVICES := api worker scheduler\n\n.PHONY: build-all test-all $(SERVICES)\n\nbuild-all: $(SERVICES)\n\n$(SERVICES):\n\t$(MAKE) -C services/$@ build\n\ntest-all:\n\t@for svc in $(SERVICES); do \\\n\t\techo \"Testing $$svc...\"; \\\n\t\t$(MAKE) -C services/$$svc test || exit 1; \\\n\tdone\n\nInclude other Makefiles\n# Split large Makefile into modules\ninclude mk/docker.mk\ninclude mk/test.mk\ninclude mk/deploy.mk\n\n# Optional include (no error if missing)\n-include .env.mk\n\nSilent execution and output control\n# @ suppresses command echo\ninstall:\n\t@echo \"Installing dependencies...\"\n\t@npm install\n\n# .SILENT for entire targets\n.SILENT: help clean\n\n# Make less verbose globally\nMAKEFLAGS += --no-print-directory\n\nJust (Modern Alternative)\nJustfile syntax\n# justfile — simpler than Make, no TAB requirement\n\n# Set shell\nset shell := [\"bash\", \"-euo\", \"pipefail\", \"-c\"]\n\n# Variables\nversion := `git describe --tags --always`\ndefault_port := \"8080\"\n\n# Default recipe (first one)\ndefault: lint test build\n\n# Recipes\nbuild: ## Build the application\n    go build -ldflags \"-X main.version={{version}}\" -o bin/app ./cmd/app\n\ntest: ## Run tests\n    go test -race ./...\n\nlint: ## Run linters\n    golangci-lint run\n\nrun port=default_port: build ## Run with optional port\n    ./bin/app --port {{port}}\n\nclean: ## Clean artifacts\n    rm -rf bin/ dist/\n\n# Recipes with dependencies\ndeploy: build test\n    ./scripts/deploy.sh\n\n# OS-specific\n[linux]\ninstall-deps:\n    sudo apt install -y build-essential\n\n[macos]\ninstall-deps:\n    brew install go golangci-lint\n\n# List recipes\nhelp:\n    @just --list\n\n# Install: https://github.com/casey/just\n# Run:\njust          # Default recipe\njust build    # Specific recipe\njust run 9090 # With argument\njust --list   # List all recipes\n\nTask (Go Task Runner)\nTaskfile.yml\n# Taskfile.yml\nversion: '3'\n\nvars:\n  VERSION:\n    sh: git describe --tags --always\n  BINARY: myapp\n\ntasks:\n  default:\n    deps: [lint, test, build]\n\n  build:\n    desc: Build the application\n    cmds:\n      - go build -ldflags \"-X main.version={{.VERSION}}\" -o bin/{{.BINARY}} ./cmd/{{.BINARY}}\n    sources:\n      - ./**/*.go\n    generates:\n      - bin/{{.BINARY}}\n\n  test:\n    desc: Run tests\n    cmds:\n      - go test -race ./...\n\n  lint:\n    desc: Run linters\n    cmds:\n      - golangci-lint run\n\n  run:\n    desc: Build and run\n    deps: [build]\n    cmds:\n      - ./bin/{{.BINARY}} {{.CLI_ARGS}}\n\n  clean:\n    desc: Clean artifacts\n    cmds:\n      - rm -rf bin/ dist/\n\n  docker:build:\n    desc: Build Docker image\n    cmds:\n      - docker build -t {{.BINARY}}:{{.VERSION}} .\n\n  # Task with preconditions\n  deploy:\n    desc: Deploy to production\n    preconditions:\n      - sh: test -f bin/{{.BINARY}}\n        msg: \"Build first: task build\"\n      - sh: git diff --quiet\n        msg: \"Uncommitted changes detected\"\n    cmds:\n      - ./scripts/deploy.sh\n\n# Install: https://taskfile.dev/installation/\n# Run:\ntask          # Default task\ntask build    # Specific task\ntask --list   # List all tasks\n\nMake vs Just vs Task\nFeature\tMake\tJust\tTask\nConfig format\tMakefile (TAB-sensitive)\tjustfile\tTaskfile.yml\nDependencies\tFile-based + phony\tRecipe-based\tTask-based\nFile change detection\tBuilt-in\tNo\tsources/generates\nVariables\tYes (complex)\tYes (simple)\tYes (YAML)\nCross-platform\tNeeds make installed\tSingle binary\tSingle binary\nLearning curve\tHigh\tLow\tLow\nBest for\tC/C++ builds, complex deps\tTask runner replacement\tYAML-native projects\nTips\nThe number one Makefile bug: using spaces instead of tabs for indentation. Make requires literal TAB characters in recipes.\n.PHONY every target that isn't a real file. Without it, make clean won't run if a file named clean exists.\nUse @ prefix to suppress command echo for cleaner output: @echo \"Building...\" prints only \"Building...\", not the echo command itself.\nThe self-documenting help target (with ## comments) is worth adding to every Makefile. make help becomes the project's command reference.\nMake is overkill for simple task running. If you just want named commands, Just or Task are simpler and don't have the TAB footgun.\nUse ?= for variables users might want to override: PORT ?= 8080 lets PORT=9090 make run work.\nFor polyglot projects (Go + Python + Docker), a Makefile at the root that delegates to language-specific tools is a clean pattern.\nMake's file-based dependency tracking is genuinely powerful for build systems. If your project compiles files, Make's target: prerequisites model avoids unnecessary rebuilds."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/gitgoodordietrying/makefile-build",
    "publisherUrl": "https://clawhub.ai/gitgoodordietrying/makefile-build",
    "owner": "gitgoodordietrying",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/makefile-build",
    "downloadUrl": "https://openagent3.xyz/downloads/makefile-build",
    "agentUrl": "https://openagent3.xyz/skills/makefile-build/agent",
    "manifestUrl": "https://openagent3.xyz/skills/makefile-build/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/makefile-build/agent.md"
  }
}