Makefiles

From Redbrick Wiki
Revision as of 04:03, 21 October 2007 by Receive (talk | contribs) (category)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

A Brief introduction to Makefiles

This is a basic demonstration of how to write simple makefiles to manage any programming projects you might be doing on redbrick (or indeed in any development environment). The keyword in the last setence is basic as it doesn't explain how to fully utilise make or explain properly a lot of the makefile syntax ;o)

Make

Make is a program which can be found on most UNIX systems and even in windows (Visual C++ ships with a version of Make called NMake). It is similar to shell scripting in the way it works. You run make in a directory which contains either a file called "Makefile" or "makefile" (if both files are in the directory it would seem to use "makefile") and it reads the file and executes the commands in the file (unless an error occurs).

Sometimes you may load a proram onto your directory on redbrick only to find that it's just a file of the form "blah.tar.gz" and then unpack it and get a load of c source code files which you don't know how to compile or link together. This is where the packaged Makefile will come in handy. Just cd to the directory the files are in, type make and make will automatically execute the commands in the makefile for putting the source code files together into an executable (binary) file. In the following example, I use tar to unzip a tar file, then to untar it, run ls to get a file listing, then execute make to get the final file.

The prompt starts here:{58}(~/crap)% ls         <cokane@enigma ZSH emacs 18:29>
program.tar.gz
The prompt starts here:{60}(~/crap)% tar -zxvf program.tar
foo.c
foo.h
main.c
makefile
The prompt starts here:{61}(~/crap)% ls         <cokane@enigma ZSH emacs 18:30>
foo.c           foo.h           main.c          makefile        program.tar
The prompt starts here:{62}(~/crap)% make       <cokane@enigma ZSH emacs 18:30>
gcc -c main.c
gcc -c foo.c
echo "This is makefile"
This is makefile
gcc -ansi -o program main.o foo.o
rm *.o
The prompt starts here:{63}(~/crap)%            <cokane@enigma ZSH emacs 18:30>

How Make works

Makefiles work very much like shell scripts in that they run through a series of commands executing them one at a time depending on whether certain conditions are satisfied. The two most common types of lines you'll see in a makefile are variable declarations and "make instructions" (for want of the proper actual word :o)).

Variable declarations allow you to give names, or aliases, to filenames or commands so that you aren't always dealing with a big long string of words, when you could just use one short variable name in it's place, e.g.

CC=gcc
FLAGS=-ansi -Wall

This allows you to use the CC variable whenever you need to call the compiler (a program known as gcc) and to use the FLAGS variable whenever you want to add those particular flags to the compiler. A sample short makefile for compiling one program might be:

CC=gcc
FLAGS=-ansi -Wall
FLAG=-o
PROGRAM=prog
OBJS=main.o lib.o
prog:
  $(CC) $(FLAGS) $(FLAG) $(PROGRAM) $(OBJS)

The other type of line, besides variable declarations, that you will see, is the last line in the example above. Here we have a "target" file which we have to make. Make looks to the bits to the right of the colon to see what it has to do to "make" the target on the left. Now here's where it gets interesting, if any of the requirements on the right of the colon are themselves "targets" further on down in the makefile, then make will go and make them before continuing. The items on the right of the semi-colon are known as "dependencies" and for make to make the target on the left, it must first make the dependencies.On the following lines (which must be indented by a tab space) make gets the instructions for what to do to make the target on the left of the semi-colon.

In the sample above, the target prog has no dependencies, so make goes ahead and makes prog using the instructions on the next line. Here we just use a bunch of variables which make knows to convert to their original meanings from the $(variable_name) syntax. So once make converts the requirements for prog, it ends up with this:

gcc -ansi -Wall -o prog main.o lib.o

As you may know this is just a program and all of it's arguments, make then executes this as if it was a program (you can run into errors if make tries to execute something which is nonsensical). Make also prints out what it is going to execute before executing it.

I will now show you a sample makefile which firstly compiles some c source code files into object files, and then links those files together to form the finished program.

# Comments are the same as in shell scripts, i.e. start a line with a # symbol
CC= gcc
LD=ld
CCFLAGS= -ansi -o
CFLAGS= -c # This flag compiles a source code file into an object (.o) file..
PROG= program
OBJECTS= main.o foo.o
program: $(OBJECTS) # In order for program to be made, the OBJECTS targets must..
       echo "This is makefile" # be made first...
       $(CC) $(CCFLAGS) $(PROG) $(OBJECTS)
main.o: main.c
       gcc -c main.c # There is no need for variable names if you don't want them
foo.o: foo.c
       $(CC) $(CFLAGS) foo.c
clean:
       rm *.o

Make arguments

Sometimes you will read the instructions for setting up a program that you have downloaded on a UNIX system and it will say something like "Run make install, then run ,make BSD.". These arguments are just names of targets within the makefile. In the above example you can see that there is a target named "clean" which isn't required in order to make the target named program, so it isn't called normally. To call it you have to type

make clean

from the prompt. This will make the clean target, which in this case just deletes any object files in the current directory. As you can guess, make will only by default make the first target it comes across in a makefile. Any other unnecessary targets won't be made.