Skip to content

Latest commit

 

History

History
100 lines (73 loc) · 3.36 KB

gnu-make.md

File metadata and controls

100 lines (73 loc) · 3.36 KB

A quick primer on GNU Make syntax

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.

Tips:

  • 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 included 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.