Unix Makefile


A file that instructs the program make how to compile and link a program.

The default name of the makefile is literally Makefile, but the name can be specified with a command-line option. 

The make program aids you in developing your large programs by keeping track of which portions of the entire program have been changed, compiling only those parts of the program which have changed since the last compile.


0. About Compilation Stages

Compiling a small C program requires at least a single .c file, with .h files as appropriate. Although the command to perform this task is simply cc file.c, there are 3 steps to obtain the final executable program, as shown:

  1. Compiler Stage: All C language code in the .c file is converted into a lower-level language called Assembly language; making .s files.

  2. Assembler Stage: The assembly language code made by the previous stage is then converted into object code which are fragments of code which the computer understands directly. An object code file ends with .o.

  3. Linker Stage: The final stage in compiling a program involves linking the object code to code libraries which contain certain "built-in" functions, such as printf. This stage produces an executable program, which is named a.out by default.



1. Why do we need a Makefile

For this session assume you have following source files.

Content of main.cpp
	   #include <iostream.h>

	   #include "functions.h"

	   int main(){
	   print_hello();
	   cout << endl;
	   cout << "The factorial of 5 is " << factorial(5) << endl;
	   return 0;
	   }
             

Content of hello.cpp
	   #include <iostream.h>

	   #include "functions.h"

	   void print_hello(){
	   cout << "Hello World!";
		   }
		   

Content of factorial.cpp
	   #include "functions.h"

	   int factorial(int n){
	   if(n!=1){
	   return(n * factorial(n-1));
	   }
	   else return 1;
	   }
	 

Conetnt of functions.h
	   void print_hello();
	   int factorial(int n);
	 

The trivial way to compile the files and obtain an executable, is by running the command:

	   CC  main.cpp hello.cpp factorial.cpp -o hello
       

This above command will generate hello binary. In our example we have only four files and we know the sequence of the function calls so it may be feasible to write the above command by hand and prepare a final binary. But for the large project where we will have thausands of source code files, it becomes difficult to maintain the binary builds.

The make command allows you to manage large programs or groups of programs. As you begin to write larger programs, you will notice that re-compiling larger programs takes much longer than re-compiling short programs. Moreover, you notice that you usually only work on a small section of the program (such as a single function that you are debugging), and much of the rest of the program remains unchanged.

In subsequent sections we will see how to prepare a makefile for our project.


2. Makefile Macros

The make program allows you to use macros, which are similar to variables. Macros are defined in a Makefile as = pairs. For example:

	   MACROS=  -me
	   PSROFF=  groff -Tps
	   DITROFF= groff -Tdvi
	   CFLAGS= -O -systype bsd43
	   LIBS = "-lncurses -lm -lsdl"
	   MYFACE = ":*)"
       

Before issuing any command in a target rule set there are certain special macros predefined.

So, for example, we could use a rule

	   hello: main.cpp hello.cpp factorial.cpp
	   $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@

	   alternatively:

	   hello: main.cpp hello.cpp factorial.cpp
           $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@
       

In this example $@ represents hello and $? or $@.cpp will pickup all the changed source files.

There are two more special macros used in the implicit rules. They are

Common implicit rule is for the construction of .o (object) files out of .cpp (source files).
	   .o.cpp:
           $(CC) $(CFLAGS) -c $<

	   alternatively

	   .o.cpp:
	   $(CC) $(CFLAGS) -c $*.c
				  

NOTE: You will get more detail about Makefile Rules in another section.

There are lots of default macros (type "make -p" to print out the defaults). Most are pretty obvious from the rules in which they are used:

These predefined variables ie. macros used in implicit rules fall into two classes: those that are names of programs (like CC) and those that contain arguments for the programs (like CFLAGS).

Here is a table of some of the more common variables used as names of programs in built-in rules: makefiles.

