@hybridlabor-api/aos 4.8.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/{agents.md → AGENTS.md} +3 -1
- package/.agents/nodes.json +3 -1
- package/.agents/vendor-manifest.json +23 -1
- package/.claude/agents/godmode-media-eventtech.md +1 -1
- package/.claude/hooks/conventional-commits.mjs +125 -0
- package/.claude/hooks/env-file-protection.mjs +105 -0
- package/.claude/hooks/go-gate.mjs +101 -81
- package/.claude/hooks/memb-inject.mjs +29 -1
- package/.claude/settings.json +13 -0
- package/.claude/workflows/startcycle-dispatch.mjs +23 -1
- package/.opencode/agents/godmode-media-eventtech.md +1 -1
- package/.opencode/plugins/bdb-aos.js +31 -4
- package/CLAUDE.md +0 -571
- package/README.de.md +1 -1
- package/README.md +1 -1
- package/README.pt.md +1 -1
- package/THIRD_PARTY_NOTICES.md +126 -0
- package/bin/aos-doctor.mjs +1 -1
- package/docs/skills_table.md +1 -1
- package/installer.js +187 -55
- package/package.json +7 -3
- package/packages/aos-cli/README.md +80 -0
- package/packages/aos-cli/bin/aos-cli.mjs +134 -0
- package/packages/aos-cli/core-skills.json +12 -0
- package/packages/aos-cli/extensions/aos.ts +321 -0
- package/packages/aos-cli/package-lock.json +1923 -0
- package/packages/aos-cli/package.json +29 -0
- package/packages/aos-cli/scripts/check-theme.mjs +63 -0
- package/packages/aos-cli/themes/aos.json +97 -0
- package/scripts/build-plugin-manifest.mjs +131 -0
- package/scripts/validate-skills.mjs +81 -6
- package/skills/basic/ao-orchestrator/SKILL.md +116 -0
- package/skills/basic/bdb-eventagency-skill/SKILL.md +252 -0
- package/skills/basic/bdb-shipping-skill/SKILL.md +161 -0
- package/skills/basic/godmode-eventtech/SKILL.md +4 -1
- package/skills/global_config/agenttrail/SKILL.md +6 -1
- package/skills/global_config/aos-project-init/SKILL.md +2 -0
- package/skills/global_config/aos-project-init/assets/AGENTS.template.md +1 -1
- package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +1 -1
- package/skills/global_config/aos-setup/SKILL.md +1 -1
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/ask-tim/SKILL.md +7 -7
- package/skills/global_config/bash-script-generator/SKILL.md +201 -0
- package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
- package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
- package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
- package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
- package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
- package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
- package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
- package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
- package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
- package/skills/global_config/bash-script-validator/SKILL.md +249 -0
- package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
- package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
- package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
- package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
- package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
- package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
- package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
- package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
- package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
- package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
- package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
- package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
- package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
- package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
- package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
- package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
- package/skills/global_config/ci-pipeline/SKILL.md +135 -0
- package/skills/global_config/deja-memory/SKILL.md +3 -1
- package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
- package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
- package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
- package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
- package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
- package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
- package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
- package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
- package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
- package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
- package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
- package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
- package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
- package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
- package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
- package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
- package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
- package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
- package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
- package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
- package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
- package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
- package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
- package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
- package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
- package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
- package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
- package/skills/global_config/github-actions-generator/SKILL.md +353 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
- package/skills/global_config/github-actions-generator/examples/README.md +147 -0
- package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
- package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
- package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
- package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
- package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
- package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
- package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
- package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
- package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
- package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
- package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
- package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
- package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
- package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
- package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
- package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
- package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
- package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
- package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
- package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
- package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
- package/skills/global_config/github-actions-templates/SKILL.md +7 -0
- package/skills/global_config/github-actions-validator/SKILL.md +576 -0
- package/skills/global_config/github-actions-validator/examples/README.md +88 -0
- package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
- package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
- package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
- package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
- package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
- package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
- package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
- package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
- package/skills/global_config/github-actions-validator/references/runners.md +317 -0
- package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
- package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
- package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
- package/skills/global_config/makefile-generator/SKILL.md +614 -0
- package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
- package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
- package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
- package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
- package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
- package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
- package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
- package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
- package/skills/global_config/makefile-validator/SKILL.md +244 -0
- package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
- package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
- package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
- package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
- package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
- package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
- package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
- package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
- package/skills/global_config/plan-canvas/SKILL.md +9 -2
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +10 -2
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +1 -1
- package/skills/global_config/read-the-damn-docs/SKILL.md +175 -0
- package/skills/global_config/requesting-code-review/SKILL.md +98 -0
- package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
- package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
- package/skills/global_config/verification-before-completion/SKILL.md +123 -0
- package/skills/global_config/writing-plans/SKILL.md +126 -46
- package/skills/global_config/writing-plans-legacy/SKILL.md +152 -0
- package/.claude/CLAUDE.md +0 -12
- package/mcps/RhinoMCP/docs/content/docs/getting-started/gemini.md +0 -61
- /package/{GEMINI.md → RULES.md} +0 -0
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
# Makefile Best Practices
|
|
2
|
+
|
|
3
|
+
Comprehensive guide to writing professional, maintainable, and efficient Makefiles.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Essential Special Targets](#essential-special-targets)
|
|
8
|
+
2. [File Organization](#file-organization)
|
|
9
|
+
3. [Target Declarations](#target-declarations)
|
|
10
|
+
4. [Variable Management](#variable-management)
|
|
11
|
+
5. [Recipe Best Practices](#recipe-best-practices)
|
|
12
|
+
6. [Dependency Management](#dependency-management)
|
|
13
|
+
7. [Performance Optimization](#performance-optimization)
|
|
14
|
+
8. [Portability](#portability)
|
|
15
|
+
9. [Documentation](#documentation)
|
|
16
|
+
10. [Security](#security)
|
|
17
|
+
11. [Advanced Patterns](#advanced-patterns)
|
|
18
|
+
|
|
19
|
+
## Modern Makefile Header (Recommended)
|
|
20
|
+
|
|
21
|
+
For modern, robust Makefiles, start with this recommended preamble from [Jacob Davis-Hansson](https://tech.davis-hansson.com/p/make/):
|
|
22
|
+
|
|
23
|
+
```makefile
|
|
24
|
+
# Modern Makefile Header
|
|
25
|
+
SHELL := bash
|
|
26
|
+
.ONESHELL:
|
|
27
|
+
.SHELLFLAGS := -eu -o pipefail -c
|
|
28
|
+
.DELETE_ON_ERROR:
|
|
29
|
+
MAKEFLAGS += --warn-undefined-variables
|
|
30
|
+
MAKEFLAGS += --no-builtin-rules
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Explanation:**
|
|
34
|
+
|
|
35
|
+
| Setting | Purpose |
|
|
36
|
+
|---------|---------|
|
|
37
|
+
| `SHELL := bash` | Use bash instead of /bin/sh for modern shell features |
|
|
38
|
+
| `.ONESHELL:` | Run entire recipe in single shell (enables multi-line scripts) |
|
|
39
|
+
| `.SHELLFLAGS := -eu -o pipefail -c` | Stop on errors (-e), undefined vars (-u), pipe failures |
|
|
40
|
+
| `.DELETE_ON_ERROR:` | Delete target on recipe failure (prevents corrupt builds) |
|
|
41
|
+
| `--warn-undefined-variables` | Alert on undefined Make variable references |
|
|
42
|
+
| `--no-builtin-rules` | Disable built-in implicit rules for faster builds |
|
|
43
|
+
|
|
44
|
+
**Note:** This preamble is for GNU Make 4.0+. For maximum portability, use a simpler header.
|
|
45
|
+
|
|
46
|
+
## Essential Special Targets
|
|
47
|
+
|
|
48
|
+
GNU Make provides several special targets that should be used in professional Makefiles.
|
|
49
|
+
|
|
50
|
+
### .DELETE_ON_ERROR (Critical)
|
|
51
|
+
|
|
52
|
+
**Always include `.DELETE_ON_ERROR:`** at the top of your Makefile. This ensures partially built targets are deleted when a recipe fails, preventing corrupt builds.
|
|
53
|
+
|
|
54
|
+
```makefile
|
|
55
|
+
# CRITICAL: Delete target on recipe failure
|
|
56
|
+
.DELETE_ON_ERROR:
|
|
57
|
+
|
|
58
|
+
# Rest of Makefile follows...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Why it matters:**
|
|
62
|
+
- Without this, a failed build leaves a partial/corrupt file
|
|
63
|
+
- Next `make` run sees the file exists and skips rebuilding
|
|
64
|
+
- Results in broken builds that are hard to debug
|
|
65
|
+
|
|
66
|
+
**From GNU Make Manual:** *"This is almost always what you want make to do, but it is not historical practice; so for compatibility, you must explicitly request it."*
|
|
67
|
+
|
|
68
|
+
**Exception:** Use `.PRECIOUS` to protect specific targets that should be preserved even on error:
|
|
69
|
+
|
|
70
|
+
```makefile
|
|
71
|
+
.DELETE_ON_ERROR:
|
|
72
|
+
.PRECIOUS: expensive-to-rebuild.dat
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### .PHONY (Always Required)
|
|
76
|
+
|
|
77
|
+
Declare non-file targets as phony to avoid conflicts and improve performance:
|
|
78
|
+
|
|
79
|
+
```makefile
|
|
80
|
+
.PHONY: all build clean test install
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### .ONESHELL (For Multi-line Recipes)
|
|
84
|
+
|
|
85
|
+
Run entire recipe in a single shell invocation:
|
|
86
|
+
|
|
87
|
+
```makefile
|
|
88
|
+
.ONESHELL:
|
|
89
|
+
|
|
90
|
+
deploy:
|
|
91
|
+
set -e
|
|
92
|
+
echo "Deploying..."
|
|
93
|
+
cd /app
|
|
94
|
+
git pull
|
|
95
|
+
./restart.sh
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Without `.ONESHELL`**, each line runs in a separate shell, so `cd` has no effect on subsequent lines.
|
|
99
|
+
|
|
100
|
+
### .SUFFIXES (For Performance)
|
|
101
|
+
|
|
102
|
+
Clear built-in suffix rules to speed up builds:
|
|
103
|
+
|
|
104
|
+
```makefile
|
|
105
|
+
# Disable all built-in suffix rules
|
|
106
|
+
.SUFFIXES:
|
|
107
|
+
|
|
108
|
+
# Only keep rules you need (optional)
|
|
109
|
+
.SUFFIXES: .c .o
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Why:** GNU Make has ~90 built-in implicit rules. Clearing them speeds up rule resolution.
|
|
113
|
+
|
|
114
|
+
### Complete Special Targets Header
|
|
115
|
+
|
|
116
|
+
```makefile
|
|
117
|
+
# Modern Makefile Header
|
|
118
|
+
.DELETE_ON_ERROR:
|
|
119
|
+
.SUFFIXES:
|
|
120
|
+
|
|
121
|
+
.PHONY: all build clean test install deploy
|
|
122
|
+
|
|
123
|
+
# Your targets follow...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## File Organization
|
|
127
|
+
|
|
128
|
+
### Directory Structure
|
|
129
|
+
|
|
130
|
+
```makefile
|
|
131
|
+
# Organized Makefile structure
|
|
132
|
+
.PHONY: all clean test install
|
|
133
|
+
|
|
134
|
+
# Variables section
|
|
135
|
+
PROJECT := myapp
|
|
136
|
+
VERSION := 1.0.0
|
|
137
|
+
BUILD_DIR := build
|
|
138
|
+
SRC_DIR := src
|
|
139
|
+
|
|
140
|
+
# Include external makefiles
|
|
141
|
+
include config.mk
|
|
142
|
+
include rules/*.mk
|
|
143
|
+
|
|
144
|
+
# Default target (should be first)
|
|
145
|
+
all: build test
|
|
146
|
+
|
|
147
|
+
# Build targets
|
|
148
|
+
build: $(BUILD_DIR)/$(PROJECT)
|
|
149
|
+
|
|
150
|
+
# ... more targets
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Modular Organization
|
|
154
|
+
|
|
155
|
+
Use `include` for large projects:
|
|
156
|
+
|
|
157
|
+
```makefile
|
|
158
|
+
# Main Makefile
|
|
159
|
+
include config/variables.mk
|
|
160
|
+
include rules/build.mk
|
|
161
|
+
include rules/test.mk
|
|
162
|
+
include rules/deploy.mk
|
|
163
|
+
|
|
164
|
+
.PHONY: all
|
|
165
|
+
all: build test
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Namespace Targets
|
|
169
|
+
|
|
170
|
+
Use `/` as delimiter for namespaced targets:
|
|
171
|
+
|
|
172
|
+
```makefile
|
|
173
|
+
# Good: Namespaced targets
|
|
174
|
+
.PHONY: docker/build docker/push docker/clean
|
|
175
|
+
docker/build:
|
|
176
|
+
docker build -t $(IMAGE) .
|
|
177
|
+
|
|
178
|
+
docker/push:
|
|
179
|
+
docker push $(IMAGE)
|
|
180
|
+
|
|
181
|
+
docker/clean:
|
|
182
|
+
docker rmi $(IMAGE)
|
|
183
|
+
|
|
184
|
+
# Avoid: Flat namespace
|
|
185
|
+
.PHONY: docker-build docker-push docker-clean
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Target Declarations
|
|
189
|
+
|
|
190
|
+
### Always Declare .PHONY
|
|
191
|
+
|
|
192
|
+
Declare targets that don't create files as phony:
|
|
193
|
+
|
|
194
|
+
```makefile
|
|
195
|
+
# GOOD: Proper .PHONY declarations
|
|
196
|
+
.PHONY: all clean test install build deploy
|
|
197
|
+
|
|
198
|
+
all: build test
|
|
199
|
+
|
|
200
|
+
clean:
|
|
201
|
+
rm -rf $(BUILD_DIR)
|
|
202
|
+
|
|
203
|
+
test:
|
|
204
|
+
go test ./...
|
|
205
|
+
|
|
206
|
+
# BAD: Missing .PHONY - causes issues if files named 'clean' or 'test' exist
|
|
207
|
+
clean:
|
|
208
|
+
rm -rf build
|
|
209
|
+
|
|
210
|
+
test:
|
|
211
|
+
go test ./...
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Organize .PHONY Declarations
|
|
215
|
+
|
|
216
|
+
```makefile
|
|
217
|
+
# Group related phony targets
|
|
218
|
+
.PHONY: all build clean
|
|
219
|
+
.PHONY: test test-unit test-integration
|
|
220
|
+
.PHONY: install uninstall
|
|
221
|
+
.PHONY: docker/build docker/push docker/clean
|
|
222
|
+
|
|
223
|
+
# Or use a single declaration (mbake can organize this)
|
|
224
|
+
.PHONY: all build clean test test-unit test-integration install uninstall
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Default Target
|
|
228
|
+
|
|
229
|
+
First target is the default (or use .DEFAULT_GOAL):
|
|
230
|
+
|
|
231
|
+
```makefile
|
|
232
|
+
# Method 1: First target is default
|
|
233
|
+
.PHONY: all
|
|
234
|
+
all: build test
|
|
235
|
+
|
|
236
|
+
# Method 2: Explicit default goal
|
|
237
|
+
.DEFAULT_GOAL := build
|
|
238
|
+
|
|
239
|
+
.PHONY: build test
|
|
240
|
+
build:
|
|
241
|
+
go build -o app
|
|
242
|
+
|
|
243
|
+
test:
|
|
244
|
+
go test ./...
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Variable Management
|
|
248
|
+
|
|
249
|
+
### Variable Assignment Operators
|
|
250
|
+
|
|
251
|
+
Choose the right operator for your use case:
|
|
252
|
+
|
|
253
|
+
```makefile
|
|
254
|
+
# Simple assignment (=) - Recursive expansion (evaluated when used)
|
|
255
|
+
CFLAGS = -Wall $(OPTIMIZE)
|
|
256
|
+
OPTIMIZE = -O2
|
|
257
|
+
# CFLAGS will expand to: -Wall -O2 (recursive)
|
|
258
|
+
|
|
259
|
+
# Immediate assignment (:=) - Expanded immediately (RECOMMENDED for most cases)
|
|
260
|
+
BUILD_TIME := $(shell date +%Y%m%d-%H%M%S)
|
|
261
|
+
VERSION := 1.0.0
|
|
262
|
+
# Evaluated once, avoids repeated shell calls
|
|
263
|
+
|
|
264
|
+
# Conditional assignment (?=) - Set only if not already defined
|
|
265
|
+
CC ?= gcc
|
|
266
|
+
PREFIX ?= /usr/local
|
|
267
|
+
# Allows environment variable override
|
|
268
|
+
|
|
269
|
+
# Append (+=) - Add to existing value
|
|
270
|
+
CFLAGS := -Wall
|
|
271
|
+
CFLAGS += -Wextra
|
|
272
|
+
CFLAGS += -O2
|
|
273
|
+
# CFLAGS = -Wall -Wextra -O2
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Use := for Most Variables
|
|
277
|
+
|
|
278
|
+
```makefile
|
|
279
|
+
# GOOD: Immediate expansion (predictable, faster)
|
|
280
|
+
BUILD_DIR := build
|
|
281
|
+
SRC_FILES := $(wildcard src/*.c)
|
|
282
|
+
TIMESTAMP := $(shell date +%s)
|
|
283
|
+
|
|
284
|
+
# AVOID: Recursive expansion (unpredictable, slower)
|
|
285
|
+
BUILD_DIR = build
|
|
286
|
+
SRC_FILES = $(wildcard src/*.c) # Re-evaluated every time!
|
|
287
|
+
TIMESTAMP = $(shell date +%s) # Shell called multiple times!
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Sane Defaults with ?=
|
|
291
|
+
|
|
292
|
+
```makefile
|
|
293
|
+
# Allow user/environment override
|
|
294
|
+
CC ?= gcc
|
|
295
|
+
CXX ?= g++
|
|
296
|
+
PREFIX ?= /usr/local
|
|
297
|
+
DESTDIR ?=
|
|
298
|
+
VERBOSE ?= 0
|
|
299
|
+
|
|
300
|
+
# Usage:
|
|
301
|
+
# make # Uses defaults
|
|
302
|
+
# make CC=clang # Override CC
|
|
303
|
+
# PREFIX=/opt make # Override via environment
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Variable Naming
|
|
307
|
+
|
|
308
|
+
```makefile
|
|
309
|
+
# GOOD: Clear, consistent naming
|
|
310
|
+
PROJECT_NAME := myapp
|
|
311
|
+
BUILD_DIR := build
|
|
312
|
+
SOURCE_FILES := $(wildcard src/*.c)
|
|
313
|
+
COMPILER_FLAGS := -Wall -Wextra -O2
|
|
314
|
+
|
|
315
|
+
# AVOID: Unclear abbreviations
|
|
316
|
+
PROJ := myapp
|
|
317
|
+
BDIR := build
|
|
318
|
+
SRCS := $(wildcard src/*.c)
|
|
319
|
+
FLAGS := -Wall
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Recipe Best Practices
|
|
323
|
+
|
|
324
|
+
### Use Tabs, Not Spaces
|
|
325
|
+
|
|
326
|
+
```makefile
|
|
327
|
+
# GOOD: Tab character (required)
|
|
328
|
+
build:
|
|
329
|
+
@echo "Building..."
|
|
330
|
+
go build -o app
|
|
331
|
+
|
|
332
|
+
# BAD: Spaces (will fail)
|
|
333
|
+
build:
|
|
334
|
+
@echo "Building..."
|
|
335
|
+
go build -o app
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Note**: Makefiles require TAB characters for recipes. Configure your editor to use tabs for Makefiles.
|
|
339
|
+
|
|
340
|
+
### Error Handling
|
|
341
|
+
|
|
342
|
+
```makefile
|
|
343
|
+
# Method 1: Prefix with @ to suppress echo, - to ignore errors
|
|
344
|
+
clean:
|
|
345
|
+
@echo "Cleaning build artifacts..."
|
|
346
|
+
-rm -rf $(BUILD_DIR)
|
|
347
|
+
@echo "Done!"
|
|
348
|
+
|
|
349
|
+
# Method 2: Use || for conditional error handling
|
|
350
|
+
build:
|
|
351
|
+
mkdir -p $(BUILD_DIR) || exit 1
|
|
352
|
+
go build -o $(BUILD_DIR)/app || exit 1
|
|
353
|
+
|
|
354
|
+
# Method 3: Use set -e for strict error handling
|
|
355
|
+
test:
|
|
356
|
+
@set -e; \
|
|
357
|
+
echo "Running tests..."; \
|
|
358
|
+
go test ./...; \
|
|
359
|
+
echo "All tests passed!"
|
|
360
|
+
|
|
361
|
+
# Method 4: Check exit codes explicitly
|
|
362
|
+
deploy:
|
|
363
|
+
@./scripts/deploy.sh
|
|
364
|
+
@if [ $$? -ne 0 ]; then \
|
|
365
|
+
echo "Deployment failed!"; \
|
|
366
|
+
exit 1; \
|
|
367
|
+
fi
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Multi-line Recipes
|
|
371
|
+
|
|
372
|
+
```makefile
|
|
373
|
+
# Use backslash for line continuation
|
|
374
|
+
build: $(SOURCES)
|
|
375
|
+
@echo "Building $(PROJECT)..."; \
|
|
376
|
+
mkdir -p $(BUILD_DIR); \
|
|
377
|
+
$(CC) $(CFLAGS) -o $(BUILD_DIR)/$(PROJECT) $(SOURCES); \
|
|
378
|
+
echo "Build complete!"
|
|
379
|
+
|
|
380
|
+
# Or use .ONESHELL for easier multi-line scripts
|
|
381
|
+
.ONESHELL:
|
|
382
|
+
test:
|
|
383
|
+
echo "Running tests..."
|
|
384
|
+
for file in tests/*.sh; do
|
|
385
|
+
bash $$file
|
|
386
|
+
done
|
|
387
|
+
echo "All tests passed!"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Silent vs Verbose Output
|
|
391
|
+
|
|
392
|
+
```makefile
|
|
393
|
+
# Use @ to suppress command echo
|
|
394
|
+
.PHONY: build
|
|
395
|
+
build:
|
|
396
|
+
@echo "Building..."
|
|
397
|
+
@$(CC) $(CFLAGS) -o app $(SOURCES)
|
|
398
|
+
|
|
399
|
+
# Optional verbose mode
|
|
400
|
+
VERBOSE ?= 0
|
|
401
|
+
ifeq ($(VERBOSE),1)
|
|
402
|
+
Q :=
|
|
403
|
+
else
|
|
404
|
+
Q := @
|
|
405
|
+
endif
|
|
406
|
+
|
|
407
|
+
build:
|
|
408
|
+
$(Q)echo "Building..."
|
|
409
|
+
$(Q)$(CC) $(CFLAGS) -o app $(SOURCES)
|
|
410
|
+
|
|
411
|
+
# Usage:
|
|
412
|
+
# make build # Silent
|
|
413
|
+
# make build VERBOSE=1 # Verbose
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Dependency Management
|
|
417
|
+
|
|
418
|
+
### Specify Dependencies Correctly
|
|
419
|
+
|
|
420
|
+
```makefile
|
|
421
|
+
# GOOD: Proper dependency chain
|
|
422
|
+
app: $(OBJECTS)
|
|
423
|
+
$(CC) -o $@ $^
|
|
424
|
+
|
|
425
|
+
%.o: %.c %.h
|
|
426
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
427
|
+
|
|
428
|
+
# BAD: Missing dependencies - app won't rebuild when headers change
|
|
429
|
+
app: $(OBJECTS)
|
|
430
|
+
$(CC) -o $@ $^
|
|
431
|
+
|
|
432
|
+
%.o: %.c
|
|
433
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Auto-generate Dependencies (C/C++)
|
|
437
|
+
|
|
438
|
+
```makefile
|
|
439
|
+
# Automatic dependency generation
|
|
440
|
+
DEPDIR := .deps
|
|
441
|
+
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
|
|
442
|
+
|
|
443
|
+
%.o: %.c $(DEPDIR)/%.d | $(DEPDIR)
|
|
444
|
+
$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@
|
|
445
|
+
|
|
446
|
+
$(DEPDIR):
|
|
447
|
+
@mkdir -p $@
|
|
448
|
+
|
|
449
|
+
# Include generated dependency files
|
|
450
|
+
-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SOURCES)))
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Order-Only Prerequisites
|
|
454
|
+
|
|
455
|
+
Use `|` for prerequisites that shouldn't trigger rebuilds:
|
|
456
|
+
|
|
457
|
+
```makefile
|
|
458
|
+
# Regular prerequisites trigger rebuild
|
|
459
|
+
$(BUILD_DIR)/app: $(SOURCES)
|
|
460
|
+
$(CC) -o $@ $^
|
|
461
|
+
|
|
462
|
+
# Order-only prerequisites (directories) don't trigger rebuild
|
|
463
|
+
$(BUILD_DIR)/app: $(SOURCES) | $(BUILD_DIR)
|
|
464
|
+
$(CC) -o $@ $^
|
|
465
|
+
|
|
466
|
+
$(BUILD_DIR):
|
|
467
|
+
mkdir -p $@
|
|
468
|
+
|
|
469
|
+
# Without |, updating BUILD_DIR timestamp would trigger app rebuild
|
|
470
|
+
# With |, app only rebuilds when SOURCES change
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### VPATH for Source Organization
|
|
474
|
+
|
|
475
|
+
```makefile
|
|
476
|
+
# Search for prerequisites in multiple directories
|
|
477
|
+
VPATH = src:include:tests
|
|
478
|
+
|
|
479
|
+
# Or use vpath for specific patterns
|
|
480
|
+
vpath %.c src
|
|
481
|
+
vpath %.h include
|
|
482
|
+
vpath %.test tests
|
|
483
|
+
|
|
484
|
+
# Now Make will find files in these directories
|
|
485
|
+
app: main.o utils.o
|
|
486
|
+
$(CC) -o $@ $^
|
|
487
|
+
|
|
488
|
+
# Make will find src/main.c and src/utils.c automatically
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## Performance Optimization
|
|
492
|
+
|
|
493
|
+
### Use .PHONY for Performance
|
|
494
|
+
|
|
495
|
+
```makefile
|
|
496
|
+
# GOOD: Phony targets skip implicit rule search
|
|
497
|
+
.PHONY: clean test install
|
|
498
|
+
|
|
499
|
+
clean:
|
|
500
|
+
rm -rf $(BUILD_DIR)
|
|
501
|
+
|
|
502
|
+
# BAD: Without .PHONY, Make checks for file existence
|
|
503
|
+
clean:
|
|
504
|
+
rm -rf $(BUILD_DIR)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Parallel Builds
|
|
508
|
+
|
|
509
|
+
```makefile
|
|
510
|
+
# Enable parallel builds (use -j flag)
|
|
511
|
+
# make -j8 build # 8 parallel jobs
|
|
512
|
+
|
|
513
|
+
# For sequential targets, use .NOTPARALLEL
|
|
514
|
+
.NOTPARALLEL: deploy
|
|
515
|
+
|
|
516
|
+
deploy: build test
|
|
517
|
+
./scripts/deploy.sh
|
|
518
|
+
|
|
519
|
+
# Or use order-only prerequisites for partial ordering
|
|
520
|
+
build-frontend: | build-backend
|
|
521
|
+
npm run build
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Intermediate File Cleanup
|
|
525
|
+
|
|
526
|
+
```makefile
|
|
527
|
+
# Mark intermediate files for auto-deletion
|
|
528
|
+
.INTERMEDIATE: $(OBJECTS)
|
|
529
|
+
|
|
530
|
+
# Or mark files to keep through one build
|
|
531
|
+
.SECONDARY: $(OBJECTS)
|
|
532
|
+
|
|
533
|
+
# Delete on error (recommended)
|
|
534
|
+
.DELETE_ON_ERROR:
|
|
535
|
+
|
|
536
|
+
# Example: .o files cleaned after linking
|
|
537
|
+
app: main.o utils.o
|
|
538
|
+
$(CC) -o $@ $^
|
|
539
|
+
# main.o and utils.o auto-deleted after successful build
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Avoid Redundant Shell Calls
|
|
543
|
+
|
|
544
|
+
```makefile
|
|
545
|
+
# BAD: Shell called every time variable is used
|
|
546
|
+
DATE = $(shell date +%Y%m%d)
|
|
547
|
+
VERSION = $(shell git describe --tags)
|
|
548
|
+
|
|
549
|
+
target1:
|
|
550
|
+
echo $(DATE) # Shell called here
|
|
551
|
+
|
|
552
|
+
target2:
|
|
553
|
+
echo $(DATE) # Shell called again!
|
|
554
|
+
|
|
555
|
+
# GOOD: Use := for one-time evaluation
|
|
556
|
+
DATE := $(shell date +%Y%m%d)
|
|
557
|
+
VERSION := $(shell git describe --tags)
|
|
558
|
+
|
|
559
|
+
target1:
|
|
560
|
+
echo $(DATE) # Expands to cached value
|
|
561
|
+
|
|
562
|
+
target2:
|
|
563
|
+
echo $(DATE) # Same cached value
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Portability
|
|
567
|
+
|
|
568
|
+
### POSIX Shell Compatibility
|
|
569
|
+
|
|
570
|
+
```makefile
|
|
571
|
+
# GOOD: POSIX-compatible commands
|
|
572
|
+
.PHONY: install
|
|
573
|
+
install:
|
|
574
|
+
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
|
575
|
+
cp -f app $(DESTDIR)$(PREFIX)/bin/
|
|
576
|
+
chmod 755 $(DESTDIR)$(PREFIX)/bin/app
|
|
577
|
+
|
|
578
|
+
# AVOID: Bashisms or GNU-specific features
|
|
579
|
+
install:
|
|
580
|
+
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
|
581
|
+
cp app $(DESTDIR)$(PREFIX)/bin/ # Missing -f for portability
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Cross-Platform Variables
|
|
585
|
+
|
|
586
|
+
```makefile
|
|
587
|
+
# Detect operating system
|
|
588
|
+
UNAME_S := $(shell uname -s)
|
|
589
|
+
ifeq ($(UNAME_S),Linux)
|
|
590
|
+
PLATFORM := linux
|
|
591
|
+
EXE_EXT :=
|
|
592
|
+
endif
|
|
593
|
+
ifeq ($(UNAME_S),Darwin)
|
|
594
|
+
PLATFORM := macos
|
|
595
|
+
EXE_EXT :=
|
|
596
|
+
endif
|
|
597
|
+
ifeq ($(OS),Windows_NT)
|
|
598
|
+
PLATFORM := windows
|
|
599
|
+
EXE_EXT := .exe
|
|
600
|
+
endif
|
|
601
|
+
|
|
602
|
+
# Use platform-specific settings
|
|
603
|
+
APP := app$(EXE_EXT)
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Avoid Hard-Coded Paths
|
|
607
|
+
|
|
608
|
+
```makefile
|
|
609
|
+
# BAD: Hard-coded paths
|
|
610
|
+
install:
|
|
611
|
+
cp app /usr/local/bin/
|
|
612
|
+
cp docs/app.1 /usr/share/man/man1/
|
|
613
|
+
|
|
614
|
+
# GOOD: Use variables for paths
|
|
615
|
+
PREFIX ?= /usr/local
|
|
616
|
+
BINDIR ?= $(PREFIX)/bin
|
|
617
|
+
MANDIR ?= $(PREFIX)/share/man
|
|
618
|
+
|
|
619
|
+
install:
|
|
620
|
+
install -d $(DESTDIR)$(BINDIR)
|
|
621
|
+
install -m 755 app $(DESTDIR)$(BINDIR)/
|
|
622
|
+
install -d $(DESTDIR)$(MANDIR)/man1
|
|
623
|
+
install -m 644 docs/app.1 $(DESTDIR)$(MANDIR)/man1/
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## Documentation
|
|
627
|
+
|
|
628
|
+
### Comment Your Makefiles
|
|
629
|
+
|
|
630
|
+
```makefile
|
|
631
|
+
# Project: MyApp
|
|
632
|
+
# Description: Build system for MyApp project
|
|
633
|
+
# Author: Your Name
|
|
634
|
+
# Version: 1.0.0
|
|
635
|
+
|
|
636
|
+
# Configuration variables
|
|
637
|
+
PROJECT := myapp
|
|
638
|
+
VERSION := $(shell git describe --tags 2>/dev/null || echo "dev")
|
|
639
|
+
|
|
640
|
+
# Build directories
|
|
641
|
+
BUILD_DIR := build
|
|
642
|
+
SRC_DIR := src
|
|
643
|
+
|
|
644
|
+
# Compiler settings
|
|
645
|
+
CC := gcc
|
|
646
|
+
CFLAGS := -Wall -Wextra -O2
|
|
647
|
+
|
|
648
|
+
# Default target: Build and test the application
|
|
649
|
+
.PHONY: all
|
|
650
|
+
all: build test
|
|
651
|
+
|
|
652
|
+
# Build the main application binary
|
|
653
|
+
.PHONY: build
|
|
654
|
+
build: $(BUILD_DIR)/$(PROJECT)
|
|
655
|
+
@echo "Build complete: $(BUILD_DIR)/$(PROJECT)"
|
|
656
|
+
|
|
657
|
+
# Run all test suites
|
|
658
|
+
.PHONY: test
|
|
659
|
+
test:
|
|
660
|
+
@echo "Running tests..."
|
|
661
|
+
@./scripts/run-tests.sh
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Help Target
|
|
665
|
+
|
|
666
|
+
```makefile
|
|
667
|
+
# Provide a help target
|
|
668
|
+
.PHONY: help
|
|
669
|
+
help:
|
|
670
|
+
@echo "Available targets:"
|
|
671
|
+
@echo " make build - Build the application"
|
|
672
|
+
@echo " make test - Run tests"
|
|
673
|
+
@echo " make clean - Remove build artifacts"
|
|
674
|
+
@echo " make install - Install to $(PREFIX)"
|
|
675
|
+
@echo ""
|
|
676
|
+
@echo "Variables:"
|
|
677
|
+
@echo " PREFIX=$(PREFIX)"
|
|
678
|
+
@echo " CC=$(CC)"
|
|
679
|
+
|
|
680
|
+
# Or auto-generate from comments
|
|
681
|
+
.PHONY: help
|
|
682
|
+
help:
|
|
683
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
|
684
|
+
awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}'
|
|
685
|
+
|
|
686
|
+
build: ## Build the application
|
|
687
|
+
@go build -o app
|
|
688
|
+
|
|
689
|
+
test: ## Run all tests
|
|
690
|
+
@go test ./...
|
|
691
|
+
|
|
692
|
+
clean: ## Remove build artifacts
|
|
693
|
+
@rm -rf $(BUILD_DIR)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
## Security
|
|
697
|
+
|
|
698
|
+
### Avoid Hardcoded Credentials
|
|
699
|
+
|
|
700
|
+
```makefile
|
|
701
|
+
# BAD: Hardcoded secrets
|
|
702
|
+
deploy:
|
|
703
|
+
curl -H "Authorization: Bearer sk-1234567890" https://api.example.com/deploy
|
|
704
|
+
|
|
705
|
+
# GOOD: Use environment variables
|
|
706
|
+
deploy:
|
|
707
|
+
@if [ -z "$$API_TOKEN" ]; then \
|
|
708
|
+
echo "Error: API_TOKEN not set"; \
|
|
709
|
+
exit 1; \
|
|
710
|
+
fi
|
|
711
|
+
curl -H "Authorization: Bearer $$API_TOKEN" https://api.example.com/deploy
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### Validate Input Variables
|
|
715
|
+
|
|
716
|
+
```makefile
|
|
717
|
+
# Validate critical variables
|
|
718
|
+
.PHONY: deploy
|
|
719
|
+
deploy:
|
|
720
|
+
@if [ -z "$(ENV)" ]; then \
|
|
721
|
+
echo "Error: ENV not specified (prod|staging|dev)"; \
|
|
722
|
+
exit 1; \
|
|
723
|
+
fi
|
|
724
|
+
@if [ "$(ENV)" != "prod" ] && [ "$(ENV)" != "staging" ] && [ "$(ENV)" != "dev" ]; then \
|
|
725
|
+
echo "Error: Invalid ENV=$(ENV)"; \
|
|
726
|
+
exit 1; \
|
|
727
|
+
fi
|
|
728
|
+
@echo "Deploying to $(ENV)..."
|
|
729
|
+
./scripts/deploy.sh $(ENV)
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Safe Variable Expansion
|
|
733
|
+
|
|
734
|
+
```makefile
|
|
735
|
+
# BAD: Unsafe variable expansion
|
|
736
|
+
clean:
|
|
737
|
+
rm -rf $(BUILD_DIR)/* # Dangerous if BUILD_DIR is empty or /
|
|
738
|
+
|
|
739
|
+
# GOOD: Validate before dangerous operations
|
|
740
|
+
.PHONY: clean
|
|
741
|
+
clean:
|
|
742
|
+
@if [ -z "$(BUILD_DIR)" ] || [ "$(BUILD_DIR)" = "/" ]; then \
|
|
743
|
+
echo "Error: Invalid BUILD_DIR"; \
|
|
744
|
+
exit 1; \
|
|
745
|
+
fi
|
|
746
|
+
rm -rf $(BUILD_DIR)/*
|
|
747
|
+
|
|
748
|
+
# BETTER: Use safer patterns
|
|
749
|
+
BUILD_DIR := build # Never empty
|
|
750
|
+
clean:
|
|
751
|
+
@test -d $(BUILD_DIR) && rm -rf $(BUILD_DIR)/* || true
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
## Advanced Patterns
|
|
755
|
+
|
|
756
|
+
### Pattern Rules
|
|
757
|
+
|
|
758
|
+
```makefile
|
|
759
|
+
# Pattern rule for object files
|
|
760
|
+
%.o: %.c
|
|
761
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
762
|
+
|
|
763
|
+
# Multiple pattern rules
|
|
764
|
+
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
|
765
|
+
@mkdir -p $(dir $@)
|
|
766
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
767
|
+
|
|
768
|
+
# Static pattern rules
|
|
769
|
+
$(OBJECTS): %.o: %.c
|
|
770
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Automatic Variables
|
|
774
|
+
|
|
775
|
+
```makefile
|
|
776
|
+
# $@ - Target name
|
|
777
|
+
# $< - First prerequisite
|
|
778
|
+
# $^ - All prerequisites
|
|
779
|
+
# $? - Prerequisites newer than target
|
|
780
|
+
# $* - Stem of pattern rule
|
|
781
|
+
|
|
782
|
+
build/%.o: src/%.c
|
|
783
|
+
@mkdir -p $(dir $@) # Directory of target
|
|
784
|
+
$(CC) -c $< -o $@ # First prereq to target
|
|
785
|
+
@echo "Built $@" # Target name
|
|
786
|
+
|
|
787
|
+
# Example:
|
|
788
|
+
# build/main.o: src/main.c
|
|
789
|
+
# $@ = build/main.o
|
|
790
|
+
# $< = src/main.c
|
|
791
|
+
# $* = main
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### Functions
|
|
795
|
+
|
|
796
|
+
```makefile
|
|
797
|
+
# Built-in functions
|
|
798
|
+
SOURCES := $(wildcard src/*.c)
|
|
799
|
+
OBJECTS := $(patsubst src/%.c,build/%.o,$(SOURCES))
|
|
800
|
+
HEADERS := $(shell find include -name '*.h')
|
|
801
|
+
|
|
802
|
+
# String manipulation
|
|
803
|
+
UPPERCASE := $(shell echo $(PROJECT) | tr '[:lower:]' '[:upper:]')
|
|
804
|
+
VERSION_MAJOR := $(word 1,$(subst ., ,$(VERSION)))
|
|
805
|
+
|
|
806
|
+
# Custom functions
|
|
807
|
+
define compile_template
|
|
808
|
+
$(1): $(2)
|
|
809
|
+
$(CC) $(CFLAGS) -c $$< -o $$@
|
|
810
|
+
endef
|
|
811
|
+
|
|
812
|
+
$(foreach src,$(SOURCES),$(eval $(call compile_template,$(patsubst %.c,%.o,$(src)),$(src))))
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Conditional Compilation
|
|
816
|
+
|
|
817
|
+
```makefile
|
|
818
|
+
# Debug vs Release builds
|
|
819
|
+
DEBUG ?= 0
|
|
820
|
+
|
|
821
|
+
ifeq ($(DEBUG),1)
|
|
822
|
+
CFLAGS := -g -O0 -DDEBUG
|
|
823
|
+
BUILD_TYPE := debug
|
|
824
|
+
else
|
|
825
|
+
CFLAGS := -O2 -DNDEBUG
|
|
826
|
+
BUILD_TYPE := release
|
|
827
|
+
endif
|
|
828
|
+
|
|
829
|
+
build:
|
|
830
|
+
@echo "Building $(BUILD_TYPE) version..."
|
|
831
|
+
$(CC) $(CFLAGS) -o app $(SOURCES)
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
## Summary Checklist
|
|
835
|
+
|
|
836
|
+
- [ ] **.DELETE_ON_ERROR:** declared at top (critical)
|
|
837
|
+
- [ ] All non-file targets declared as .PHONY
|
|
838
|
+
- [ ] Tabs used for recipe indentation (not spaces)
|
|
839
|
+
- [ ] Variables use := for immediate expansion
|
|
840
|
+
- [ ] Sane defaults with ?= for user override
|
|
841
|
+
- [ ] Dependencies properly specified
|
|
842
|
+
- [ ] Error handling in critical recipes
|
|
843
|
+
- [ ] Default target documented and listed first
|
|
844
|
+
- [ ] No hardcoded credentials or paths
|
|
845
|
+
- [ ] Help target provided
|
|
846
|
+
- [ ] Parallel build safety considered
|
|
847
|
+
- [ ] Intermediate files managed (.INTERMEDIATE/.SECONDARY)
|
|
848
|
+
- [ ] Comments explain complex logic
|
|
849
|
+
- [ ] Portable commands used (POSIX compatible)
|
|
850
|
+
- [ ] Variables validated before dangerous operations
|
|
851
|
+
- [ ] .SUFFIXES: considered for disabling built-in rules
|
|
852
|
+
|
|
853
|
+
## Additional Resources
|
|
854
|
+
|
|
855
|
+
- [GNU Make Manual](https://www.gnu.org/software/make/manual/)
|
|
856
|
+
- [Makefile Style Guide](https://clarkgrubb.com/makefile-style-guide)
|
|
857
|
+
- [Advanced Makefile Tricks](https://www.gnu.org/software/make/manual/html_node/Quick-Reference.html)
|
|
858
|
+
- [Recursive Make Considered Harmful](https://aegis.sourceforge.net/auug97.pdf)
|