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/^/ /'
- using sed to remove ‘##’ characters;
- using column to remove “:” and format a table;
- 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.