Mar 13th, 2019
Make is a build tool that became (relatively) popular with the rise of Unix. GNU Make is the most common version, present on both Linux and MacOS. It's used to build even complicated software like the Linux kernel.
Of course, Make does not have to be complicated. Like most other Unix tools, it's based on a few simple ideas!
Make Just Builds Files
Makefile is usually just a set of instructions on how to build a file. Instructions generally are programs on the system (though not necessarily run by a shell!).
hello.txt: touch hello.txt
Try it out! If you wrote this in a
Makefile, then entered
make hello.txt, you would have the file
hello.txt created in your directory.
If you ran it again, it'd look like this:
$ make hello.txt touch hello.txt $ make hello.txt make: 'hello.txt' is up to date
Make sees that there is a
hello.txt file, and does not run the command again.
Files Have Dependencies
Sometimes, you'll need to do more than just create text files using
touch. Surprising right? Thankfully, you can chain your contrived dependencies together using Make, and it's super easy:
awesome.txt: hello.txt cp hello.txt awesome.txt hello.txt: touch hello.txt
The instructions on creating an
awesome.txt now include
hello.txt as a dependency. Make looks for instructions on how to create a file of the same name, and checks if it's up to date.
That's also how to create the ubiquitous
make build or
make test commands, by the way:
.PHONY: build build: awesome.txt awesome.txt: hello.txt cp hello.txt awesome.txt hello.txt: touch hello.txt
.PHONY: <command> just means that the command name does not correspond to a file. Otherwise, Make would look for a file named
build, which is probably not what you want.
Automate Docker Releases
Look at this adorable whale! www.docker.com
Docker is pretty easy to use, but I have found that building and saving releases is awkward. It's a little too easy to lose track of version numbers and to forget the "latest" container.
Plus, any command entered by hand that warrants a backslash probably shouldn't be entered by hand.
As you can probably guess, Make to the rescue. Let's automate image building and tagging! Create a new directory that looks something like this:
make-docker/ ├── dist/ ├── Dockerfile ├── Makefile └── VERSION 1 directory, 4 files
You can use whatever you would like in your
Dockerfile! Here's a "Hello World" Docker image.
FROM alpine:latest RUN echo "Hello World" > greeting.txt CMD cat greeting.txt
We'll mark this as version 0.1.0 in our
Finally, let's add the contents of our
VERSION := $(shell cat VERSION) IMAGE := my-hello-world .PHONY: build build: dist/$(IMAGE)-$(VERSION).tar.gz dist/$(IMAGE)-$(VERSION).tar.gz: docker build -t "$(IMAGE):$(VERSION)" -t "$(IMAGE):latest" . docker save "$(IMAGE):$(VERSION)" > dist/$(IMAGE)-$(VERSION).tar.gz
Makefile reads the value stored in
VERSION upon any invocation of
make. Make then relays the commands to Docker, which builds our images for us. Docker tags the images as both
By saving a .tar file for the docker image, our requirements for
make build are met. Subsequent runs with the same version will not create extra images!
Congrats! You can now create a new release of your image with just one command.
That's a quick intro to Make! You now know enough of the basics to get out there and use Make in your projects.
To learn more about Make, the manual is a pretty great resource that explains a lot more than this overview. Happy Making!