24
2011
Create a ‘makefile’
When working with a C project we might have multiple .c files in the same project. Having to compile each and every source file after some alterations in one of them would be a painful task. Hence to avoid this , we make use of the make utility which reads its makefile and makes the building and managing of our C project lot more easier. Thus when one of the .c files is changed , we just need to run the make utility to ensure all the remaining compilations have been achieved. We will learn here a simple way to write our own makefile to manage our projects better.
According to manual pages:
The ‘make’ utility automatically determines which pieces of a large program need to be recompiled, and issue the commands to recompile them. In fact, make is not limited to programs. You can use it to describe any task where some files must be updated automatically from others whenever the others change.
Every time we issue the make command , it looks up for a file known as makefile in the current directory and carries out the action that is asked of it. Makefile describes the relationships among files in your program, and the states the commands for updating each file.
In other words the make program gets its dependency “graph” from a text file called makefile or Makefile which resides in the same directory as the source files. Make checks the modification times of the files, and whenever a file becomes “newer” than something that depends on it, (in other words, modified) it runs the compiler accordingly.
So what exactly is a dependency graph?
Imagine that we have a program where we have 3 .c files and 2 .h files. Let the 3 .c files be named hello.c , main.c and hello1.c and the 2 .h files be function.h and function1.h . Then suppose we have the dependency as follows :
hello.c depends on function.h
hello1.c depends on function1.h
main.c depends on hello.c and hello1.c
/*function.h*/ void print_hello();
/*function1.h*/ void print_hello1();
/*hello1.c*/
# include "stdio.h"
# include "function1.h"
void print_hello1()
{
printf("Hello World1");
}
/*hello.c*/
# include "stdio.h"
# include "function.h"
void print_hello()
{
printf("Hello World\n");
}
/*main.c*/
# include "stdio.h"
# include "function.h"
# include "function1.h"
void main()
{
print_hello();
print_hello1();
}
If suppose we want our executable to be called my_project then our dependency graph might look something like this :
A simple makefile for the above tree :
my_project : hello.o hello1.o main.o cc hello.o hello1.o main.o -o my_project main.o : function.h function1.h cc -c main.c hello.o : function.h hello.c cc -c hello.c hello1.o : function1.h hello1.c cc -c hello1.c
When we issue the make command in the directory where we have the .c and .h files , we get an executable file by the name of my_project. Run it as ./my_project to get the output which with the above makefile and the code snippets is :
Hello World
Hello World1
Basic Makefile Syntax is :
target : source file(s)
command (must be preceded by a tab)
A target given in the Makefile is a file which will be created or updated when any of its source files are modified. The command(s) given in the subsequent line(s) (which must be preceded by a tab character) are executed in order to create the target file.
The name given to our makefile should be Makefile or makefile. Others are not recognized. To make them recognizable we need to use make -f mymakefile where mymakefile is the name of our makefile.
In the example makefile above , if we just type make it will attempt to create or update the first target which was my_project. Suppose we just want to update the hello.o target , then we can do so by typing
make hello.o
which will carry out the command in that part of the target.
There are 3 steps to obtain the final executable file :
Compiler stage:
Converting .c files into lower-level assembly language program.
Assembler stage:
Converting the assembly language program into object codes ( .o files ) which are the code fragments that the computer understands directly.
Linker stage:
Compiling a program to produce executable files which involves linking the object code to code libraries.
The make program allows you to use macros, which are similar to variables, to store names of files.
To use the macro in our makefile it is necessary to precede the name of the macro by a dollar sign and enclose the macro name in parentheses.
For example the previous makefile can be modified as following using the macros.
mytargets = hello.o hello1.o main.o my_project : $(mytargets) cc $(mytargets) -o my_project main.o : function.h function1.h cc -c main.c hello.o : function.h hello.c cc -c hello.c hello1.o : function1.h hello1.c cc -c hello1.c
In addition to the macros we can explicitly define , there are some internally defined macros. Some of those macros of special significance are :
$@ -> this will copy the current target name.
$< -> this will copy the FIRST file name in the dependency list.
$^ -> this will copy ALL of the file names in the dependency list.
$? -> The names of all the prerequisites that are newer than the target, with spaces between them
CC – > Contains the current C compiler. Defaults to cc.
For instance :
mytargets = hello.o hello1.o main.o my_project : $(mytargets) cc $(mytargets) -o $@
will copy the target name my_project and the object file would be named my_project.
Suffix rules
Make has a set of default rules called suffix or implicit rules. These are generalized rules that make can use to build a program. For example in building a C program these rules tell make that .o object files are made from .c source files.
.c.o: $(CC) $(CFLAGS) -c $
The variable CFLAGS controls the flags given to the C compiler by the implicit rule for C compilation.
By itself, make knows already that in order to create a .o file, it must use cc -c on the corresponding .c file.
This reduces our Makefile made earlier further, as shown:
mytargets = hello.o hello1.o main.o my_project : $(mytargets) cc $(mytargets) -o my_project main.o : function.h function1.h hello.o : function.h hello1.o : function1.h
Rules for cleaning the directory
It is annoying how our directory gets stuffed with all those object files that are of absolutely no use for us. Wouldn’t it be better if we found a way to get rid of all these object files? Adding the following two lines in our makefile described earlier does the job.
clean : rm my_project *.o
where my_project was our target after compiling all the object files.
Note that just typing make does not clean the directory of the object files. We need to issue the make clean to get rid of those object files. Also it is advised to include the above rule at the end of the makefile cause we don’t want to change the motive of our makefile.
Phony Targets
A phony target is one that is not really the name of a file. It is just a name for some commands to be executed when you make an explicit request.
There are two reasons to use a phony target:
1.To avoid a conflict with a file of the same name
2. To improve performance.
Because the rm command does not create a file named ‘clean’, probably no such file will ever exist. Therefore, the rm command will be executed every time we say ‘make clean’.
However if there is a file called ‘clean’ in the same directory and because it has no dependencies, the file ‘clean’ would inevitably be considered up to date, and its commands would not be executed. To avoid this problem, we can explicitly declare the target to be phony, using the special target .PHONY.
.PHONY : clean clean : rm my_project *.o
Once this is done, ‘make clean’ will run the commands regardless of whether there is a file named ‘clean’ in the same directory.
Phony targets can have dependencies. When one directory contains multiple programs, it is most convenient to describe all of the programs in one makefile . Since the target remade by default will be the first one in the makefile, it is common to make this a phony target named ‘all’ and give it, as dependencies, all the individual programs.
all : program1 program2 program3 .PHONY : all
where program1 , program2 and program3 are 3 different program targets. We can now run program1 for instance by typing ./program1 , program2 by typing ./program2 and program3 by typing ./program3.
Note : One important thing to note down is that running the make utility always runs the first command preceded by the tab in the makefile. To run other rules we must specify them explicitly with make. Thus it is generally a good idea to include the clean rule at the end and not at the beginning of the makefile.
Finally what is ‘make’ and ‘make install’ when we are installing from a source file ( tar.gz or .gz )?
‘make’ compiles the program after configuring it. The compiled program can then be executed from its directory where it has been compiled, but this directory is not in the path. Thus it actually builds the binary, the executable program, from the source code.
‘make install’, which you have to run as root, copies all pertinent files over to their correct locations in the directory tree. For instance, it copies the executables into /usr/bin, libraries into /usr/lib and so on. We need root privileges to achieve the same.
Related Posts
Leave a comment
Fortystones Lab Projects
Categories
- Articles (43)
- Idea (2)
- Review (5)
- Social Media (29)
- Trending Topics (13)
- Collection (29)
- How To (27)
- Linux (28)
- News (15)
- PHP (6)
- Project (2)
- Tutorials (36)
- Java (4)
- Programming (10)
- Wordpress (7)
Popular Posts
- 40 Basic Linux Command-line Tips and Tricks
- Tips and Tricks for Facebook Chat (Save History/ Video Chat/ Send Files)
- 40 Linux Shell Commands for Beginners
- Creating a Simple GUI for Absolute Beginners (Java Tutorials)
- Online Coding Zones for Programmers
- Special: Facebook Smiley, Special Text Symbols and ASCII Arts
- The First on the World Wide Web

An article by Raju Khanal







