This week's lab introduces an important Linux tool: the make facility. This allows you to easily maintain and keep your files up to date when you are working on a project that is split across several .c files and their associated .h header files. You will find your multi-file programs easier to manage if you keep all of the files for one project in a directory, and create a makefile, as described below, to manage the compilation of the files.
The file makefile is the usual input file for a tool called make, which can be invoked from the command line of a Linux shell. The purpose of make is to help you build, update, and maintain a collection of related program files. It does this by figuring out which files have changed since you last built or rebuilt your programs. It keeps track of dependencies among files, so that it knows which have to be recompiled as a result of any edits or other changes you make.
A properly designed makefile is normal part of every non-trivial programming project in Linux or Unix, whether at WPI or in the professional world. It allows someone else - a grader, a boss, a colleague, or a user - to rebuild your system in another situation by simply typing the command make. Using the instructions and dependencies encoded in the makefile, make takes care of it from there, and the result should be an executable copy of the program, system, or application. (Equivalent tools are part of Visual Studio, Eclipse, and other integrated development environments.)
if sinewave.c or intarray.h changes, then sinewave.o must be regenerated by the compiler command:
gcc -Wall -c sinewave.cTo see this dependency, open up the file named makefile using your favorite editor. Near the bottom of the file you'll find these two lines:
sinewave.o: intarray.h sinewave.c gcc -c $(CFLAGS) sinewave.c
The first line is called a target line, which begins with a file name and a colon. The file is called the target file. After the colon is a list of zero or more names that the target file depends on. Whenever one of the files after the colon is edited or changed, the target file becomes "out of date". The make tool uses the target line and the last modification dates of the files to determine whether the target file must be rebuilt. It does this recursively, so that if one of the files upon which the target depends is itself out of date, make causes that file to be rebuilt first, etc.
(Note: make depends upon a consistent and reliable time-of-day-and-date clock in order to determine whether one file is "newer" than another. If you are using more than one computer and their clocks are not in sync, modification dates on files could be inconsistent with each other, and make could produce unexpected results.)
After the target line, there is a series of commands that tell exactly how to rebuild the target file. For the case of sinewave.o, only one gcc command is needed to compile the file. Note that this command includes the -c flag to tell gcc that it should only compile the specified file(s) and not attempt to link them into an executable program yet.
The gcc command also includes the notation $(CFLAGS). This is not a compiler switch but rather a variable to make. It lets you control from the command line which compiler switches you wish to apply to all of the files compiled by make. The makefile also contains the following line:
CFLAGS = -g -Wall
This causes the variable CFLAGS to default to -g -Wall, indicating that the programs should be compiled for debugging, and with warnings enabled. We will see below how to override this default.
(Note: There is one other peculiar requirement of makefiles: command lines, such as the gcc command, must each begin with a tab character, not a sequence of blanks!)
Continuing with the theme of dependencies among files, the executable file sinewave is created by compiling together the object files intarray.o and sinewave.o. If either of these two object files should change, then sinewave also needs to be recreated. Here is the appropriate target line and command from our makefile:
sinewave: intarray.o sinewave.o gcc $(CFLAGS) -lm sinewave.o intarray.o -o sinewave
This target line says that if sinewave.o or intarray.o is newer than the target file sinewave, then make must cause the sinewave program to be regenerated with the gcc command shown.
The make command will find the dependency information in the makefile file. It sees that intarray.o depends on other files, so it will first ensure that those files are present (and regenerate them if necessary). In this example, the two files intarray.h and intarray.c are necessary for generating intarray.o. These two files are present, so the make command proceeds to generate intarray.o, using the gcc command that is specified in the makefile. When the gcc command is executed, it is displayed on the screen, so you will see this appear on the screen:
gcc -c -g -Wall intarray.c
After this make command finishes, you should list the files in your directory, where you will find the object file intarray.o is now present.
Without a specified file, the make command will regenerate the first target that it finds in makefile. Try this now, and you will see that the executable file sinewave is regenerated, since sinewave is the first target file in makefile. During the process of regenerating the sinewave file, the make command had to carry out several steps. In the first step, the make command realizes that sinewave depends on intarray.o and also on sinewave.o. But the file sinewave.o is not present. So, the make command first regenerates sinewave.o, and then it can proceed to regenerate the executable file sinewave. On the screen you'll see the two steps displayed:
gcc -c -g -Wall sinewave.c gcc -g -Wall -lm sinewave.o intarray.o -o sinewaveNext, edit the file intarray.h in some trivial way, for example, by adding or changing a comment. Now type the command
again and you will see that it rebuilds everything. Can you explain why?
Finally, if you wish to turn off the -g switch, you may use one of the following commands:
The first simply disables compiling for the debugger. The second tells the compiler to optimize the compiled code for speed. You can consult the man page for the gcc command for other compiler switches, which may also be specified in the CFLAGS variable.
clean: rm -f sinewave.o intarray.o sinewaveThis is a target line to "make" the target called clean. You can see that clean does not have any dependencies, but it does have one command line, namely a command to remove the .o files and the original target sinewave. This is common practice in system programming to provide a way of cleaning up intermediate files (in this case the .o files) needed to build a system, leaving only the original files.
Type the command
and then list your directory. You will see that sinewave and the .o files have disappeared. Now type
and you will see that they have been rebuilt.
This causes emacs to issue the "make -k" command (the -k tells make to continue as much as possible after an error). The make program realizes that sinewave.c has changed, and proceeds to rebuild it as if you had invoked make yourself.
This feature is not available in other editors such as kwrite, vi, or pico. However, something equivalent is available in integrated development environments such as Visual Studio and Eclipse.
See you next week.