Learning C++ [understanding makefiles]

I only recently started working with C and C++, both developing my own projects (simple ones), and installing packages that require local builds. In this short time I have come across numerous makefile. Unfortunately, I had no clue what the hell is a makefile, that is until now.

Here is one line description I came across earlier today: “Makefiles are special format files that together with the make utility will help you to automagically build and manage your projects.” [from http://mrbook.org/tutorials/make/]

Make files were created to facilitate the tedious process of building projects that are comprised of multiple files. They help ensure that each file is compiled with the appropriate settings, and that files are compiled and linked in the proper order. This is specially useful, since most projects need to be compiled hundreds of times during the development process.

Makefile Structures
The main section of make files is comprised of a list of targets elements. Each one of these target elements is associated to dependencies, and commands that should be run when those dependencies are met. These files also hold information such as variables that can be used to capture compile options or to streamline the system commands and dependency descriptions.

Here is an example of the syntax for each target element pairing:

target label: dependencies
[tab] system command

The way to read these target element pairings is as follows:

The target label is just an identifier that functions similar to a variable name. It is good practice to make the target label consistent with the name of the file that is generated by the command associated to a target element.

For example if the system command for a target element generates a file called compiled_file.o, then a good target label would be compiled_file.o. It is not always possible to follow this guidance because sometimes you may have commands that generate multiple files, or none at all.

The dependencies feature one or more target elements that need to be ready before the command associated to the current target element is run. Sometimes variables are used to identify groups of dependencies. There are also instance where a target element has no dependencies at all.

The commands are standard unix/linux system commands that can be used in a terminal/console to compile or link files – they specify a compiler followed by compile options such as flags, input and output file information.

Sometimes a target element has no associated commands. Why is this so? This strategy is often used to confirm that multiple different targets elements have been compiled before a makefile ends.

Here is a brief overview of how to use variables in a make file. There are three important things to keep in mind. First off, all variables need to be defined at the top of a make file before the target element pairings. Here is an example of a variable declaration:

variable=gcc

Secondly, there is no variable typing since all variables hold text information. Lastly, you need to use a the following syntax to when using a variable (but not when declaring it):

$(variable)

Here is an example of what the contents of a makefile may look like. This is a pretty simple makefile that does not make use of any of the more advanced features of a makefile (considering the focus of this tutorial has been on providing you and me with a basic understanding of how makefiles work). This example was taken almost verbatim from http://mrbook.org/tutorials/make/.

compiler=g++

all: hello

hello: main.o factorial.o hello.o
	$(compiler) main.o factorial.o hello.o -o hello

main.o: main.cpp
	$(compiler) -c main.cpp

factorial.o: factorial.cpp
	$(compiler) -c factorial.cpp

hello.o: hello.cpp
	$(compiler) -c hello.cpp

clean:
	rm -rf *o hello

You can download a pdf version of this tutorial (if you can call it that) here.

Leave a Reply