Useful gmake Recipes

Reusable targets for gmake

The venerable make program comes in many flavors. It has numerous quirks. And it offers only a very restricted interface. Yet it remains an important tool for organizing project builds, deploys, and other operations.

If you use make in your projects, chances are that you use gmake. Here are some useful tricks to use with gmake.

Which make?

Do you work on a system where make is not gmake? If you inadvertently invoke make, do you get a long list of syntax errors? Some versions of make use a .BEGIN target that executes before anything else, and that gmake sees as an ordinary target. So adding the following target at the end of your Makefile alerts you to the use of the wrong version of make.

    .BEGIN:
            @echo "This Makefile requires gmake"; exit 1

Help by Default

The default target in gmake is the first target in the Makefile. A help target is a good choice for that position. It is a safe, because it merely informs without taking potentially dangerous actions. And it encourages the good practice of documenting all targets in the Makefile. The define and info directives make it easy to write multiline help.

    define help_text :=

      help:
        Print this message

        Some more generic help for using this Makefile.

      target:  
        Documentation for target.


    endef

    help:
            @$(info $(help_text))

Confirmations

What if you call gmake from the command line, sometimes under unusual conditions, and in those cases you want to confirm whether to proceed? Use one of these targets as a prerequisite for any recipe that requires confirmation before execution.

Simple Confirmation

Continue if the answer is ‘yes’ and stop otherwise.

    confirm:
            @printf "Continue? [yes|No] " && \
            read answer && test 'yes' = "$$answer" && exit 0 || exit 1

Process ID Confirmation

Continue if the answer is ‘yes’ plus the process ID and stop otherwise. Functionally the same as simple confirmation, it requires more engagement from the caller.

  confirm-pid:
          @r="$$$$" && \
          printf "Continue? [yes-$$r|No]: " && \
          read answer && test "yes-$$r" = "$$answer" && exit 0 || exit 1

Git Branch Confirm

Confirm that you are on the intended branch in git. Proceed if the current branch matches the required branch name, otherwise confirm before proceeding.

    confirm-git-branch-%:
            @test '$*' = "`git branch --show-current`" && exit 0 || { \
              printf "Current branch '`git branch --show-current`' is not '$*'. Continue? [yes|No] " && \
              read answer && test 'yes' = "$$answer" && exit 0 || exit 1; \
            }

Required Environment Variables

This recipe fails if an environment variable is either unset or empty. Use it as a prerequisite in a recipe that relies on an environment variable.

    require-env.%:
            @test "X$($*)" != "X" && exit 0 || { \
              echo 'Environment variable not set: $*' && exit 1; \
            }

Useful Options

By default, gmake announces every change of directory. These outputs may interfere with pipelines in recipes, or clutter the otherwise useful output of your gmake run. To avoid this, use the following variable setting.

    MAKEFLAGS += --no-print-directory

Does your project use the vast array of builtin rules and variables that support them? It doesn’t? Then turn them off.

    MAKEFLAGS += -r -R

Further Reading

John Graham-Cumming has written numerous articles detailing tricks and troubles with gmake. He also authored The GNU Make Book, and the GNU Make Standard Library.

gnu  unix 

See also