Skip to main content

2. A simple makefile

Following on from the previous section, instead of building an executable by hand, you can use a utility called make. make can automate the process of building and linking object files. Furthermore, make will minimize rebuilds - important if some of your source files take a long time to compile. Provided you inform make properly, this utility can also take care of all of you source dependencies for you (e.g. in the last example, hellomain.c depends on hellofunc.h and hellofunc.c).

To inform make about what to do, you need to create a file called a Makefile.

Putting it into Practice:

Using the exercise from the previous section, we will instead create a Makefile to make the executable myexecutable. To do this, use emacs to create a file called Makefile, an in this file, type:
  myexecutable:  hellomain.o hellofunc.o
          cc -o myexecutable hellomain.o hellofunc.o
hellomain.o: hellomain.c cc -c hellomain.c -I.
hellofunc.o: hellofunc.c cc -c hellofunc.c -I.
WARNING: the 2nd, 4th and 6th lines MUST start with a TAB! This is not just blank space!
Now save this file and quit out of emacs. To build your program, at the prompt type:
 make myexecutable  
Hit return. This should have built your executable. Try running it by typing at the prompt:
Now save this file and quit out of emacs. To run your program, at the prompt type:
 ./myexecutable 

In the above Makefile, there are 3 entries. Each entry comprises a line containing a colon (:) - this is called a dependency line, and one (or more) lines starting with a tab (these are command lines). On the dependency line, the target is to the left of the colon, and to the right of the colon are the target pre-requisites. The command lines instruct how to build the targets out of their pre-requisites.

When make is invoked, the first command line in the file will not be the first that will be executed. First, make will check to see if myexecutable already exists. If it does, make will then check the pre-requesites hellomake.o and hellofunc.o to see if any of these are newer than myexecutable. If your program was built since the latest modifications to hellomake.o and hellofunc.o, make will decide there is no need to rebuild myexecutable, and will just exit without having changed or built anything.

Putting it into Practice:

You should have all your .o files and the file myexecutable left from the last exercise. Now, see what happens if you try to build your program, by typing (in the usual way..)
 make myexecutable 
If everything is up to date, you will get the message to your prompt:
 make: `myexecutable' is up to date.

In general, however, make will also check each of the object (.o) files, to see if the pre-requesites of these objects are up to date. Therefore, if hellofunc.c was last modified after hellofunc.o was built, then make will execute the command to re-compile hellofunc. The same goes for the dependencies of hellomain.o. For example, if hellofunc.c is more recent than hellomain.o, then make will re-build hellofunc.o and ....


Putting it into practice

Assuming no files have been changed since the last exercise, try updating the access time of one of your source files
hint: do this by typing, eg, touch hellofunc.c
and then, again type
 make myexecutable 
make should now rebuild the executable, since now your source file was newer than the current executable
Re-touch one of your files and just type make at the prompt. What happens?


If you don't specify the target you wish to build when invoking make on the command line, it will build the first target executable that is listed in the Makefile. So far this isn't an issue, because your Makefile only contains rules for making one executable target. make is really useful for specifying rules for making several executable targets. For example, you could in principle re-write your code (or any code) such that the prgram could be run from a graphical user interface (GUI). Let's say the GUI code is named gui.c, and that gui.c calls hellomain.c. Then you could specify two executables for your code - one with a GUI (called, say, gui_executable) and one just run at the shell prompt (called, say, prompt_executable). The makefile could then look like the following:

gui_executable: gui.o  hellomain.o hellofunc.o 
   cc -o gui_executable gui.o hellomain.o hellofunc.o 

prompt_executable: hellomain.o hellofunc.o 
   cc -o prompt_executable hellomain.o hellofunc.o 

gui.o: gui.c
   cc -c gui.c -I.

hellomain.o: hellomain.c
   cc -c hellomain.c -I.

hellofunc.o: hellofunc.c
   cc -c hellofunc.c -I.

This way you can make either of these two targets. To make the first executable, you could (in principle) type make gui_executable at the shell prompt. To make the second target, you would need to type make prompt_executable at the shell prompt.

Dependency lines do not always have to contain pre-requisites (to the right of the colon). A good example of this is a common target in make is called clean. This target basically deletes all temporary files associated with the build. The example entry in a Makefile would be:

clean:
   /bin/rm -f core *.o

By typing make clean at the prompt, all the specified files will be deleted. One caveat is that there shouldn't exist a file actually called clean in your directory!


Putting it into practice

Add a clean target into your existing Makefile, and see if the appropriate files are deleted when you type make clean at the prompt.

One final point: you can always put comment statements into your Makefile by using the hash symbol (#) at the start of the line. If using emacs with font-lock-mode enabled, these should come up as red (comment) lines.