Sei sulla pagina 1di 11

How to Write a Simple Makefile

makefile is very important topic in software development. If you dont use any IDE like Eclipse, NetBeans, QT, Visual Studio ..etc, you should know how to create and edit makefiles. Even if you use an ide that create and modify makefiles automatically for you, you can be sure that one day you will need these informations. makefile is a very comprehensive topic. I tried to mentioned about only important part of the makefiles. I tried summarize the hot topics as far as i can. I started from how to generate executable and object files from source codes. After than i try to explain whats a makefile and why we need makefiles with very simple projects. I hope this tutorial will be helpful for all you. I used Ubuntu 10.10 as an operating system and gcc as a complier. From Source Code To Executable Lets assume that we have a very simple hello world c project. It has only one c file which is called helloworld.c #include <stdio.h> int main ( void ) { printf("Hello World\n"); return 0; } In a linux system if you use gcc helloworld.c command you will see that an executable a.out file is created. olcay@ubuntu:~/makefile$ ls -l total 12 -rwxr-xr-x 1 olcay olcay 7096 2011-12-18 22:12 a.out -rw-r--r-- 1 olcay olcay 68 2011-12-18 21:32 helloworld.c You can execute a.out from command line. olcay@ubuntu:~/makefile$ ./a.out 1

Hello World gcc hellworld.c ( and also gcc -o hello helloworld.c ) is a short cut from source code to executable file. But in order to generate an executable from source code first we have to generate object file. So the process order must be source code -> object file -> executable. gcc -o helloworld.c will generate object file. olcay@ubuntu:~/makefile$ ls -l total 8 -rw-r--r-- 1 olcay olcay 68 2011-12-18 21:32 helloworld.c -rw-r--r-- 1 olcay olcay 856 2011-12-18 22:24 helloworld.o But as you can see helloworld.o isnt an executable file. If you try to execute you will get this error. olcay@ubuntu:~/makefile$ chmod +x helloworld.o olcay@ubuntu:~/makefile$ ./helloworld.o bash: ./helloworld.o: cannot execute binary file We have one more step to obtain the executable file ( object file -> executable ). gcc -o hello helloworld.o will generate the executable file which is name hello. olcay@ubuntu:~/makefile$ gcc -o hello helloworld.o olcay@ubuntu:~/makefile$ ls -l total 16 -rwxr-xr-x 1 olcay olcay 7096 2011-12-18 22:30 hello -rw-r--r-- 1 olcay olcay 68 2011-12-18 21:32 helloworld.c -rw-r--r-- 1 olcay olcay 856 2011-12-18 22:30 helloworld.o olcay@ubuntu:~/makefile$ ./hello Hello World Why We Need a Makefile? First chapter was just an entry to learn how to generate executable from source code. In an actual project there will be much more source files to build. Whenever you change a source file you have to apply the same procedures again again. Thats why we need makefiles. Whats Makefile? The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them. To prepare to use make, you must write a file called the makefile that describes the relationships among files in your program and provides commands for updating each file. In a program, typically, the executable file is updated from object files, which are in turn made by compiling source files.

You need a file called a makefile to tell make what to do. Most often, the makefile tells make how to compile and link a program. Once a suitable makefile exists, each time you change some source files, this simple shell command: make When you run the the script make system will look for files with names makefile and MakeFile. If you don't have any of them you will get this error. olcay@ubuntu:~/makefile/test$ make make: *** No targets specified and no makefile found. Stop. If you have a makefile with any other names you have to run make with f options. make [ -f makefile ] Basic Rules A simple makefile consists of rules with the following shape: target ... : prerequisites ... recipe ( a tab character must be exist be before all of the recipe lines. ) ... ... A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out, such as clean. A prerequisite is a file that is used as input to create the target. A target often depends on several files. A recipe is an action that make carries out. A recipe may have more than one command, either on the same line or each on its own line. Usually a recipe is in a rule with prerequisites and serves to create a target file if any of the prerequisites change. However, the rule that specifies a recipe for the target need not have prerequisites. For example, the rule containing the delete command associated with the target clean does not have prerequisites. A rule, then, explains how and when to remake certain files which are the targets of the particular rule. make carries out the recipe on the prerequisites to create or update the target. A rule can also explain how and when to carry out an action. A Sample Makefile 3