AR Archive-maintaining program; default `ar'.
AS Program for compiling assembly files; default `as'.
CC Program for compiling C programs; default `cc'.
CO Program for checking out files from RCS; default `co'.
CXX Program for compiling C++ programs; default `g++'.
CPP Program for running the C preprocessor, with results to standard output; default `$(CC) -E'.
FC Program for compiling or preprocessing Fortran and Ratfor programs; default `f77'.
GET Program for extracting a file from SCCS; default `get'.
LEX Program to use to turn Lex grammars into source code; default `lex'.
YACC Program to use to turn Yacc grammars into source code; default `yacc'.
LINT Program to use to run lint on source code; default `lint'.
M2C Program to use to compile Modula-2 source code; default `m2c'.
PC Program for compiling Pascal programs; default `pc'.
MAKEINFO Program to convert a Texinfo source file into an Info file; default `makeinfo'.
TEX Program to make TeX dvi files from TeX source; default `tex'.
TEXI2DVI Program to make TeX dvi files from Texinfo source; default `texi2dvi'.
WEAVE Program to translate Web into TeX; default `weave'.
CWEAVE Program to translate C Web into TeX; default `cweave'.
TANGLE Program to translate Web into Pascal; default `tangle'.
CTANGLE Program to translate C Web into C; default `ctangle'.
RM Command to remove a file; default `rm -f'.

Here is a table of variables whose values are additional arguments for the programs above. The default values for all of these is the empty string, unless otherwise noted.

ARFLAGSFlags to give the archive-maintaining program; default `rv'.
ASFLAGSExtra flags to give to the assembler (when explicitly invoked on a `.s' or `.S' file).
CFLAGSExtra flags to give to the C compiler.
CXXFLAGSExtra flags to give to the C compiler.
COFLAGSExtra flags to give to the RCS co program.
CPPFLAGSExtra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers).
FFLAGSExtra flags to give to the Fortran compiler.
GFLAGSExtra flags to give to the SCCS get program.
LDFLAGSExtra flags to give to compilers when they are supposed to invoke the linker, `ld'.
LFLAGSExtra flags to give to Lex.
YFLAGSExtra flags to give to Yacc.
PFLAGSExtra flags to give to the Pascal compiler.
RFLAGSExtra flags to give to the Fortran compiler for Ratfor programs.
LINTFLAGSExtra flags to give to lint.

NOTE: You can cancel all variables used by implicit rules with the `-R' or `--no-builtin-variables' option.


You can also define macros at the command line such as
           make CPP = /home/courses/cop4530/spring02
	 


3. Defining Dependencies in Makefile

It is very common that a final binary will be dependent on various source code and source header files. Dependencies are important because they tell to make about the source for any target. Consider the following example

	   hello: main.o factorial.o hello.o
	   $(CC) main.o factorial.o hello.o -o hello
       

Here we are telling to make that hello is dependent on main.o, factorial.o and hello.o so whenever there is a change in any of these object files then make will take action.

Same time we would have to tell to make how to prepare .o files so we would have to define those dependencies also as follows
	   main.o: main.cpp functions.h
	   $(CC) -c main.cpp

	   factorial.o: factorial.cpp functions.h
	   $(CC) -c factorial.cpp

	   hello.o: hello.cpp functions.h
	   $(CC) -c hello.cpp
	 


4. Defining Rules in Makefile

The general syntax of a Makefile Target Rule is

	   target [target...] : [dependent ....]
	   [ command ...]
       

Items in brackets are optional, ellipsis means one or more. Note the tab to preface each command is required.

A simple example is given below where you define a rule to make your target hello from three other files.

	   hello: main.o factorial.o hello.o
	   $(CC) main.o factorial.o hello.o -o hello
       

NOTE: In this example you would have to give rules to make all object files also from the source files

The semantics is pretty simple. When you say "make target" make finds the target rule that applies and, if any of the dependents are newer than the target, make executes the commands one at a time (after macro substitution). If any dependents have to be made, that happens first (so you have a recursion).

A make will terminate if any command returns a failure status. That's why you see rules like:

	   clean:
           -rm *.o *~ core paper

       

Make ignores the returned status on command lines that begin with a dash. eg. who cares if there is no core file?

Make will echo the commands, after macro substition to show you what's happening as it happens. Sometimes you might want to turn that off. For example:

	   install:
           @echo You must be root to install
       

People have come to expect certain targets in Makefiles. You should always browse first, but it's reasonable to expect that the targets all (or just make), install, and clean will be found.


Makefile Implicit Rules

The command is one that ought to work in all cases where we build an executable x out of the source code x.cpp This can be stated as an implicit rule:

	   .cpp:
           $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@
       

This Implicit rule says how to make x out of x.c -- run cc on x.c and call the output x. The rule is implicit because no particular target is mentioned. It can be used in all cases.

Another common implicit rule is for the construction of .o (object) files out of .cpp (source files).

	   .o.cpp:
           $(CC) $(CFLAGS) -c $<
				

	   alternatively

	   .o.cpp:
	   $(CC) $(CFLAGS) -c $*.cpp
       

