Home
Netbeans Eclipse Qt Java
Games
College of Engineering Aeronautics and Astronautics Agricultural and Biological Engineering Biomedical Engineering Chemical Engineering Civil Engineering Construction Engineering and Management Electrical and Computer Engineering Engineering Education Engineering Professional Education Environmental and Ecological Engineering Industrial Engineering Materials Engineering Mechanical Engineering Nuclear Engineering
EPICS (Engineering Projects In Community Service) First-Year Engineering Program First-Year Engineering Honors Program Global Engineering Program Minority Engineering Program Professional Practice (Co-Op) Program Women in Engineering Program
College Administration Schools Programs All Groups All People ECN Webmail
Purdue Home

ECE 264 Exercise 3

Makefile

In the previous exercise, you learned how to compile and link multiple files. It may seem that writing a program using multiple files is troublesome.  You have to compile the individual source files separately and then link them. Wouldn't it be all right to write a program using only one file? First, let me explain the advantages of using mulitple files. Then, I will explain a tool, called make, to help you manage multiple files.

Advantages of Multiple Files

It is impossible to write a C program using a single file. When you include a header file, you are already using multiple files.You do not implement printf, do you? Where does it come from? It comes from C libraries, such as libc.so in /usr/lib/. Why is C designed in this way?  What are the advantages of writing a program using multiple files?

  • Reduce redundant work.  Many frequently used functions are supported by libraries. You don't have to write your own printf, scanf, or fopen.
  • Improve portability. Some functions handle low-level activities related to hardware, such as reading files from a disk or sending packets through a network. It is better to isolate the hardware-specific in libraries so that your program can run on different computers, as long as your program uses the correct libraries (usually need to recompile the source code).
  • Enhance performance. Libraries are well optimized so your program can have better performance.
  • Partition work for collabortion. When you write a large program with other people, it is natural to break the work into smaller units. Each of you is responsibile for some units. Each unit is stored in a single file.
  • Save compilation time. Compiling a large program can take hours. If the whole program is in a single file and you change only one line, you have to wait for hours to check whether your change is correct. A better solution is to break the program into many files and compile only the files affected by your change. Then the object files are linked. This may take a few seconds, much better than a few hours. 

The last advantage is critical in developing large programs. This is an important reason to separate compilation from linking.

Makefile

(Please read 11.17 in "A Book on C".)

Even though there are so many advantages using multiple files, nobody is patient enough to type "gcc -c file1.c ... gcc -c file2.c ..." every time. Fortunately, there are many tools to help, among them is make. This is a UNIX command and by default, it takes an input file called Makefile. This exercise explains how to write simple Makefiles.

Download the code used in this exercise. Use the command "unzip" to extract the files.

The following is a basic Makefile to compile and link the program.


main: main.o f1.o f2.o
	gcc main.o f1.o f2.o -o main

main.o: main.c f1.h f2.h
	gcc -c main.c

f1.o: f1.h f1.c
	gcc -c f1.c

f2.o: f2.h f2.c
	gcc -c f2.c

clean:
	rm *.o main


Type the above code into a file called Makefile and save it.

The first line means the program main depends on three files: main.o, f1.o, and f2.o. A .o file is called an object file. If these three files are available, the second line uses gcc to link the three object files and create the executable called main.  Please notice that the command gcc is after a TAB, not space. How are the three object files are created? They are created by the following three rules. The file main.o requires three files: main.c, f1.h, and f2.h. The following line uses gcc to compile (with the -c flag) main.c and create the object file main.o.  The same situation applies to f1.o and f2.o.  How do you use Makefile? Simply type

          make

and you will see the following messages

 

gcc -c main.c
gcc -c f1.c
gcc -c f2.c
gcc main.o f1.o f2.o -o main

 

 

You can execute the program by typing

         ./main

What is so important about Makefile? It is the dependence rules.

main: main.o f1.o f2.o

says that the executable main depends on main.o, f1.o, and f2.o. If any of the three object files has been changed, execute the following command

	gcc main.o f1.o f2.o -o main

How does make know whether f1.o has changed? It checks the following rule

f1.o: f1.h f1.c

If f1.h or f1.c is changed, execute the following command

	gcc -c f1.c

If neither f1.h nor f1.c has changed, then f1.c will not be compiled. How does the computer know whether a file has been changed? It compares the time of the files. If f1.c's time is later than f1.o's time, make thinks f1.c has been changed and generates a new f1.o.  If you the following command

            touch f1.c

the time of f1.c is set to the current time and it is later than f1.o's time. Now, type

            make

again and you will see the following message

gcc -c f1.c
gcc main.o f1.o f2.o -o main

The message means that f1.c is compiled again and a newer version of main is created. Notice that f2.c is not recompiled. This saves time.  If a file has not changed, it is unnecessary to compile it again.  The last rule says, if you type

                     make clean

