137 lines
4.7 KiB
Markdown
137 lines
4.7 KiB
Markdown
# makefile magic
|
|
|
|
author: [ben](https://tilde.team/~ben/)
|
|
|
|
i've used makefiles plenty of times for a variety of projects, but my
|
|
understanding has not really improved beyond the most basic level.
|
|
|
|
working with pandoc for the [\~club wiki](https://tilde.club/wiki/) and
|
|
the [zine](https://zine.tildeverse.org/) inspired me to replace a [janky
|
|
build
|
|
script](https://github.com/tildeclub/site/blob/25e3ed375791d48d085b434beabac10f584f5540/wiki/build-wiki.sh)
|
|
with some makefile goodness.
|
|
|
|
feeling a loss for where to start, i reached out to
|
|
[tomasino](gopher://tilde.black) for some guidance. he responded that i
|
|
should have a look at a makefile he built for a javascript project
|
|
template which he linked on [his
|
|
github](https://github.com/jamestomasino/ts-boilerplate/blob/master/Makefile).
|
|
|
|
let's go through some of my biggest takeaways from inspecting tomasino's
|
|
example and creating several of my own makefiles from it.
|
|
|
|
## anatomy of a makefile
|
|
|
|
this was something that i thought i understood, but realized i didn't
|
|
fully grok.
|
|
|
|
i *think* that i understand it now, or at least know now what to search
|
|
for when i get stuck the next time.
|
|
|
|
let's start with the basics: a makefile should be named Makefile (note
|
|
the capital M). the basic syntax consists of a target followed by a
|
|
colon (`:`) and an optional list of requirements, then a list of
|
|
commands to be run (indented by literal tabs).
|
|
|
|
myproc: source.c
|
|
cc -o myproc source.c
|
|
|
|
this example defines how to build myproc, which depends on source.c.
|
|
|
|
### variables
|
|
|
|
makefiles use variables in two main formats.
|
|
|
|
- recursively-expanded: evaluated every time they're referenced, even
|
|
in other variables
|
|
- defined with a single `=`
|
|
- simply-expanded: evaluated once at the time of definition
|
|
- defined with `:=`
|
|
|
|
simply-expanded variables are generally more predictable and will likely
|
|
be what you should use in most cases.
|
|
|
|
x := foo
|
|
y := $(x) bar
|
|
x := later
|
|
|
|
will become
|
|
|
|
y := foo bar
|
|
x := later
|
|
|
|
variable names are case-sensitive. you'll likely see all-caps variable
|
|
names more often than not.
|
|
|
|
### guided example
|
|
|
|
this was the major revelation for me coming from my brittle buildscript
|
|
that deleted and recreated everything on each run.
|
|
|
|
make wants to know what files you want built, as well as how to build
|
|
them.
|
|
|
|
let's take the \~club wiki as an example: we want to build one html for
|
|
each markdown file in the source directory. how do we tell make which
|
|
files to build and how to build each one?
|
|
|
|
using a standard static target name, you'd need to add a target for each
|
|
markdown file.
|
|
|
|
ssh.html: source/ssh.md
|
|
pandoc -so ssh.html source/ssh.md
|
|
|
|
git.html: source/git.md
|
|
pandoc -so git.html source/git.md
|
|
|
|
this already feels like a lot of duplication. let's stay
|
|
[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). here's an
|
|
example from tomasino's makefile that i adapted for the wiki: [github
|
|
source](https://github.com/jamestomasino/ts-boilerplate/blob/master/Makefile#L46)
|
|
|
|
$(DST_DIR)/js/%.js: $(SRC_DIR)/ts/%.ts
|
|
$(mkdir)
|
|
$(tsc) $< --outFile $@
|
|
|
|
in this example, we're telling make that files under `DST_DIR` are built
|
|
from `SRC_DIR` with a command defined in the `tsc` variable.
|
|
|
|
let's adapt this to our wiki example:
|
|
|
|
%.html: source/%.md
|
|
pandoc -so $@ $<
|
|
|
|
here we're telling make that it can build html files from the
|
|
corresponding md file in source by running pandoc. but how do we tell
|
|
make which html files we want to build? let's go back to tomasino's
|
|
[example](https://github.com/jamestomasino/ts-boilerplate/blob/master/Makefile#L18-L19)
|
|
|
|
SRC_TS_FILES != find $(SRC_DIR)/ts -name '*.ts'
|
|
DST_JS_FILES := $(SRC_TS_FILES:$(SRC_DIR)/ts/%.ts=$(DST_DIR)/js/%.js)
|
|
|
|
in this case, we're using the special assignment form with `!=` that
|
|
uses the output of a shell executable. we now have a list all the `.ts`
|
|
files that need to be compiled in `SRC_TS_FILES`. next, we assign
|
|
`DST_JS_FILES` with the special replacement syntax, which changes
|
|
`ts/%.ts` into `js/%.js`. so, these variables now contain a list of
|
|
source and desired files, along with a definition of how to build those
|
|
files.
|
|
|
|
here's how i adapted this step for the wiki:
|
|
|
|
SRC_MD_FILES != find source -name '*.md'
|
|
DST_HTML_FILES := $(SRC_MD_FILES:source/%.md=%.html)
|
|
|
|
this means that make now has a list of html files that are required
|
|
based on the list of all markdown files in the source directory.
|
|
|
|
now all that's left is to tell make that we want to build all of the
|
|
`DST_HTML_FILES`. let's add that list to the `all` target:
|
|
|
|
all: $(DST_HTML_FILES)
|
|
|
|
now we can generate all html files by simply calling `make`
|
|
|
|
here's the completed
|
|
[Makefile](https://github.com/tildeclub/site/blob/master/wiki/Makefile).
|