Lets go back to the first chapter and remember the hello world example. We have just one c file and we want to generate an executable. gcc -c helloworld command will generate helloworld.o from helloworld.c. Target is helloworld.o. Prerequisite(s) is helloworld.c. Recipe is the command itself. gcc -o hello helloworld.o command will generate the executable hello from the object file helloworld.o. Here the target is hello. Prerequisite(s) is helloworld.o. Recipe is the command itself. Now its time to write our first make file. olcay@ubuntu:~/makefile/helloworld$ touch makefile olcay@ubuntu:~/makefile/helloworld$ vim makefile olcay@ubuntu:~/makefile/helloworld$ cat makefile hello : helloworld.o gcc -o hello helloworld.o helloworld.o : helloworld.c gcc -c helloworld.c olcay@ubuntu:~/makefile/helloworld$ make gcc -c helloworld.c gcc -o hello helloworld.o olcay@ubuntu:~/makefile/helloworld$ ls -l total 20 -rwxr-xr-x 1 olcay olcay 7096 2011-12-24 13:02 hello -rw-r--r-- 1 olcay olcay 68 2011-12-24 13:02 helloworld.c -rw-r--r-- 1 olcay olcay 856 2011-12-24 13:02 helloworld.o -rw-r--r-- 1 olcay olcay 99 2011-12-24 13:00 makefile olcay@ubuntu:~/makefile/helloworld$ ./hello Hello World olcay@ubuntu:~/makefile/helloworld$ Typing "make" alone should generally result in some kind of reasonable behavior. When you type "make" without specifying a target in the corresponding makefile, it will simply execute the first target in the makefile. In this example first target of the makefile is hello. When a target is a file, it needs to be recompiled or relinked if any of its dependencies change. In addition, any dependencies that are themselves automatically generated should be updated first. In this example the executable target hello is depends on helloworld.o. And object file helloworld.o is depends on helloworld.c. If we try to make the project again we will get an error. Because all of the prerequisites are up to date. olcay@ubuntu:~/makefile/helloworld$ make 4

make: `hello' is up to date. olcay@ubuntu:~/makefile/helloworld$ olcay@ubuntu:~/makefile/helloworld$ touch helloworld.o olcay@ubuntu:~/makefile/helloworld$ make gcc -o hello helloworld.o olcay@ubuntu:~/makefile/helloworld$ touch helloworld.c olcay@ubuntu:~/makefile/helloworld$ make gcc -c helloworld.c gcc -o hello helloworld.o olcay@ubuntu:~/makefile/helloworld$ If you touch an exist file only the last modified date will be changed. First we touched the helloworld.o file. Its the prerequisite of the hello. So when we ran make the recipe of the hello was executed. Second time we touched the helloworld.c. helloworld.o is depends on helloworld.c and hello is depends on helloworld.o. So when we ran make command the both the recipes of the hello and helloworld.o was executed. Instead of using touch command we can add a new target to remove all the executable and object file. This target is usually called clean. In this example new rule will be like that. clean: rm hello helloworld.o As you can see this target hasnt got any dependencies. olcay@ubuntu:~/makefile/helloworld$ make make: `hello' is up to date. olcay@ubuntu:~/makefile/helloworld$ ls -l total 20 -rwxr-xr-x 1 olcay olcay 7096 2011-12-24 13:27 hello -rw-r--r-- 1 olcay olcay 75 2011-12-24 13:27 helloworld.c -rw-r--r-- 1 olcay olcay 856 2011-12-24 13:27 helloworld.o -rw-r--r-- 1 olcay olcay 99 2011-12-24 13:26 makefile olcay@ubuntu:~/makefile/helloworld$ make clean rm hello helloworld.o olcay@ubuntu:~/makefile/helloworld$ ls -l total 8 -rw-r--r-- 1 olcay olcay 75 2011-12-24 13:27 helloworld.c -rw-r--r-- 1 olcay olcay 130 2011-12-24 13:40 makefile olcay@ubuntu:~/makefile/helloworld$ make gcc -c helloworld.c gcc -o hello helloworld.o olcay@ubuntu:~/makefile/helloworld$ We wrote our first makefile. Now we will edit that makefile and convert it to a more professional mode. 5