the object files and main are deleted.

In Makefile, anything after # is treated as a comment.

Define Symbols

We can define symbols in Makefile. For example


GCC = gcc
CFLAGS = -Wall

main: main.o f1.o f2.o
	$(GCC) $(CFLAGS) main.o f1.o f2.o -o main

main.o: main.c f1.h f2.h
	$(GCC) $(CFLAGS) -c main.c

f1.o: f1.h f1.c
	$(GCC) $(CFLAGS) -c f1.c

f2.o: f2.h f2.c
	$(GCC) $(CFLAGS) -c f2.c

clean:
	rm *.o main

If we decide to use another compiler, we can change this line

GCC = gcc

without changing any other lines because GCC is substituted in the following lines. We can also change the flags by adding -g for debugging or -O for optimization.

We can also create a symbol OBJS that represents all object files:


GCC = gcc
CFLAGS = -Wall
OBJS = main.o f1.o f2.o 
main: $(OBJS)
	$(GCC) $(CFLAGS) $(OBJS) -o main

main.o: main.c f1.h f2.h
	$(GCC) $(CFLAGS) -c main.c

f1.o: f1.h f1.c
	$(GCC) $(CFLAGS) -c f1.c

f2.o: f2.h f2.c
	$(GCC) $(CFLAGS) -c f2.c

clean:
	rm *.o main

 

Special Symbols

You can use $@ in the second line to replace the name before : in the first line.  The previous Makefile is equivalent to the following

GCC = gcc
CFLAGS = -Wall
OBJS = main.o f1.o f2.o

main: $(OBJS)
	$(GCC) $(CFLAGS) $(OBJS) -o $@

main.o: main.c f1.h f2.h
	$(GCC) $(CFLAGS) -c main.c -o $@

f1.o: f1.h f1.c
	$(GCC) $(CFLAGS) -c f1.c -o $@

f2.o: f2.h f2.c
	$(GCC) $(CFLAGS) -c f2.c -o $@

clean:
	rm *.o main

 

There are three object files and we have list main.o, f1.o, and f2.o separately. This is somewhat tedious. A solution is to use symbol substitution rule in Makefile.

GCC = gcc
CFLAGS = -Wall
OBJS = main.o f1.o f2.o

main: $(OBJS)
	$(GCC) $(CFLAGS) $(OBJS) -o $@

.c.o: 
	$(GCC) $(CFLAGS) -c $*.c 

The last two lines tell make that "If you want an object file, compile a .c file of the same name."

makedepend

This Makefile is, unfortunately, wrong. If you modify f1.h and type make, you will see

make: `main' is up to date.

One solution is to use the makedepend command. Modify Makefile as follows

GCC = gcc

CFLAGS = -Wall

SRCS = main.c f1.c f2.c
OBJS = main.o f1.o f2.o

main: $(OBJS)
	$(GCC) $(CFLAGS) $(OBJS) -o $@

.c.o: 
	$(GCC) $(CFLAGS) -c $*.c 

clean:
	rm *.o main

depend:
	makedepend $(SRCS)

Type make depend and the required header files are listed in Makefile. If you modify f1.h, main.c and f1.c will be recompiled and main will be linked again.

Beyond Makefile

Every programmer has to know make, at least understands its purpose and basic principles. There are many features not covered in this exercise. Some tools can inspect your source code structure and automatically generate Makefile. New tools are being develped to replace or improve make. For example, some Java programs are built using ant.

What to Submit?

Your final Makefile.  You must submit the Makefile as an attachment, otherwise the grading program may not recognize what you've submitted.

FAQ

Q: Why do we need to learn Makefile?

A: Makefile is widely used to compile complex programs. You really don't want to type "gcc ..." so many times.

Q: Is Makefile just a quick way to compile programs?

A: Makefile detects which files have been changed and compile only these files. When you are developing a large program, you don't want to compile everything if you change only one line in a single file. Compiling a complex program (such as Linux kernel) may take hours.

Q: What are the advantages of the flags?

A: They can be applied to all files consistently.

Q: Do we need to write Makefile by hand for every program?

A: There are tools to generate Makefile, for example GNU configure. Eclipse and qmake can also create Makefile.

Q: What happens if the dependence in Makefile is wrong?

A: If there are unnecessary depenence, more files will be recompiled unnecessarily and this wastes time. If a dependence is missing, some needed files are not recompiled and the executable can be incorrect.

Q: To prevent missing dependence, can I just do "make clean" every time before "make"?

A: Yes, for small programs. For large programs, you don't want to wait for hours compiling files unnecessarily.

Q: Can Makefile be used to compile programs only?

A: Makefile can be used to handle other types files. For example, if you are writing a report that includes several files, you can update only when some files have been changed.