Make¶
A Makefile
is used to determine which pieces of the program need to be recompiled. The Makefile
is run using the make
command.
Tip
If the make
command is not available, the tool can be installed using the system's package manager, e.g. sudo apt install make -y
.
Why use a Makefile
?¶
Honestly, I primarily use these in conjunction with compiled languages, e.g. within my Rust
or Mojo
projects. They make more sense in those contexts (no need to use .PHONY
everywhere), although for Rust, cargo-make
arguably does the job better. The main reason I have chosen to include one in this project is to demonstrate their application.
The make
tool incorporates logic to determine what to recompile and what not to recompile (or re-transpile). For example, if a source file has not been changed, it will not be recompiled.
This seems trivial in a small project, but it makes a tremendous difference in build times for large projects. Furthermore, you can use Makefile
to make specific command chains (recipes) dependant on another.
Recipe: Default¶
The help
recipe has been marked as the default recipe. Hence, it will show when calling make
without a recipe specified.
default: help
.PHONY: help
help: # Show help for each recipe.
@grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | sort | while read -r l; do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done
Usage:
make <recipe>
Available recipes:
help Show help for each recipe.
pkg-install Installs the project.
pkg-update Updates the project's dependencies to their latest versions.
pkg-types Build the typing stubs for the project.
pkg-compile Compiles the project into sdist and wheel.
pc-install Installs the pre-commit hooks.
pc-update Updates the pre-commit hooks to their latest version.
pc-run Run pre-commit hooks on all files immediately.
md-serve Run MkDocs' builtin development server.
md-build Build the MkDocs documentation.
Recipe: Package setup¶
The all
recipe installs the project, installs the pre-commit hooks and then updates the pre-commit hooks to their latest version.
.PHONY: all
all: pkg-install pc-install pc-update
.PHONY: pkg-install
pkg-install: # Installs the project.
poetry install
.PHONY: pc-install
pc-install: pkg-install # Installs the pre-commit hooks.
poetry run pre-commit install
.PHONY: pc-update
pc-update: pc-install # Updates the pre-commit hooks to their latest version.
poetry run pre-commit autoupdate
poetry install
Installing dependencies from lock file
Package operations: 3 installs, 0 updates, 0 removals
• Installing iniconfig (2.0.0)
• Installing pluggy (1.4.0)
• Installing pytest (7.4.4)
Installing the current project: mkdocs-demo (1.1.0)
poetry run pre-commit install
pre-commit installed at .git/hooks/pre-commit
poetry run pre-commit autoupdate
[https://github.com/pre-commit/pre-commit-hooks] already up to date!
[https://github.com/astral-sh/ruff-pre-commit] already up to date!
[https://github.com/pre-commit/mirrors-mypy] already up to date!
Recipe: Run pre-commit hooks¶
The pc-run
recipe executes the pre-commits hooks on all files on demand.
Info
This recipe depends on the pc-install
recipe, which in turn depends on the pkg-install
recipe. In other words, make
ensures the package is installed, the pre-commit hooks are installed and then executes the pc-run
recipe.
.PHONY: pc-run
pc-run: pc-install # Run pre-commit hooks on all files immediately.
poetry run pre-commit run --all-files
poetry install
Installing dependencies from lock file
No dependencies to install or update
Installing the current project: mkdocs-demo (1.1.0)
poetry run pre-commit install
pre-commit installed at .git/hooks/pre-commit
poetry run pre-commit run --all-files
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Initializing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
fix end of files.........................................................Passed
trim trailing whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook
Fixing .github/ISSUE_TEMPLATE/bug_report.yml
Fixing TODO.md
Fixing .github/ISSUE_TEMPLATE/feature_request.yml
check yaml...............................................................Passed
check python ast.........................................................Passed
check builtin type constructor use.......................................Passed
check docstring is first.................................................Passed
check json...............................................................Passed
check for merge conflicts................................................Passed
check toml...............................................................Passed
debug statements (python)................................................Passed
detect private key.......................................................Passed
fix end of files.........................................................Passed
fix utf-8 byte order marker..............................................Passed
fix python encoding pragma...............................................Passed
mixed line ending........................................................Passed
Ruff Linter..............................................................Passed
Ruff Formatter...........................................................Passed
Mypy.....................................................................Passed
make: *** [Makefile:35: pc-run] Error 1
Complete Makefile
¶
Check out the full Makefile
here.
default: help
.PHONY: all pkg-install pkg-update pkg-compile pkg-types pc-install pc-update pc-run md-serve md-build
help: ## Show help for each recipe.
@echo "Usage:\n\tmake <recipe>"
@echo "\nAvailable recipes:"
@awk 'BEGIN {FS = ":.*##"; } /^[$$()% a-zA-Z_-]+:.*?##/ \
{ printf "\t\033[36m%-30s\033[0m %s\n", $$1, $$2 } /^##@/ \
{ printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
all: pkg-install pc-install pc-update
# Package recipes
pkg-install: ## Installs the project.
poetry install
pkg-update: pkg-install ## Updates the project's dependencies to their latest versions.
poetry update
pkg-types: pkg-install ## Build the typing stubs for the project.
stubgen -p mkdocs_demo -o typings
pkg-compile: pkg-install pkg-types ## Compiles the project into sdist and wheel.
poetry build
# Pre-commit recipes
pc-install: pkg-install ## Installs the pre-commit hooks.
poetry run pre-commit install
pc-update: pc-install ## Updates the pre-commit hooks to their latest version.
poetry run pre-commit autoupdate
pc-run: pc-install ## Run pre-commit hooks on all files immediately.
poetry run pre-commit run --all-files
# MkDocs recipes
md-serve: all ## Run MkDocs' builtin development server.
poetry run mkdocs serve --config-file src/mkdocs_demo/mkdocs.yml
.DELETE_ON_ERROR:
md-build: all pkg-update ## Build the MkDocs documentation.
poetry run mkdocs build --config-file src/mkdocs_demo/mkdocs.yml --clean --use-directory-urls
Alternative: poetry
scripts¶
Within poetry
's pyproject.toml
, you can define scripts. You can define these scripts under the [tool.poetry.scripts]
header.
Subsequently, after running poetry install
to register the script, you can run them using poetry run <script_name>
.
Info
The scripts in poetry
are quite similar to those in a package.json
file, for those familiar with NodeJS projects.
However, the main drawback of these scripts is that they require a specific entrypoint within a source file (e.g., gen-typings = "src.mkdocs_demo.scripts.mypy:gen_types"
).
Moreover, they will run "naively". Specifically, they will transpile/compile every single file encompassed by the script's command. If all but one source file has remained unchanged, it will still encompass all files.
Note
Of course, this depends on the parameters passed to the subprocess command. I am generalising here for the sake of providing an example.