This tries to cover the syntax that is hard to ctrl-f for in
https://www.gnu.org/software/make/manual/make.html (err, hard to
C-s for in M-: (info "Make")
).
At the core is a "rule":
target: dependency1 dependency2
command to run
If target
something that isn't a real file (like 'build', 'lint', or
'test'), then it should be marked as "phony":
target: dependency1 dependency2
command to run
.PHONY: target
You can write reusable "pattern" rules:
%.o: %.c
command to run
Of course, if you don't have variables for the inputs and outputs, it's hard to write a "command to run" for a pattern rule. The variables that you should know are:
$@ = the target
$^ = the list of dependencies (space separated)
$< = the first (left-most) dependency
$* = the value of the % glob in a pattern rule
Each of these have $(@D) and $(@F) variants that are the
directory-part and file-part of each value, respectively.
I think those are easy enough to remember mnemonically:
- $@ is where you should direct the output at.
- $^ points up at the dependency list
- $< points at the left-most member of the dependency list
- $* is the % glob; "*" is well-known as the glob char in other languages
Make will do its best to guess whether to apply a pattern rule for a given file. Or, you can explicitly tell it by using a 3-field (2-colon) version:
foo.o bar.o: %.o: %.c
command to run
In a non-pattern rule, if there are multiple targets listed, then it is as if rule were duplicated for each target:
target1 target2: deps
command to run
# is the same as
target1: deps
command to run
target2: deps
command to run
Because of this, if you have a command that generates multiple outputs, it must be a pattern rule:
%.c %.h: %.y
command to run
Normally, Make crawls the entire tree of dependencies, updating a file if any of its dependencies have been updated. There's a really poorly named feature called "order-only" dependencies:
target: normal-deps | order-only-deps
Dependencies after the |
are created if they don't exist, but if
they already exist, then don't bother updating them.
-
Use absolute filenames; it's silly, but can often reduce headaches. Use
$(OSS_HOME)
to spell the absolute filenames.Though, this isn't so helpful in Emissary anymore. The main case where this helps is when the same directory-specific Makefile might be
include
d from several different top-level directories; as the Helm chart Makefile used to be from the main Emissary Makefile, or the Emissary Makefile used to be from Edge Stack. But Emissary no longer does this. -
If you have a multiple-output command where the output files have dissimilar names, have
%
be just the directory (the above tip about using absolute filenames makes this easier--this is a real pain if you have such a target in the top-level directory and aren't using absolute filenames). -
It can be useful to use the 2-colon form of a pattern rule when writing a rule for just one file; it lets you use
%
and$*
to avoid repeating yourself, which can be especially useful with long filenames.