GNU Make

Introduction

GNU Make is a common tool for creating software among Linux developers. Other build systems include waf, cmake, autoconf, and ninja. A list of build systems can be found on Wikipedia. Advantages to using Make include:

  • It has been around a long time. As such, it is on most Linux distributions.

  • If a code base is very big then it is time consuming and problematic building an executable from from scratch when the change in source code is very small. Make takes care of that. It tracks which files are changed and resolves dependencies accordingly to rebuild that specific part of the program.

  • All the details of how to build executables and process non-source files are listed in the Makefile – so the process becomes repeatable by all people or systems trying to build a project.

  • It is programming language agnostic.

  • When building executable programs many intermediate files are created that need not be there when the building is finished. Make deletes some of these files automatically.

At the time of this writing I am using Ubuntu 18.04. To install GNU Make on that system, run the following command:

sudo apt install build-essential

Sans Make

We will first do a simple example without Make. The compiler we will be using is GNU C++. We first create a file called hello.cpp as follows:

#include <iostream>

int main() {
std::cout << "Hello world" << std::endl;
return 0;
}

Then compile the program with G++ with:

g++ hello.cpp

This should result in an “a.out” file getting added to your local system. Run that file and you should get the following (for details on why you need the ‘./’ see here):

./a.out
Hello world

Voila! Next we will do the same thing using a Makefile.

Makefiles

A note about tabs: GNU Make is very picky about them! Make certain you have tab spacing set up correctly in your editor! If you try to copy and paste from the examples below the you will probably end up with incorrect spacing in the Makefile.

Makefiles are pretty magic. You can specify variables, use environment variables, clean up extra files that GNU Make leaves behind, tell Make to run across multiple cores, and more. Since we are just getting started we will keep things simple. Create a file called “Makefile” and use your favorite editor to put the following text into it:

target: hello.cpp
g++ hello.cpp -o hello

This instructs Make to:

  • Create a target based on hello.cpp

  • Build with g++

  • Instead of a.out, create an executable named hello (-o argument)

Now try running the Makefile from a terminal prompt:

make
g++ hello.cpp -o hello

./hello
Hello world

Congratulations! You’ve just written a Makefile! (Note: the -j argument tells make to run across multiple processes. Since my machine has 8 cores I like to use 7 processes. This is unimportant for such a small build, but it can be a major time saver for larger ones.)

Advanced Makefiles

Modify the Makefile as follows:

.PHONY = all clean

CPP = g++

SRCS := $(wildcard *.cpp)
BINS := $(SRCS:%.c=%)

final_target: hello.cpp
${CPP} hello.cpp -o hello

all: $BINS

clean:
@echo "Cleaning up..."

This does the following:

  • Create phony targets ‘all’ and ‘clean’

  • Create variable CPP and set it equal to ‘g++’

  • Create rules (with a wildcard) SRCS and BINS

  • Create targets final_target, all, and clean.

Treating Warnings as Errors

Something I like doing is treating warnings as errors in g++. To do so simply modify your Makefile with the -Wall option, like so.

References:
https://www.gnu.org/software/make/manual/make.html
https://linuxhint.com/gnu-make-tutorial/
https://interrupt.memfault.com/blog/gnu-make-guidelines

Previous
Previous

VirtualBox: Windows Host

Next
Next

Cheat Sheet for vim