Jb Doyon - https://jiby.tech/
S
for speaker notes
Press SPACE
for next page (arrows are wonky)
How do you build that?
How do you build that?
How do you build that?
My job makes me jump between many code repos
How do I start building the code?
gradle
? mvn
?./thescript.py
? reqs.txt
?“Building” = transforming some files into other files
A single place to read about most common commands
To remind me how, and to avoid typing boring magic
make # Build & test & format & [...] = "do what I mean"
# Specific task combo make install test
# Real specific, but easily forgettable command: make docker-login # docker login to the AWS ECR
DOCKER_IMAGE=my-lovely-project .PHONY: all all: install lint docs test build .PHONY: install install: poetry install .PHONY: lint lint: # Use all linters on all files (not just staged for commit) pre-commit run --all --all-files .PHONY: test test: poetry run pytest .PHONY: docs docs: openapi-spec.json cd docs && make html .PHONY: docs-serve docs-serve: cd docs/build/html && python3 -m http.server .PHONY: docker-build docker-build: docker build -t ${DOCKER_IMAGE} .
using Makefiles in your repos
you
document and automate common “build” tasks
Isn’t Makefiles for compiling C?
It was built for compiling C, but works on any cmd.
I’m talking about a restricted subset of Makefiles
npm run
, poetry
, cargo
, etc
make
: it’s build-essential
!sh
doesn’t compose complex cmdsUsing an SQL course as example
# Download a compressed SQLite database dump db.gz: wget -O db.gz \ http://15445.courses.cs.cmu.edu/fall2017/files/md_courts.dump.gz
database.gz
, use wget
”<TAB>
before wget\
allows multilinemake # Run the default (first) target # Alternatively, explicit the file to create make db.gz
Extracts db.gz
into db.sqlite3
db.sqlite3: db.gz
zcat db.gz | sqlite3 db.sqlite3
db.gz
: file existsdb.gz
more recent than db.sqlite3
: rerunresults/01.txt: query/01.sql db.sqlite3
sqlite3 db.sqlite3 <query/01.sql >results/01.txt
Computing query results via:
make results/01.txt
That’s long: shorter?
# Top of the Makefile: first target = default all: results/01.txt
New target all
“needing” results/01.txt
.
make # or, explicitly make all
all
is not a real file!dependencies:
sudo apt-get install sqlite
dependencies
not a file: “Phony target”
test: # Risky! pytest
test
is createdSolution: mark file as “phony”
.PHONY: test # "test" isn't a real file, rebuild always test: pytest
Remember: “if it’s not a real file, it’s .PHONY
!”
Shorten URL
DB_URL=http://15445.courses.cs.cmu.edu/fall2017/files/md_courts.dump.gz db.gz: wget -O db.gz "${DB_URL}"
APP_VERSION="v1.2.3" .PHONY: docker-build-release docker-build-release: docker build -t "my-lovely-app:${APP_VERSION}" .
Run via:
make docker-build-release # Builds v1.2.3 # Override variable just this time: make docker-build-release APP_VERSION=experimental # equivalent to: make APP_VERSION=experimental docker-build-release
Every line is run in different shell
bad: export VERSION=1.2.3 wget http://example.com/${VERSION} # "No such variable: 'VERSION'"
Links files (nodes) to build commands (edges)
Directed Acyclic Graph (DAG)
(Rant begins)
Every repo task SHOULD be a Makefile target
.PHONY: all all: install lint docs test build
.PHONY: install install: poetry install .PHONY: lint lint: # Use all linters on all files (not just staged for commit) pre-commit run --all --all-files .PHONY: test test: poetry run pytest .PHONY: docs docs: openapi-spec.json cd docs && make html .PHONY: docs-serve docs-serve: cd docs/build/html && python3 -m http.server
using Makefiles in your repos
you
document and automate common “build” tasks
docker system prune
…make init
If you ever say “I need to debug the Makefile”: too far
Once most commands are listed
It’s clean enough to exhibit in your README
For ease of development, a `Makefile` is provided, use it like this: make # equivalent to "make all" = install lint docs test build # run only specific tasks: make install make lint make test # Combine tasks: make install test
Note the abstraction: could be Java, Rust, Python…
using Makefiles in your repos
you
document and automate common “build” tasks
See GNU Make online docs, or offline:
sudo apt-get install make-doc info make # Much richer pages than "man make" (*giggles*)
Classic: show list of commands
help: ## display this help message grep '^[a-zA-Z]' $(MAKEFILE_LIST) | \ sort | \ awk -F ':.*?## ' 'NF==2 {printf " %-26s%s\n", $$1, $$2}'
Many variants exist, cargo-culted.
Prefix command with -
to not fail on nonzero code:
results/failing_query.txt: db.gz -sqlite3 db.gz "An incorrect SQL query"
Match all files, set to a variable
all_queries = $(wildcard query/*.sql)
BRANCH=$(shell echo "${BUILDKITE_BRANCH}" | sed 's;/;_;g')
Generic rules, regardless of input:
results/%.txt: query/%.sql db.sqlite3 sqlite3 db.sqlite3 < $< > $@
Any value that fits %
makes rule work:
make results/01.txt # % now set to 01 make results/blah.txt # % now set to blah, assumes query/blah.txt
define generate_file sed 's/{NAME}/$(1)/' greetings.tmpl >$(2).txt endef all: $(call generate_file,John Doe,101) $(call generate_file,Peter Pan,102)
Avoid phonies via real file dependency:
Create “sentinel files” manually
build/docker_image: docker build -t myimagename . docker images -q myimagename > build/docker_image test.sentinel: pytest && touch test.sentinel
# Solve the graph with 8 parallel processes make -j 8
Use -n
flag for dry-run (debug)
make -n
zcat db.gz | sqlite3 db.sqlite3 sqlite3 db.sqlite3 <query/01.sql >results/01.txt
makefile2dot python package, for GraphViz (dot)
pipx install makefile2dot makefile2dot | dot -Tpng > makefile.png
Run all commands into one shell
.ONESHELL: # Just declaring this is enough # All further commands are run in one persisting shell invocation good: export VERSION=1.2.3 wget http://example.com/${VERSION}
Don’t shell out: use python
!
.ONESHELL: python: SHELL := python3 python: greeting = "hello" print(f"{greeting}, python!")
Or docker run
:
IMG=ubuntu CMD=/bin/bash PWD=$(shell pwd) WRK=workdir .SHELLFLAGS = run -v ${PWD}:/${WRK} --rm --workdir /${WRK} -e ${CMD} ${IMG} -c SHELL := docker docker: echo "hello, $$(uname -a)!"
See more on Polyglot Makefiles.
More opinions about Makefiles
Read more rants, and this presentation, on https://jiby.tech