how i use Make and just for builds and tasks
make is for incremental builds and just is a task runner
2024-08-04
tldr: separation of concerns (make for builds, just for tasks) allows for a terse makefile, and a consistent entrypoint from project to project. just allows for rapid iteration on scripts and oneliners, and serves as documentation and a cookbook.
Contents
- Make is a build system
- Just is a task runner
- Just is a lovely cookbook
- Just is a sketchbook
- Just is a friend to others
Make is a build system
Make is the worst build system, except for all the other ones.
Its dependence on literal tabs is annoying. Its globbing, matching, and substitution syntax is arcane and inscrutible. Basically, one time I wrote a makefile that works and ever since then I just copy it from project to project making slight tweaks and adjustments as needed.
Hereâs the makefile for this site.
LATEST := $(shell cat LATEST)
SRCS=$(shell find src -name '*.m4' ! -name "feed.m4")
OUTS=$(SRCS:src/%.m4=%.html)
vpath %.m4 src
vpath %.html www
all: $(OUTS) list.html www/rss.xml index.html
www/index.html: src/$(LATEST).m4
m4 -D__latest=$(LATEST) $< | pandoc -f markdown+autolink_bare_uris -t html5 > $@
www/rss.xml: src/feed.m4
m4 -D__latest=$(LATEST) $< > $@
%.html: %.m4
m4 -D__latest=$(LATEST) $< | pandoc -f markdown+autolink_bare_uris -t html5 > www/$@
I canât explain to you with 100% confidence what it all does without really stopping to think about it. At least, I wouldnât be able to recreate this from scratch without a reference. Especially the vpath stuff and the substitutions on line 3.
But make is ubiquitous. Itâs everywhere. Itâs available. And itâs well supported. You can get help with your makefile by asking online and people will generally be able to help you.
So I continue to use it.
My most frequent use case is for building static sites like this one. Sites where I have plain text in markdown, or in this case m4 + markdown, or in other cases in a recfile, or even in a twtxt file. And I need to do some tranformations on that text to turn it into html for the browser so you can consume it with your eyes. (Or with your ears if youâre using a screenreader. Hi, Matt!)
What I donât use make for any more, though, is for running commands.
Just is a task runner
My makefiles these days are always distilled down to their incremental build essence.
Basically, I donât use .PHONY
commands any more. If itâs
not a build, if itâs just a command, I donât use make. For example, my
makefiles always used to include a phony command to
scp or rsync my build up to a remote
server. Or to run a watcher to build the project when the source files
change. The ubiquitous âcleanâ and âinstallâ commands are not build
steps. They only remain part of the build toolchain because historically
nobody separates building from running tasks.
But I donât do any of that any more. If itâs just a command, I just use just.
just is âjust a task runner.â It has some real niceties and modern
ergonomics and conventions. Such as whitespace indifference, more
conventional variables and interpolation, scripting in arbitrary
languages (just add the appropriate #!
to the start of your
recipe), and a whole lot more.
Hereâs a portion of the justfile for this site. (Omitted are some halfbaked curiosities; see âJust is a sketchbookâ below.)
# list all recipes
default:
just --list --unsorted
# build html and rss
build:
touch src/list.m4 src/feed.m4 && make all
# build assets
assets:
rsync -vurp static/* www/
# watch for changes
watch:
ls src/*.m4 | entr -r make $(cat LATEST).html
# clean build
clean:
rm www/*.html www/rss.xml
# build all
all: assets build
Just is a lovely cookbook
Some of you are rolling your eyes right now. I can practically hear it.
âJust write write a task.sh
file!â youâre crying out.
And to you I say, your opinions and your ways of doing things are
perfectly valid.
In fact, sometimes I do things your way! I have some projects with a
bin/build.sh
. But guess what. I call it from my
justfile.
It calls out to make and to build scripts as needed.
My justfile is nearly always the entry point for my projects now. My
default command is always just --list --unsorted
so I can
pop into a project, mash j
, and see a documented list of
all that projectâs commands. Itâs like a lovely cookbook full of
recipes! It documents all of the weird one-off oneliners and small
scripts that a project tends to accumulate over time. Not just for
actually getting stuff done, but also for testing and diagnostics and
reporting and statistics and stuff.
When I run just for this project, this is what I see:
just --list --unsorted
Available recipes:
default # list all recipes
build # build html and rss
glossary # show a glossary
assets # build assets
watch # watch for changes
clean # clean build
recfeed # build recfeed
all # build all
Just is a sketchbook
And it serves as a sketchbook where I might be composing and testing out a finicky command pipeline or a many branching awk script or something.
If the script gets long and complicated enough, sure it might get
relegated to its own .sh
file. That rarely happens though.
I just keep it all in the justfile.
Just is a friend to others
I use just in conjunction with other script files and, crucially, with make.
Remember, make is still probably the best build system.
My makefiles are terse these days. They contain the bare minimum. Make is just a build tool.
And my justfile will contain, for example, an html
recipe that consists of just make html
.
just is still my entrypoint. But it is still just a command runner. It can happily rely on make to handle incremental builds. just and make are friends.
Although, for the simplest of cases, you may not even need make:
# build html
html:
[ source.md -nt out.html ] \
&& echo "Creating html" && markdown source.md > out.html \
|| echo "Nothing to be done"