56 Make for Python (Basic)
56.1 Basic Structure
target: dependencies
command
Automatic variables:
target: dependency1 dependency2
# $@ - The target's name
# $< - The first dependency's name
# $^ - Names of all dependencies
# $* - The stem (part matched by %) in pattern rules
$(CC) -o $@ $^
56.2 Makefile for Python
Let’s start with a simple example of a Python project structure:
my_project/
├── src/
│ ├── data_processor.py
│ └── utils.py
├── tests/
│ └── test_processor.py
├── requirements.txt
└── Makefile
Here’s a basic Makefile to handle common Python project tasks:
# Variables
PYTHON = python3
PIP = pip3
TEST_DIR = tests
SRC_DIR = src
# Default target (runs when you just type 'make')
.DEFAULT_GOAL := help
# Help target - lists available commands
help:
@echo "Available commands:"
@echo " make install - Install Python dependencies"
@echo " make test - Run tests"
@echo " make clean - Remove Python cache files"
@echo " make lint - Check code style with flake8"
# Install dependencies
install:
$(PIP) install -r requirements.txt
# Run tests
test:
$(PYTHON) -m pytest $(TEST_DIR)
# Clean Python cache files
clean:
find . -type d -name "__pycache__" -exec rm -rf {} +
"*.pyc" -delete
find . -type f -name "*.pyo" -delete
find . -type f -name ".coverage" -delete
find . -type f -name
# Lint code
lint:
flake8 $(SRC_DIR)
# Mark targets that don't create files
.PHONY: help install test clean lint
Let me explain the key concepts:
Rules and Targets: Each block in a Makefile is called a rule. The basic structure is:
target: dependencies commands
For example, in our
test
target:test: $(PYTHON) -m pytest $(TEST_DIR)
When you run
make test
, it executes the pytest command.Variables: We define variables at the top of the Makefile:
PYTHON = python3 PIP = pip3
You can use them with
$(VARIABLE_NAME)
. This makes the Makefile more maintainable..PHONY
Targets: These are targets that don’t create files. We mark them with.PHONY
to tell Make that these targets are always considered “out of date” and should run every time.Dependencies: Make’s real power comes from handling dependencies. Here’s an example of a more complex rule:
report.pdf: analysis.py data.csv $(PYTHON) analysis.py
This tells Make that
report.pdf
depends onanalysis.py
anddata.csv
. If either file changes, Make will rebuildreport.pdf
.
To use the Makefile:
Navigate to your project directory
Run commands like:
make install # Install dependencies make test # Run tests make clean # Clean up cache files
Some advanced features you might find useful:
# Run multiple targets in sequence
all: clean install test lint
# Target with automatic variables
%.html: %.md
pandoc $< -o $@
# Conditional execution
check-env:
@if [ -z "$(VIRTUAL_ENV)" ]; then \
echo "Not in a virtual environment!"; \
exit 1; \
fi
Common patterns in Python projects:
Virtual Environment Management:
venv: python3 -m venv .venv source .venv/bin/activate && pip install -r requirements.txt
Development Setup:
dev-setup: venv pip install -e ".[dev]"
Documentation Generation:
docs: cd docs && make html
Remember these key points:
- Use tabs (not spaces) for indentation in Makefiles
- Commands in a rule are executed in a new shell by default
- Use
@
before a command to prevent it from being printed - Use
-
before a command to continue even if it fails
56.3 Variable Summarize
Let me explain the commonly used variables in Makefiles, starting with the basic syntax and building up to more advanced usage.
Basic Variable Definition:
# Simple assignment (immediate evaluation)
PYTHON = python3
# Recursive assignment (lazy evaluation)
TESTS != ls tests/*.py
# Conditional assignment (only if not already set)
PREFIX ?= /usr/local
Here are the most commonly used variables in Makefiles, organized by their purpose:
56.3.1 Program and Tool Variables
# Basic commands
PYTHON = python3
PIP = pip3
RM = rm -f
MKDIR = mkdir -p
CP = cp -r
# Build tools
CC = gcc # C compiler
CXX = g++ # C++ compiler
AR = ar # Archive maintainer
MAKE = make # Make command itself
56.3.2 Directory and Path Variables
# Common directory structure
SRC_DIR = src
BUILD_DIR = build
DIST_DIR = dist
DOC_DIR = docs
TEST_DIR = tests
# Installation paths
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
56.3.3 Flags and Options Variables
# Compiler and linker flags
CFLAGS = -Wall -O2 # C compiler flags
CXXFLAGS = -std=c++11 # C++ compiler flags
LDFLAGS = -L/usr/lib # Linker flags
# Python-specific flags
PYTEST_FLAGS = -v --cov
PIP_FLAGS = --no-cache-dir
56.3.4 Special Make Variables (Automatic Variables)
target: dependency1 dependency2
# $@ - The target's name
# $< - The first dependency's name
# $^ - Names of all dependencies
# $* - The stem (part matched by %) in pattern rules
$(CC) -o $@ $^
56.3.5 Shell Command Variables
# Get system information
UNAME := $(shell uname)
VERSION := $(shell git describe --tags)
PYTHON_VERSION := $(shell python --version)
# Dynamic file listing
SOURCES := $(shell find $(SRC_DIR) -name '*.py')
56.3.6 Environment Variables Access
# Access environment variables
HOME = $(HOME)
PATH = $(PATH)
VIRTUAL_ENV = $(VIRTUAL_ENV)
56.3.7 Examples
Let’s see a practical example that puts these together:
# System detection
UNAME := $(shell uname)
# Set system-specific variables
ifeq ($(UNAME), Darwin)
PYTHON = python3
BROWSER = open
else
PYTHON = python3
BROWSER = python -mwebbrowser
endif
# Project structure
PROJECT = myproject
SRC_DIR = src
TEST_DIR = tests
BUILD_DIR = build
DIST_DIR = dist
# Python settings
VENV = .venv
PIP = $(VENV)/bin/pip
PYTEST = $(VENV)/bin/pytest
PYTEST_FLAGS = -v --cov=$(PROJECT)
# Targets using variables
test:
$(PYTEST) $(PYTEST_FLAGS) $(TEST_DIR)
build: $(BUILD_DIR)
$(PYTHON) setup.py build
$(BUILD_DIR):
$(MKDIR) $@
Important things to remember about Makefile variables:
Variable Expansion: Variables are expanded when used, not when defined (unless := is used)
# Immediate expansion FILES := $(wildcard *.txt) # Delayed expansion FILES = $(wildcard *.txt)
Environment Variables: Make automatically imports all environment variables
# Can use environment variables directly USER_HOME = $(HOME)
Override from Command Line: Variables can be overridden when running make
make PYTHON=python3.9 test