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 `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.
