Python Projects with Makefiles

If you have a python script which requires a packages - e.g. graphviz - you can automate the setup with a Makefile. The Makefile will:

  1. Make three copies of an 8-line installer script.
  2. Use one of the installer script to install local packages.
  3. Install a symbolic link to python.
  4. Make a script called activate, which tells python to use

Context

Python coders don't like updating their projects, they just expect everyone to install the same version of everything that they have. Historically, people dealt with this by installing only half a dozen copies of graphviz; but now each python project uses a local environment, with a local copy of graphviz, which means everyone gets to install a new copy of graphviz every time they try out a project.

Downloading 40MB of software for each 40-line script you write is called 'virtual environments' because it sounds cool. We can make it even cooler with make, but not yet, because python - like the fae of old - will not fetch anything until you know its true name.

Setup

1command -v python
2realpath `!!`

You must reveal that true path, because python is always a relative symbolic link, to an absolute symbolic link, which leads to a shortcut. We can finally let make know how to invoke python, and where it will install graphviz.

If your python's version is '3.14', then python needs its packages placed in ${somewhere}/lib/python3.14/site-packages/. You must create a new, local, name for these packages, because - like the fey of old - python demands a private name in return for revealing its true name.

I'll call mine camelot, because the path is long and arduous. Set up the Makefiles like this:

1py_link != command -v python
2py != realpath $(py_link)
3version != basename $(py)
4
5virtenv = camelot

Now you can ask for a local pip script, which can install the python packages:

1    [...]
2
3$(virtenv)/bin/pip:
4    $(py) -m venv $(virtenv)

Finally, list the packages you want in requirements.txt, and make pip install from it.

1ppkg=graphviz
2echo ${ppkg} > requirements.txt
1    [...]
2
3pkgs = $(virtenv)/lib/$(version)/site-packages/
4
5$(pkgs): $(virtenv)/bin/pip
6$(pkgs): requirements.txt
7    $(virtenv)/bin/pip install -r $<

The complete Makefile looks like this:

 1all: .default
 2
 3py_link != command -v python
 4py != realpath $(py_link)
 5version != basename $(py)
 6
 7virtenv = camelot
 8
 9$(virtenv)/bin/pip:
10    $(py) -m venv $(virtenv)
11
12pkgs = $(virtenv)/lib/$(version)/site-packages/
13
14$(pkgs): $(virtenv)/bin/pip
15$(pkgs): requirements.txt
16    $(virtenv)/bin/pip install -r $<
17
18.PHONY: .default
19.default: $(pkgs)