5. Defining Custom Suffix Rules in Makefile

By itself, make knows already that in order to create a .o file, it must use cc -c on the corresponding .c file. These rules are built into make, and you can take advantage of this to shorten your Makefile. If you just indicate just the .h files in the dependency line of the Makefile that the current target is dependent on, make will know that the corresponding .c file is already required. You don't even need to include the command for the compiler.

This reduces our Makefile further, as shown:
	   OBJECTS = main.o hello.o factorial.o
	   hello: $(OBJECTS)
           cc $(OBJECTS) -o hello
	   hellp.o: functions.h
	   main.o: functions.h 
	   factorial.o: functions.h 
	 

Make uses a special target, named .SUFFIXES to allow you to define your own suffixes. For example, the dependency line:
	   .SUFFIXES: .foo .bar
tells make that you will be using these special suffixes to make your own rules.

Similar to how make already knows how to make a .o file from a .c file, you can define rules in the following manner:
	   .foo.bar:
           tr '[A-Z][a-z]' '[N-Z][A-M][n-z][a-m]' < $< > $@
           .c.o:
           $(CC) $(CFLAGS) -c $<

The first rule allows you to create a .bar file from a .foo file. (Don't worry about what it does, it basically scrambles the file.) The second rule is the default rule used by make to create a .o file from a .c file.


6. Makefile Directives

There are numerious directives available in different forms. You make program may not support all the directives. So please check if your make supports the directives we are explaining here. GNU make supports these directives

Conditional Directives

There are conditional directives

Syntax of Conditionals Directives

The syntax of a simple conditional with no else is as follows:,/p>
conditional-directive
text-if-true
endif

The text-if-true may be any lines of text, to be considered as part of the makefile if the condition is true. If the condition is false, no text is used instead.

The syntax of a complex conditional is as follows:

conditional-directive
text-if-true
else
text-if-false
endif

If the condition is true, text-if-true is used; otherwise, text-if-false is used instead. The text-if-false can be any number of lines of text.

The syntax of the conditional-directive is the same whether the conditional is simple or complex. There are four different directives that test different conditions. Here is a table of them:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2" 

Opposite directives of the above conditions are are follows

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2" 

Example of Conditionals Directives

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

The include directive

The include directive tells make to suspend reading the current makefile and read one or more other makefiles before continuing. The directive is a line in the makefile that looks like this:
include filenames...

filenames can contain shell file name patterns.Extra spaces are allowed and ignored at the beginning of the line, but a tab is not allowed.For example, if you have three `.mk' files, `a.mk', `b.mk', and `c.mk', and $(bar) expands to bish bash, then the following expression.

include foo *.mk $(bar)

is equivalent to

include foo a.mk b.mk c.mk bish bash

When make processes an include directive, it suspends reading of the containing makefile and reads from each listed file in turn. When that is finished, make resumes reading the makefile in which the directive appears.

The override Directive

If a variable has been set with a command argument then ordinary assignments in the makefile are ignored. If you want to set the variable in the makefile even though it was set with a command argument, you can use an override directive, which is a line that looks like this:

override variable = value

or

override variable := value

7. Makefile Recompilation

The make program is an intelligent utility and works based on the changes you do in your source files. If you have four files main.cpp, hello.cpp, factorial.cpp and functions.h. Here all the reamining files are dependent on functions.h and main.cpp is dependent on hello.cpp and factorical.cpp. So if you make any change in functions.h then make will recompile all the source files to generate new object files. But if you make any change main.cpp, as this is not dependent of any other fil, then in this case only main.cpp file will be recompiled and hellp.cpp and factorial.cpp will not be recompiled.

While compiling a file, make checks its object file and comprare the time staps, if source file has newer time stamp than object file then it will generate new object file assuimg that source file has been changed.

Avoiding Recompilation

There may be a project consisted of thausands of files. Sometimes you may have changed a source file but you do not want to recompile all the files that depend on it. For example, suppose you add a macro or a declaration to a header file that many other files depend on. Being conservative, make assumes that any change in the header file requires recompilation of all dependent files, but you know that they do not need to be recompiled and you would rather not waste the time waiting for them to compile.

If you anticipate the problem before changing the header file, you can use the `-t' flag. This flag tells make not to run the commands in the rules, but rather to mark the target up to date by changing its last-modification date. You would follow this procedure:

  1. Use the command `make' to recompile the source files that really need recompilation.

  2. Make the changes in the header files.

  3. Use the command `make -t' to mark all the object files as up to date. The next time you run make, the changes in the header files will not cause any recompilation.

If you have already changed the header file at a time when some files do need recompilation, it is too late to do this. Instead, you can use the `-o file' flag, which marks a specified file as "old". This means that the file itself will not be remade, and nothing else will be remade on its account. Follow this procedure:

  1. Recompile the source files that need compilation for reasons independent of the particular header file, with `make -o headerfile'. If several header files are involved, use a separate `-o' option for each header file.

  2. Touch all the object files with `make -t'.


8. Makefile's Other Features

Recursive Use of make

Recursive use of make means using make as a command in a makefile. This technique is useful when you want separate makefiles for various subsystems that compose a larger system. For example, suppose you have a subdirectory `subdir' which has its own makefile, and you would like the containing directory's makefile to run make on the subdirectory. You can do it by writing this:
           subsystem:
                      cd subdir && $(MAKE)

           or, equivalently
 	
           subsystem:
                      $(MAKE) -C subdir

You can write recursive make commands just by copying this example, but there are many things to know about how they work and why, and about how the sub-make relates to the top-level make

Communicating Variables to a Sub-make

Variable values of the top-level make can be passed to the sub-make through the environment by explicit request. These variables are defined in the sub-make as defaults, but do not override what is specified in the makefile used by the sub-make makefile unless you use the `-e' switch

To pass down, or export, a variable, make adds the variable and its value to the environment for running each command. The sub-make, in turn, uses the environment to initialize its table of variable values

The special variables SHELL and MAKEFLAGS are always exported (unless you unexport them). MAKEFILES is exported if you set it to anything.

If you want to export specific variables to a sub-make, use the export directive, like this:

export variable ...

If you want to prevent a variable from being exported, use the unexport directive, like this:

unexport variable ...

The Variable MAKEFILES

If the environment variable MAKEFILES is defined, make considers its value as a list of names (separated by whitespace) of additional makefiles to be read before the others. This works much like the include directive: various directories are searched for those files.

The main use of MAKEFILES is in communication between recursive invocations of make

Header file inclusion from different directories

If you have put your header files in different directories and you arerunning make in different directory then it is required to tell the path of header files. This can be done using -I option in makefile. Assuming that functions.h file is available in /home/tutorialspoint/header and rest of the files are available in /home/tutorialspoint/src/ then make file would be written as follows.
INCLUDES = -I "/home/tutorialspoint/header"
CC = gcc
LIBS =  -lm
CFLAGS = -g -Wall
OBJ =  main.o factorial.o hello.o

hello: ${OBJ}
   ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
.cpp.o:
   ${CC} ${CFLAGS} ${INCLUDES} -c $<


Appending More Text to Variables

Often it is useful to add more text to the value of a variable already defined. You do this with a line containing `+=', like this:

objects += another.o

This takes the value of the variable objects, and adds the text `another.o' to it (preceded by a single space). Thus:

objects = main.o hello.o factorial.o
objects += another.o

sets objects to `main.o hello.o factorial.o another.o'.

Using `+=' is similar to:

objects = main.o hello.o factorial.o
objects := $(objects) another.o

Continution Line in Makefile

If you don't like too big lines in your Makefile then you can break your line using a back-slash "\" as shown below

OBJ =  main.o factorial.o \
	hello.o

is equivalent to

OBJ =  main.o factorial.o hello.o

Running Makefile from command prompt

If you have prepared yur Makefile with a name as "Makefile" then simply write make at command prompt and it will run your Makefile file. But if you have given any other name to your Makefile then use the following command

make -f your-makefile-name


9. Makefile Example

This is an example of the Makefile for compiling the hello program. This program consists of three files main.cpp, factorial.cpp and hello.cpp.

# Define required macros here
SHELL = /bin/sh

OBJ =  main.o factorial.o hello.o
CFLAG = -Wall -g
CC = gcc
INCLUDE =
LIB = -lm

hello:${OBJ}
   ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}

clean:
	-rm -f *.o core *.core

.cpp.o:
   ${CC} ${CFLAGS} ${INCLUDES} -c $<

Now you can build your program hello using "make". If you will issue a command "make clean" then it will remove all the object files and core files available in the current directory.


Mission Complete!!