Lab 2 (10 points)
 January 26-27, 2010



Laboratory Assignment #2 —
The Unix/Linux “make” facility

Due: at 11:59 pm on the day of your lab session

Objective

To learn to create makefiles and to use the make command in Linux and Unix.

Introduction

This week’s lab introduces an important tool:– the make command. 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 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.)

In Part 1 of this Lab, you will experiment with make and a pre-existing makefile. In Part 2, you will create a makefile for your submission for Programming Assignment #1 and resubmit that assignment for this Lab.

Getting Started

1.      Sign the attendance sheet.

2.      Create a directory to hold the files for Lab 2, and change to that directory now. Download the zip file at the following URL and unzip it into your directory:–

http://web.cs.wpi.edu/~cs2303/c10/Common/Lab2Files.zip

Your directory should now contain the files intarray.c, intarray.h, sinewave.c, and makefile.

What’s in a Makefile?

The collection of files needed to build a system or application usually includes header files (.h files), source files (.c files or source code files for the language you are using), compiled files (.o files), and sometimes other kinds of files. Many of these depend on each other. For example, consider the program downloaded for this week’s lab. The final product is an executable file named sinewave, which is created by linking together two other compiled files, sinewave.o and intarray.o. The two compiled files are created by compiling sinewave.c and intarray.c (both of which include the header file named intarray.h). So, one of the dependencies expressed in the makefile is:–

      if sinewave.c or intarray.h changes, then sinewave.o must be regenerated by the compiler command:–

gcc -Wall -c sinewave.c

To see this dependency, open up the file named makefile in kwrite, emacs, or your favorite editor.

Near the bottom of the file you’ll find these two lines:–

sinewave.o: intarray.h sinewave.c
        gcc -Wall -c $(CFLAGS) sinewave.c

The first of these 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 of files 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

This causes the variable CFLAGS to default to –g, indicating that the programs should be compiled for debugging. 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 linking 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 -Wall sinewave.o intarray.o -o sinewave

This target line says that if either 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.

Part 1: Practice Using Make

There are two simple ways to use the make facility to automatically regenerate your files. The first approach builds a specific file. For example, suppose you want to regenerate intarray.o. Then you can use the make command shown here. Type this command in now:–

make intarray.o

The make command will find the dependency information in the makefile. It sees that intarray.o depends on two 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 in your command shell or on your display screen. It this case, it would be:–

gcc -Wall -c –g 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.

Now trying using the make command without specifying a file, like this:–

make

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, make discovers that sinewave depends on intarray.o and on sinewave.o. But the file sinewave.o is not present. So, before make can build sinewave, it must first regenerate sinewave.o. Only then it can proceed to regenerate the executable file sinewave. On the screen you will see the two steps displayed:–

gcc -Wall -c –g sinewave.c

gcc -Wall sinewave.o intarray.o -o sinewave

Next, edit the file intarray.h in some trivial way, for example, by adding or changing a comment. Now type the command

make

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 the following command:–

make CFLAGS=

This simply disables compiling for the debugger. You may also change the compiler flags, for example, by

make CFLAGS=O       or    make CFLAGS=O2

This 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.

To get a better feeling about how dependencies work, edit your makefile to remove the dependency of sinewave.o depends up intarray.h. Again make a trivial edit to intarray.h, and then type make. You will notice that it did not rebuild sinewave.o. This, of course, is an error.

Cleaning up

Notice the last two lines of the makefile. These say

clean:
        rm -f sinewave.o intarray.o sinewave

This 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

make clean

and then list your directory. You will see that sinewave and the .o files have disappeared. Now type

make

and you will see that they have been rebuilt.

Using make from within Emacs

If you use emacs as your editor, you can edit and compile from within emacs. For example, make a small edit in one of the .c or .h files, and then give the emacscompile” command by typing

ALT-x compile <ENTER>

This causes emacs to issue the “make –k” command in the current directory. The -k tells make to continue as much as possible after an error. The make program realizes that one of the source files 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.

Part 2: Getting Credit for this Lab

To get credit for this lab, you need to construct and submit a makefile for the programs in Programming Assignment #1, which was due last Thursday. The easiest way to do this is to copy the makefile from this assignment and to edit the dependencies to conform to the files of your programming project. Submit your files as follows:–

/cs/bin/turnin submit cs2303 LAB2 makefile <your PA1 files>

(There is no need to resubmit the README file.) The graders will attempt to rebuild your PA1 project by downloading your files to a sub-directory and then typing make and make clean.

If by chance you did not submit anything for PA1, speak to the TAs and ask to be graded for this lab as part of Programming Assignment #2.

Future Programming Assignments

Please note that Programming Assignment #2 and all future programming assignments in CS-2303 and any other course at WPI must be accompanied by makefiles unless you are explicitly instructed otherwise.

Reference

For more information about make and makefiles, consult the man pages, the web, and/or the following URL

http://linuxdevcenter.com/lpt/a/1426