Makefile Usage

UNIX provides a way to automate the building of programs. We will see a simple example here. The Half Adder code example consists of several programs. Here is a listing of the directory:

--> ls
Makefile        halfadder.java   halfadder3.c     main.java
halfadder.c     halfadder2.c     halfadder3.exe*  outfile
halfadder.exe*  halfadder2.exe*  index.html
Normally, we would compile the c files into executables by executing:
	 g++ -o halfadder halfadder.c
We would have to do this for each .c file. This is already tedious and a little error prone. In this case, we have to repeat this three times using different filenames. Also, we have to remember to re-do this when we make changes.

The make program uses a file of rules that we define to determine which files have to be re-built and how to do it. It uses the date stamp of the file to determine if the executable is older than the source file. It it ise, it follows the instructions we give to build the executable from the source.

The build rules have two basic parts: the dependency and the rule. The dependency relates the source to the result. It has to parts as well, the target and the list of files or other targets it is dependent on. The rule part is pretty much some shell code to build the program.

This example shows the rules for building halfadder.c into the executable.

halfadder: halfadder.c
	g++ -o halfadder halfadder.c
The target is halfadder. It is dependant on the file halfadder.c. If the executable (halfadder) is older than the .c file, then the executable must be re-built because the .c file has changed. The rule to build the executable in this case is the familiar
	g++ -o halfadder halfadder.c
In front of the build commands, there must be a tab character. Not all the targets need to be files nor do the targets need to have dependencies.

Usually, the file that contains the build rules is called either Makefile or makefile. If you use these default names, then running make just involves executing the make program. This reads the make file and checks the first target it sees. If you want to check a specific target, you put its name on the command line. If the list of dependencies for a target include other targets, make checks those as well. In our example, we want to check all the versions of halfadder by default. So the first line in the file is

all : halfadder halfadder2 halfadder3
Since all the dependencies are targets, they will all get checked. Note that there are no build commands here. The actual building will be handled by the halfadder, halfadder2 and halfadder3 targets. If we just want to check halfadder2, we run make like this
		  make halfadder2
This checks the dependencies of the target halfadder2 which are:
halfadder2: halfadder2.c
	g++ -o halfadder2 halfadder2.c
If halfadder2.c has changed, then the compile line gets run.

We are not limited to compiling things. In this example, I want to run all the programs. So I added a target called run. It has no dependencies, so if I run make like

		  make run
It executes the commands that come after it, each line starting with a tab character.
run:
	echo "----halfadder output" > outfile
	./halfadder >> outfile
	echo "----halfadder2 output" >> outfile
	./halfadder2 >> outfile
	echo "----halfadder3 output" >> outfile
	./halfadder3 >> outfile
Note that the output fom running the versions of halfadder is redirected into a file outfile. The double > tell the shell to append the output to the file. But first, we insert a message into the file like this:
	echo "----halfadder output" > outfile
echo is a shell command that simply prints the command line to the output. In this case, the quoted string goes into the file outfile. The single > means to first create or erase the file before writing the message.

We have one more target without dependencies in here. This is used to remove the exceutables.

clean : 
	rm -f halfadder halfadder2 halfadder3
	rm -f halfadder.exe halfadder2.exe halfadder3.exe
The -f option tell remove to delete the file without messages. In this case, we use it because we don't want to see error messages if the files don't exist. There are two remove lines because on a real UNIX system, the executables have no extension but under Cygwin, they end in .exe.

These are just some of the basic uses and operation of make. There are many builtin rules to make automating program compilation easier. There are whole books written on how to use it.

Here is the complete make file.

all : halfadder halfadder2 halfadder3

halfadder: halfadder.c
	g++ -o halfadder halfadder.c
halfadder2: halfadder2.c
	g++ -o halfadder2 halfadder2.c
halfadder3: halfadder3.c
	g++ -o halfadder3 halfadder3.c

run:
	echo "----halfadder output" > outfile
	./halfadder >> outfile
	echo "----halfadder2 output" >> outfile
	./halfadder2 >> outfile
	echo "----halfadder3 output" >> outfile
	./halfadder3 >> outfile

clean : 
	rm -f halfadder halfadder2 halfadder3
	rm -f halfadder.exe halfadder2.exe halfadder3.exe