# this is a comment line CC=gcc # setting which compiler to use CFLAGS=-c # compiler parameters RM=rm OBJ=helloworld.o EXE=hello FILES=helloworld.c $(EXE) : $(OBJ) $(CC) -o $(EXE) $(OBJ) $(OBJ) : $(FILES) $(CC) $(CFLAGS) $(FILES) clean : $(RM) $(EXE) $(OBJ) If you replace the variables with their values you will see that we didnt change nothing in makefile. Now it is very easy to make changes in makefile. Assume that you want to change the compiler to g++. The only thing you have to do is changing the value of CC to g++, thats all. More Complex Makefiles We have a very simple calculator project. But this time it has two c files ( calculator.c and mathexpression.c ) and two header files ( calculator.h and mathexpression.h ). calculator.c #include <stdio.h> #include "mathexpression.h" #include "calculator.h" int main ( void ) { printf("%2d + %2d = %3d\n",NUMBER1,NUMBER2,addition(NUMBER1,NUMBER2)); printf("%2d * %2d = %3d\n",NUMBER1,NUMBER2,multiplication(NUMBER1,NUMBER2)); printf("%2d - %2d = %3d\n",NUMBER1,NUMBER2,abstraction(NUMBER1,NUMBER2)); printf("%2d / %2d = %3d\n",NUMBER1,NUMBER2,division(NUMBER1,NUMBER2)); return 0; } calculator.h #define NUMBER1 10 #define NUMBER2 2 6

mathexpression.c #include mathexpression.h int addition ( int number1, int number2 ) { return ( number1 + number2 ); } int multiplication( int number1, int number2 ) { return ( number1 * number2 ); } int abstraction( int number1, int number2 ) { return ( number1 - number2 ); } int division( int number1, int number2 ) { return ( number1 / number2 ); } mathexpression.h int addition ( int, int ); int multiplication( int, int ); int abstraction( int, int ); int division( int, int );

gcc -MM cfile.c command give us the dependies of cfile.o file. So we can use this command to write the makefile rules for object files calculator.o and mathexpression.o. olcay@ubuntu:~/makefile/calculator$ gcc -MM calculator.c calculator.o: calculator.c mathexpression.h calculator.h olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ gcc -MM mathexpression.c mathexpression.o: mathexpression.c mathexpression.h olcay@ubuntu:~/makefile/calculator$ Now we know the dependencies of calculator.o and mathexpression.o. At the previous chapters we learned how to generate object file from c files and how to generate executable files from object files. So we can easily write the first version of the makefile. olcay@ubuntu:~/makefile/calculator$ touch makefile olcay@ubuntu:~/makefile/calculator$ vim makefile 7

olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ cat makefile all : calculator calculator : calculator.o mathexpression.o gcc -o calculator calculator.o mathexpression.o calculator.o : calculator.c calculator.h mathexpression.h gcc -c calculator.c mathexpression.o : mathexpression.c mathexpression.h gcc -c mathexpression.c clean : rm calculator calculator.o mathexpression.o olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ make gcc -c calculator.c gcc -c mathexpression.c gcc -o calculator calculator.o mathexpression.o olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ ls -l total 36 -rwxr-xr-x 1 olcay olcay 7240 2011-12-24 15:54 calculator -rw-r--r-- 1 olcay olcay 392 2011-12-24 15:17 calculator.c -rw-r--r-- 1 olcay olcay 37 2011-12-24 12:25 calculator.h -rw-r--r-- 1 olcay olcay 1304 2011-12-24 15:54 calculator.o -rw-r--r-- 1 olcay olcay 324 2011-12-24 15:53 makefile -rw-r--r-- 1 olcay olcay 346 2011-12-24 15:45 mathexpression.c -rw-r--r-- 1 olcay olcay 114 2011-12-23 23:32 mathexpression.h -rw-r--r-- 1 olcay olcay 827 2011-12-24 15:54 mathexpression.o olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ ./calculator 10 + 2 = 12 10 * 2 = 20 10 - 2 = 8 10 / 2 = 5 olcay@ubuntu:~/makefile/calculator$ all is the first target of the makefile so its default target. The make utility will execute this target if no other one is specified. So make and make all commands do the same job. Lets change the makefile name. olcay@ubuntu:~/makefile/calculator$ mv makefile makefilev1 olcay@ubuntu:~/makefile/calculator$ make 8

