Makefile: properly displaying its usage

Makefile is an awesome tool and I use it a lot, mainly in Golang projects. In this quick article we’ll see how to display a useful help message with all targets that are available to be called.

Motivation

If you’re using Makefile as your build tool to your project, chances are that you have several targets in there. Wouldn’t it be nice to properly document its usage, so a new developer coming to your project knows what are the available targets and what are they’re used for?

Example

In real life, I have several targets in a Makefile for a bunch of things in Golang projects:

  • detecting any suspicious, abnormal, or useless code in the application;
  • formatting the source code;
  • setting up the local database;
  • running database migrations;
  • building the app’s binary;
  • running the app;
  • and so on.

So suppose this simple “hello world” app:

package main

import "fmt"

func main() {
	fmt.Println("Hey there!")
}

And this is the Makefile we’ll use to execute it:

.PHONY: help build run

## help: show this help message
help:
	@ echo "Usage: make [target]\n"
	@ sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' |  sed -e 's/^/ /'

## go-vet: runs go vet ./...
go-vet:
	@ go vet ./...

## go-fmt: runs go fmt ./...
go-fmt:
	@ go fmt ./...

## build: builds the app's binary
build: go-vet go-fmt
	@ go build main.go

## run: runs the app
run: build

    @ ./main

Let’s see what help target does:

tiago:~/make-help-example$ make help

Usage: make [target]

  help     show this help message

  go-vet   runs go vet ./...

  go-fmt   runs go fmt ./...

  build    builds the app's binary

  run      runs the app

Pretty cool, isn’t it? Now that we know what are the targets and what are they used for, let’s run the app:

tiago:~/make-help-example$ make run

Hey there!

Explaining it

For each target, just above it, we write a string that contains both target name and what it’s used for:

## <target_name>: brief explanation of what it does

Next, as we can see here, the variable MAKEFILE_LIST contains the list of Makefiles currently loaded or included. In our case, it contains all the content of our Makefile. Then, we’ll use both sed and column to format the strings:

@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' |  sed -e 's/^/ /'

  1. using sed to remove ‘##’ characters;
  2. using column to remove “:” and format a table;
  3. using sed again to ident the targets with one space.

Conclusion

In this quick article we saw how we can use tools like sed and column to build a help for our Makefile.