As in any other profession, knowing your tools is the key for successfully doing your job. A chef without knives or a painter without a brush find it much harder to perform their job successfully and the same goes for a professional software developer. One such tool that a professional software developer should be aware of (at least) is make. Let’s have a look at this tool, how to run it and read a makefile.
What is make?
First of all it might be a good idea to understand what make is and it’s purpose. Make is a utility tool, primarily for unix/linux, which helps out by constructing programs from source code. It can enable the user to build and install a program without knowing the details of how that is done. A great feature, especially if you work on large programs, is that it can detect if parts of a program needs to be (re-)created and then re-create on these parts so that you don’t need to compile all source code which can be very time consuming.
What is a makefile?
In order for make to be able to create a program you need to tell it how to do it and the way to do this is by providing make with a makefile. A makefile is a configuration file which is created for a specific project or program and it defines how to compile and link the program(s).
How to run make
The simplest way to run make it to use the following shell command.
By default, make looks a makefile called ’Makefile’ or ’makefile’ in the current directory. If make cannot find then makefile then make prints a warning. If you have a makefile which is called something else or if it is located in a different directory then you specify it by using the -f flag like this
make -f path/to/makefile
Reading a makefile
To read a makefile you need to understand how it is constructed and what parts it can include.
A makefile is build up by using 5 different components
- Explicit Rules
- Implicit Rules
- Variable definitions
Explicit rules tells make when and how to remake one or more files. An explicit rule consists of target(s), prerequisite(s) and a recipe and are based on the following syntax
target(s) : prerequisite(s)
- The targets are a list of file names which are separated by spaces. Normally there is only one target but make supports several which sometimes might be required.
- The prerequisites are also a list of file names, separated by spaces, which the targets are dependent upon.
- A recipe tells make what to do when one or more prerequisites has changed, i.e. is newer than the target. This is also refereed to as when the target is out-of-date. Every line in the recipe must start with a tab character.
Here is a simple example of a target (foo.o) which has one prerequisite (foo.c) and the recipe invokes the cc compiler with some arguments (-c -g foo.c).
foo.o : foo.c
cc -c -g foo.c
Implicit rules tell make when and how to remake a class of files based on their names. One example is how to make object files from C or C++ files. Make implicitly knows how to create a .o file from a .c so there is no need to explicit tell make how to do this using the cc compiler as show in the previous example.
If you’d like to change how make does this, i.e. the implicit recipe, you can create your own explicit rule or only change variables used by make in the implicit recipe. This is handy when you might only want to set some compiler flags for enabling warnings as an example.
In make, a comment starts with a # anywhere in a line. Any content after the # will be ignored by make. If the line ends with a \ (backslash) then the following row will be considered to be a comment as well.
# This is a comment
In make it is possible to define variables by using the following syntax
variable = value
When make encounters a variable in run time it replaces it with the value of the variable. It should be noted that variables are case-sensitive which means that ”variable” and ”Variable” are not the same variables.
Variables are used in targets, prerequisites or recipes by using the following syntax.
In the following simple example the variable obj is assigned the value ”foo.o bar.o” and at run time it is replaced by its value making app dependent on both foo.o and bar.o.
obj = foo.o bar.o
app : $(obj)
A directive tells make to do something, such as reading another makefile or deciding what to do.
By now you should have a rough idea on how make is used. By using a tool like make one simplifies your work, improves your productivity and eases collaboration between you and others. Being able to automatically build and create an application from its source code is a prerequisite for practices such as test driven development (tdd), continuous integration and continuous delivery.
If you wish to learn more about I can recommend the GNU make manual which is a great source for more knowledge about make.