Why learn about makefiles?
This is motivated well by :
The fact of the matter is that it is altogether too easy to forget to recompile some source file. Then, when the different object code files are merged, we may end up with a program that does not work, and is hard to debug because it does not correspond to the current source code. A simple, but wasteful, way to avoid this kind of problem is to recompile every source file after a series of changes have been performed.
make program is a software tool that can be used to keep
track of which files need recompiling after any changes have been made and
to actually issue the sequence of commands that performs all the necessary
target ... : dependencies ... command ... ...
g++ -MM myFile.c
makecarries out. A rule may have more than one command, each on its own line.
makedoes not know anything about how the commands work. It is up to you to supply commands that will update the target file properly. All
makedoes is execute the commands in the rule you have specified when the target file needs to be updated.
makeis not called with any arguments, by default it executes the first target
A Simple Makefile
Here is a straightforward makefile that describes the way an executable
edit depends on 4 object files which, in turn,
depend on four C source and three header files.
edit : main.o kbd.o command.o display.o g++ -g -o edit main.o kbd.o command.o display.o main.o : main.c defs.h g++ -g -c main.c kbd.o : kbd.c defs.h command.h g++ -g -c kbd.c command.o : command.c defs.h command.h g++ -g -c command.c display.o : display.c defs.h buffer.h g++ -g -c display.c clean : rm edit main.o kbd.o command.o display.o
To use this makefile to create the executable file called 'edit', type:
To use this makefile to delete the executable file and all the object files from the directory, type:
Now we're going to "spice" up the above makefile example a bit. To start with, we will add a few variables. A variable is defined in the makefile using format:
variable_name = value
In our case, we would like to define a variable for all of the object files. This way, we only need to list all of the objects files once.
Further, it is stylistic to specify the compiler type and options in only one place. This way different compiler specifications can be used easily.
COMPILER = g++ CFLAGS = -g OBJECTS = main.o kbd.o command.o display.o edit : $(OBJECTS) $(COMPILER) $(CFLAGS) -o edit ($OBJECTS) main.o : main.c defs.h $(COMPILER) $(CFLAGS) -c main.c kbd.o : kbd.c defs.h command.h $(COMPILER) $(CFLAGS) -c kbd.c command.o : command.c defs.h command.h $(COMPILER) $(CFLAGS) -c command.c display.o : display.c defs.h buffer.h $(COMPILER) $(CFLAGS) -c display.c clean : rm edit $(OBJECTS)
Dealing with errors in commands
After each shell command returns,
make looks at its exit status. If the command completed successfully, the next command line is executed; after the last command line is finished, the rule is finished.
If there is an error (the exit status is nonzero),
make gives up on the
current rule, and perhaps on all rules.
Sometimes the failure of a certain command does not indicate a problem.
For example, in the above we don't want an error reported if one of the
object files does not exist for removal by
To ignore errors in a command line, write a `-' at the beginning of the line's text (after the initial tab). The `-' is discarded before the command is executed. In our case, this will look:
... clean : -rm edit $(OBJECTS)
Now the makefile will continue executing in spite of errors reported by
Adding echo statements and automatic variables
make prints each command line before it is executed.
This is referred to as echoing because it gives the appearance that you
are typing the commands yourself.
When a line starts with `@', the echoing of that line is suppressed.
The `@' is discarded before the command is executed. Typically this feature
is only used with
echo commands that indicate progress through
Automatic variables have values computed afresh for each rule that is executed, based on the target and dependencies of the rule. For example:
Incorporating these features into our running example, we get:
COMPILER = g++ CFLAGS = -g OBJECTS = main.o kbd.o command.o display.o edit : $(OBJECTS) @echo "$@ being recompiled due to updates in $?" $(COMPILER) $(CFLAGS) -o $@ ($OBJECTS) main.o : main.c defs.h @echo "$@ being recompiled due to updates in $?" $(COMPILER) $(CFLAGS) -c $< kbd.o : kbd.c defs.h command.h @echo "$@ being recompiled due to updates in $?" $(COMPILER) $(CFLAGS) -c $< command.o : command.c defs.h command.h @echo "$@ being recompiled due to updates in $?" $(COMPILER) $(CFLAGS) -c $< display.o : display.c defs.h buffer.h @echo "$@ being recompiled due to updates in $?" $(COMPILER) $(CFLAGS) -c $< clean : -rm edit $(OBJECTS)