make: *** No targets specified and no makefile found. Stop. olcay@ubuntu:~/makefile/calculator$ make -f makefilev1 make: Nothing to be done for `all'. olcay@ubuntu:~/makefile/calculator$ make -f makefilev1 clean rm calculator calculator.o mathexpression.o olcay@ubuntu:~/makefile/calculator$ make -f makefilev1 gcc -c calculator.c gcc -c mathexpression.c gcc -o calculator calculator.o mathexpression.o olcay@ubuntu:~/makefile/calculator$ Now makefiles name is different than makefile and Makefile. So we have to use make -f instead of make. Version 2 of the makefile is looking much more smarter. But still there are somethings to do. CC=gcc CFLAGS=-c LDFLAGS= OBJECTS=calculator.o mathexpression.o EXECUTABLE=calculator all : $(EXECUTABLE) $(EXECUTABLE) : $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $(EXECUTABLE) calculator.o : calculator.c calculator.h $(CC) $(CFLAGS) calculator.c mathexpression.o : mathexpression.c mathexpression.h $(CC) $(CFLAGS) mathexpression.c clean : rm $(EXECUTABLE) $(OBJECTS) At this project we have only one object file. What if we have hundreds of object files? Do we have to write a rule for all of them? Of course no. We will try to solve this problem at version 3 of the make file. CC=gcc CFLAGS=-c LDFLAGS= OBJECTS=calculator.o mathexpression.o SOURCES=$(OBJECTS:.o=.c) EXECUTABLE=calculator 9

all : $(EXECUTABLE) $(EXECUTABLE) : $(OBJECTS) @echo Generating Executable File $@ $(CC) $(LDFLAGS) $(OBJECTS) -o $(EXECUTABLE) .c.o: @echo Generating Object File $@ $(CC) $(CFLAGS) $< clean : @echo Cleanup rm $(EXECUTABLE) $(OBJECTS) Now we dont have to define rules for all the object files. But still we have a problem. I am sure that you can notice this error. olcay@ubuntu:~/makefile/calculator$ make -f makefilev3 Generating Object File calculator.o gcc -c calculator.c Generating Object File mathexpression.o gcc -c mathexpression.c Generating Executable File calculator gcc calculator.o mathexpression.o -o calculator olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ touch calculator.h olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ make -f makefilev3 make: Nothing to be done for `all'. olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ touch calculator.c olcay@ubuntu:~/makefile/calculator$ olcay@ubuntu:~/makefile/calculator$ make -f makefilev3 Generating Object File calculator.o gcc -c calculator.c Generating Executable File calculator gcc calculator.o mathexpression.o -o calculator olcay@ubuntu:~/makefile/calculator$ Make file utility couldnt detect the differences at calculator.h. Because there is nothing to indicate this dependence. So you should be so careful while writing makefiles. You shouldnt forget the dependencies. Touching the file is a good method for checking dependencies.

10

References
1. 2. "GNU `make'". Free Software Foundation. "Manual Pages: make". OpenBSD 4.8.

Writer
Olcay Ay Software Design Engineer

About GTUG Ankara Who are we? We are an Ankara-wide community interested in technology and willing to develop and share information. What is our interest in Google? Google corporation devoted few employees to form a group aimed to discuss Google Technologies; currently website and mailing list is in use. Google requires these technology groups to make organizations and discuss Google Technologies. In addition, Google may hold events at the location of group depending on member size. We formed Ankara branch of these Google Technology User Groups. What we plan to do? Our aim is, to organize series of events that spreads current technologies, make participants communicate with each other effectively and produce something with small workgroups. Furthermore information visit our web site.

11

Potrebbero piacerti anche