Makefile

Installation

Ubuntu/Debian

# Install make and build tools
sudo apt update
sudo apt install build-essential make -y
 
# Verify installation
make --version

CentOS/RHEL

# Install make and build tools
sudo yum groupinstall "Development Tools" -y
 
# Verify installation
make --version

Basic Makefile Structure

# Simple Makefile example
.PHONY: help install build test clean
 
help:
	@echo "Available targets:"
	@echo "  make install - Install dependencies"
	@echo "  make build   - Build project"
	@echo "  make test    - Run tests"
	@echo "  make clean   - Clean build files"
 
install:
	@echo "Installing dependencies..."
	npm install
 
build:
	@echo "Building project..."
	npm run build
 
test:
	@echo "Running tests..."
	npm test
 
clean:
	@echo "Cleaning build files..."
	rm -rf dist
	rm -rf node_modules

Common Makefile Variables

# Define variables
SHELL := /bin/bash
PROJECT_NAME := myproject
VERSION := 1.0.0
PORT := 3000
 
# Environment
.EXPORT_ALL_VARIABLES:
ENVIRONMENT := production
 
# Targets using variables
build:
	@echo "Building $(PROJECT_NAME) v$(VERSION)"
 
run:
	@echo "Running on port $(PORT)"
	npm start -- --port $(PORT)
 
info:
	@echo "Project: $(PROJECT_NAME)"
	@echo "Version: $(VERSION)"
	@echo "Environment: $(ENVIRONMENT)"

Prerequisites & Dependencies

# Define prerequisites
.PHONY: check-node check-npm
 
check-node:
	@command -v node >/dev/null 2>&1 || { echo "Node.js not installed"; exit 1; }
 
check-npm:
	@command -v npm >/dev/null 2>&1 || { echo "npm not installed"; exit 1; }
 
# Use as prerequisite
install: check-node check-npm
	npm install

Common Patterns

Docker Targets

.PHONY: docker-build docker-run docker-stop
 
docker-build:
	docker build -t myapp:latest .
 
docker-run:
	docker run -d -p 3000:3000 --name myapp myapp:latest
 
docker-stop:
	docker stop myapp
	docker rm myapp

File Operations

.PHONY: create-dirs copy-files clean-files
 
create-dirs:
	mkdir -p build dist src
 
copy-files:
	cp -r src/* build/
 
clean-files:
	rm -f *.log
	find . -name "*.tmp" -delete

Git Operations

.PHONY: git-status git-commit git-push
 
git-status:
	git status
 
git-commit:
	git add .
	git commit -m "Auto-commit"
 
git-push:
	git push origin main

Server Management

.PHONY: start stop restart logs
 
start:
	sudo systemctl start myapp
 
stop:
	sudo systemctl stop myapp
 
restart:
	sudo systemctl restart myapp
 
logs:
	sudo journalctl -u myapp -f

Advanced Features

Conditional Statements

# Simple if-else
deploy: build
ifeq ($(ENV),production)
	@echo "Deploying to production"
	./deploy-prod.sh
else
	@echo "Deploying to staging"
	./deploy-staging.sh
endif

Loops

# Loop through files
cleanup:
	@for file in *.log; do \
		echo "Removing $$file"; \
		rm $$file; \
	done

Shell Commands

# Execute shell commands
version:
	@echo "Version: $$(cat package.json | grep version | head -1 | awk -F: '{ print $$2 }')"
 
date-build:
	@echo "Build time: $$(date)"

Best Practices

Use .PHONY for Non-File Targets

.PHONY: all install build test clean deploy

Add Help Target

help:
	@echo "Available commands:"
	@grep -E "^[a-zA-Z_-]+:" Makefile | sed 's/:.//' | sort

Use Variables for DRY Code

SRC_DIR := src
BUILD_DIR := build
TEST_DIR := tests
 
# Reuse variables
build:
	@echo "Building from $(SRC_DIR) to $(BUILD_DIR)"
	cp -r $(SRC_DIR)/* $(BUILD_DIR)/

Suppress Output

# Suppress command output
silent-build: SHELL := /bin/sh
silent-build:
	@npm run build >/dev/null 2>&1
	@echo "Build complete"

Debugging

# Show expanded variables
make -p
 
# Trace execution
make -d
 
# Dry run (show commands without executing)
make -n
 
# Show specific target
make -n target-name