Files for this lab are located in: /stage/classes/archive/2011/summer/50101-1/lab/lab7/src
Note that the src directory contains two subdirectories, myfileio and mypoint. You can copy both subdirectories and their contents with cp -r:
$ mkdir lab7 $ cd lab7 $ cp -r /stage/classes/archive/2011/summer/50101-1/lab/lab7/src/* . $ ls myfileio mypoint
$ cd myfileio $ ls demo.c in1.txt Makefile myfileio.h myfileio.c test_ex1.cCopy your version of myfileio.c from last week's lab into the myfileio directory. You will use this source file for this section of the lab.
In Lab 6 you saw how to organize source code into different files. In particular, you added functions to a small library called myfileio, implemented with files myfileio.h and myfileio.c. Then you tested this library with separate driver programs (ex: demo.c, test_ex1.c). To build a program that tests this library, you used this gcc command:
gcc -Wall -std=c99 myfileio.c demo.c -o demoWith this gcc command, if the programmer updates either myfileio.c or demo.c then both source code files must be recompiled to rebuild the executable demo. In large software projects, recompiling all source code files for every single change can be very time-consuming.
Another approach is separate compilation, where .c source files are compiled separately into object files and then linked to create a single executable. Here we will walk through the steps for separate compilation to build the program "demo" from the last lab.
First, call gcc as follows. Note the use of option -c:
$ gcc -Wall -std=c99 -c myfileio.c $ gcc -Wall -std=c99 -c demo.cHere option -c tells gcc to compile source code without creating an executable. The output is instead stored in an object file with a .o extension. Object files contain machine code; however they can not be executed as stand-alone programs. You can see the .o object files created by these calls to gcc in the current directory:
$ ls *.o demo.o myfileio.o
Next, run this gcc command to link object files into a new executable. (Note .o files are used as input to gcc, not .c files):
$ gcc demo.o myfileio.o -o demoRecall that the driver program demo.c calls the function show_file(), which is implemented in another file, myfileio.c. So we have a call and implementation of a function represented in two different object files. At a high level, linking matches the reference to show_file() in object file demo.o with its implementation in myfileio.o.
You can now execute the program demo at the command line, just as you did in last week's lab:
$ ./demo in1.txt Calling show_file() ... 100 110 120 125 130One advantage of separate compilation is that is that you do not need to recompile all sources files as you update your code. Let's say you would like to build test_ex1 which also uses functions in myfileio.c. Since we have not updated myfileio.c we do not need to recompile it. All we need to do is compile test_ex1.c and then link to build the executable test_ex1:
$ gcc -Wall -std=c99 -c test_ex1.c $ gcc test_ex1.o myfileio.o -o test_ex1 $ ./test_ex1 in1.txt Number of lines in in1.txt: 5
If we update file myfileio.h or myfileio.c then the object file myfileio.o must be rebuilt. So we say that myfileio.o depends on myfileio.h and myfileio.c. When working with large software projects, it can be very cumbersome to remember which object files must be recompiled. To manage this process, programmers use makefiles to describe dependencies among files, and ensure that a file is recompiled only when its dependent files are changed. Below we will show how to use make to build the executable file test_ex1 in a single step.
Users run make with commands such as the following:
$ make target
This command searches the current directory for a file named Makefile,
and then tries to update target if it is out of date.
You will read more about rules and targets below.
If no target is specified, as shown here:
$ make
then make will use the first one in the makefile.
Follow these steps to rebuild the program test_ex1. First remove object files and executables that you already built above:
A Makefile contains rules that have the following format:
As an example, we can write this rule to compile the object file myfileio.o:
Command lines in a makefile must start with a tab character, not a sequence of spaces.
So in this example gcc is preceded by a tab.
Lines that start with # are comments and will be ignored. Lines containing only whitespace are also ignored.
Our makefile should also include these two rules to build our program:
You can use multiple lines for one command by ending the previous line with a backslash ("\").
make and Makefiles
In this section you will learn how to use the tool make that automates the compilation process for programs using multiple source files.
Basics: Calling make
You use make by creating a file named Makefile for your program.
(Note that the myfileio directory already contains a Makefile).
A Makefile is just a text file that contains rules that describe how the program should be compiled.
$ rm *.o
$ rm test_ex1
Run make as shown above (without a target). You will see that the program test_ex1 is built in the current directory.
$ make
gcc -g -Wall -c -o myfileio.o myfileio.c
gcc -g -Wall -c -o test_ex1.o test_ex1.c
gcc -g myfileio.o test_ex1.o -o test_ex1
$ ls test_ex1
test_ex1
Read through the following overview of make and Makefiles. You do not have to make any edits to the Makefile in the myfileio directory until you get to Exercise 0.
Rules
Next we will cover how to read and write Makefiles.
Before you start, take a look at the Makefile in the myfileio directory (using cat or a text editor).
target: prerequisite1 prerequisite2 ...
command
Here, target is usually the name of a file that will be updated by this rule.
The list prerequisite1 prerequisite2 ... gives files that target depends on.
On the second line, command shows how to update the target when
one of the prerequisite files are modified.
# compile myfileio.o
myfileio.o: myfileio.c myfileio.h
gcc -g -Wall -c myfileio.c
By listing myfileio.c and myfileio.h as the prerequisites,
we tell make that myfileio.o must be recompiled whenever myfileio.c or myfileio.h is updated.
To ensure that this happens correctly, make will check
the timestamps on all files and recompile myfileio.o when either of the source files has a newer timestamp.
If myfileio.o is not out of date then it will not be recompiled.
# compile test_ex1.o
test_ex1.o: test_ex1.c myfileio.h
gcc -g -Wall -c test_ex1.c -o test_ex1.o
# link test_ex1
test_ex1: test_ex1.o myfileio.o
gcc myfileio.o test_ex1.o -o test_ex1
Phony targets: all and clean
The command line need not be a compilation instruction; instead it can be any shell command, or even empty.
These targets are known as phony targets
It is conventional to include a target named "all" to update all outputs of the makefile:
# top-level rule all: test_ex1This rule has no command, instead it will call the rule with target "test_ex1" that we defined above. It is common to make "all" the default target, so that all outputs are updated when the user calls make without specifying a target (i.e., just entering "make" at the command line).
Recall that we can make "all" the default by listing it as the first rule in the makefile.
It is also common to include a "clean" target to remove outputs of the makefile such as executables and .o files:
clean: rm -f test_ex1.o myfileio.o test_ex1Here we use the -f flag with rm to avoid generating error messages when a file does not exist.
The command make clean will do what we expect as long as we don't have a file in the current directory named "clean". To avoid problems when this file does exist, use the special target .PHONY to indicate that "clean" is not the name of an actual file:
.PHONY: clean clean: rm -f test_ex1.o myfileio.o test_ex1
# use "gcc" to compile source files CC = gcc # set compiler flags CFLAGS = -Wall -gWe then update our rules to use these variables. To access the value of a variable use $( ) as shown below.
# top-level rule all: test_ex1 # compile myfileio.o myfileio.o: myfileio.c myfileio.h $(CC) $(CFLAGS) -c myfileio.c # compile test_ex1.o test_ex1.o: test_ex1.c myfileio.h $(CC) $(CFLAGS) -c test_ex1.c # link test_ex1 test_ex1: test_ex1.o myfileio.o $(CC) $(LDFLAGS) myfileio.o test_ex1.o -o test_ex1 # remove outputs .PHONY: clean clean: rm -f test_ex1.o myfileio.o test_ex1
Variables can be set from the command line, in which case they override the definitions in the makefile:
$ make "CFLAGS=-g" gcc -g -c -o myfileio.o myfileio.c gcc -g -c -o test_ex1.o test_ex1.c gcc -g myfileio.o test_ex1.o -o test_ex1Note that "-g" is used instead of "-g -Wall" as set in the Makefile.
Automatic variables come in handy especially when writing implicit rules. Implicit rules are extremely useful tools to specify how to deal with all files with the same suffix. For example, the rule
%.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@specifies how to create an object file n.o from source file n.c. Note how no particular target is mentioned, instead the '%' character is used to match with any object file that ends in .o.
There are a number of "built-in" implicit rules that are always available unless you override them specifically. You can review these rules in the GNU Manual. In particular, note that:
The myfileio directory of this lab contains the Makefile for the program described above. In particular, note that these rules do not have a command line:
Here, myfileio.c will be compiled to object file myfileio.o using built-in rules. Likewise test_ex1.c will be compiled to output test_ex1.o. Since myfileio.h is included in the prerequisite lists, myfileio.o and test_ex1.o will both be recompiled when myfileio.h is updated.# compile - relies on predefined rule for .c files myfileio.o: myfileio.c myfileio.h test_ex1.o: test_ex1.c myfileio.h
Open the file myfileio.h and add a space character at the end of the file. This will update the timestamp on myfileio.h. Then, call make again. You will see that test_ex1 is rebuilt:
$ make gcc -g -Wall -c -o myfileio.o myfileio.c gcc -g -Wall -c -o test_ex1.o test_ex1.c gcc -g -Wall myfileio.o test_ex1.o -o test_ex1Next remove the executable test_ex1 and call make again. You will see that the executable file is rebuilt but object files are not recompiled. This is because make detects that the prerequisites of myfileio.o and test_ex1.o were not updated since these files were last recompiled.
$ rm test_ex1 $ make gcc myfileio.o test_ex1.o -o test_ex1
struct mypoint { double x; double y; };This structure declaration shows how a mypoint object is represented in memory. Note that structure declarations end with a semicolon (";"). It is a common compile-time error to forget the semicolon at the end of a structure definition.
A variable of type struct mypoint can be declared as:
struct mypoint p1;The members of a mypoint variable can be accessed with the "." (dot) operator. For example:
struct mypoint p1; p1.x=1.0; p1.y=5.0;You can also initialize a structure with a comma-separated list:
struct mypoint p1 = {1.0,5.0};
You can declare pointers to structures. For example, a pointer to p1 would be:
struct mypoint * p_p1 = &p1;The -> operator is used to access structure members with a pointer.
printf("Point p1: (%.2f,%.2f)\n",p_p1->x,p_p1->y);The expressions p_p1->x and (*p_p1).x evaluate to the same value.
You can read more about structures in Chapter 14 of Prata.
The mypoint directory includes the file simple.c, a simple program that declares and uses the mypoint structure as shown above.
double get_distance( struct mypoint * p_p1, struct mypoint * p_p2);Note that this function is call-by-reference, since its arguments are pointers to structures.
./distance <x1> <y1> <x2> <y2>for points p1=(x1,y1) and p2=(x2,y2). You can use the function atof() in stdlib.h to convert a string to a double. (You should then also include stdlib.h.)
$./distance 1 5 4 9 Distance between (1.00,5.00) and (4.00,9.00): 5.00
LDFLAGS = -lm
$ make clean rm -f mypoint.o distance.o distance $ make gcc -g -Wall -std=c99 -c -o mypoint.o mypoint.c gcc -g -Wall -std=c99 -c -o distance.o distance.c gcc -lm mypoint.o distance.o -o distance $ ./distance 1 5 4 9 Distance between (1.00,5.00) and (4.00,9.00): 5.00Your output may be slightly different so long as the above requirements are met.
Lab 7 is due: Saturday, Aug 13 at 11:59pm
You may submit the contents of the mypoint directory for this lab. But, call make clean before you submit! This should remove files mypoint.o, distance.o, and distance.
I will grade your work in these files:
Be sure that every source file you submit includes this information in comments at the top of the file:
Also include a simple README file in your lab7 directory with your name, username, and any other comments or questions for the Lab TA.
Do not submit any compiled binary files
You will submit this lab with the hwsubmit program.
$ hwsubmit cspp50101lab <path to your lab7 directory>
prepared by Sonjia Waxmonsky