make and Makefile
My definition: make
is a data-driven (data is in a Makefile
) filter for
building ‘targets’.
GNU man page, manual, quick reference.
Summary of automatic variables
Variable | Description |
---|---|
$@ |
the target name |
$% |
the target member name, when the target is an archive member |
$< |
just the first prerequisite |
$? |
all the prerequisites newer than the target |
$^ |
all the prerequisites |
.PHONY |
Adding .PHONY to a target will prevent make from confusing the phony target with a file name. manual, one of many special built-in targets |
Examples
#
# simple C program build
#
CC=gcc
CFLAGS=-g
RM=rm -f
default: all
all: build
build: hello.c
$(CC) $(CFLAGS) -o hello hello.c
clean:
$(RM) hello
# The name of the source files
SOURCES = main.c foo.c bar.c
# The name of the executable
EXE = main
# Flags for compilation (adding warnings are always good)
CFLAGS = -g -Wall
# Flags for linking, e.g. -L./lib
LDFLAGS =
# Libraries to link with, e.g. -lcurl
LIBS = -lcurl
# Use the GCC frontend program when compiling
CC = gcc
# Use the GCC frontend program when linking
LD = gcc
# place to store auto-generated dependencies
DEPDIR = .d
#
# Nothing to change below
#
# This creates a list of object files from the source files
OBJECTS = $(SOURCES:%.c=%.o)
.PHONY: all
# Having an "all" target is customary, so one could write "make all"
# It depends on the executable program
all: $(EXE)
# The first target, this will be the default target if none is specified
# This target tells "make" to make the "all" target
default: all
# This will link the executable from the object files
$(EXE): $(OBJECTS)
$(LD) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $(EXE)
# Target to clean up after us
.PHONY: clean
clean:
# Remove the executable file
rm -f $(EXE) $(OBJECTS)
# Remove the dependencies and symbols
rm -Rf $(DEPDIR) $(EXE).dSYM
# Advanced auto-dependency, from:
# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
$(shell mkdir -p $(DEPDIR) >/dev/null)
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
POSTCOMPILE = mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
%.o : %.c
%.o : %.c $(DEPDIR)/%.d
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d
-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SOURCES)))
.DEFAULT_GOAL := help
SHELL:=/bin/bash
export PROJECT_ROOT = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
# define the name of the virtual environment directory
VENV:=.venv
PYTHON=$(VENV)/bin/python3
PIP=$(VENV)/bin/pip
# targets which are NOT files
.PHONY: help venv run test clean build
help: ## Shows the help
@echo 'Usage: make <TARGETS>'
@echo ''
@echo 'Available targets are:'
@echo ''
@grep -E '^[ a-zA-Z_-]+:.*?## .*$$' $(shell echo "$(MAKEFILE_LIST)" | tr " " "\n" | sort -r | tr "\n" " ") \
| sed 's/Makefile[a-zA-Z\.]*://' | sed 's/\.\.\///' | sed 's/.*\///' | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}'
@echo ''
# venv is a shortcut target
venv: $(VENV)/bin/activate ## Activate the venv
$(VENV)/bin/activate: requirements.txt
python3 -m venv $(VENV)
$(PIP) install -r requirements.txt
run: venv ## Execute python program
$(PYTHON) main.py $(SITE)
test: venv ## Execute python tests
$(PYTHON) -m unittest -v *_test.py
clean: ## Cleanup the artifacts
rm -rf $(VENV) .mypy_cache
find . -name __pycache__ | xargs rm -rf
DOCKER_USERNAME ?= john.doe
APPLICATION_NAME ?= da-app
GIT_HASH ?= $(shell git log --format="%h" -n 1)
build: ## Build docker image
docker build --tag ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_HASH} .
release: ## Release docker image
cat ./.docker-password | docker login --username ${DOCKER_USERNAME} --password-stdin
docker push ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_HASH}
docker pull ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_HASH}
docker tag ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_HASH} ${DOCKER_USERNAME}/${APPLICATION_NAME}:latest
docker push ${DOCKER_USERNAME}/${APPLICATION_NAME}:latest
World class:
Support in VSCode
Verify the file exists
Add this to your Makefile:
#
# check the prerequisites
#
EXECUTABLES := exec1 exec2
K:=$(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),'',$(error "No $(exec) in PATH")))
Verify the git hook installed
Add this to your Makefile:
SHELL:=/bin/bash
export PROJECT_ROOT=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PATH_TO_HOOK:=$(PROJECT_ROOT)/.git/hooks/pre-commit
ifeq ("$(wildcard $(PATH_TO_HOOK))","")
$(error git hook not installed)
endif