Making ‘make’ work

‘make’ is used to produce output files from several input files, although it is more usually used to compile programs.
This is a very short note of ‘make’ utility, mostly made for my own reference :). The best way to study is to look at makefiles! ‘Beginning Linux Programming’ by Neil Matthew and Richard Stones is a good book to start linux programming. It also mentions ‘make’ utility.
‘make’ searches for a file ‘makefile’ or ‘Makefile’. To use another filename with make use -f option.
make -f Makefile4

Makefile Syntax

Makefile is a set of Dependencies and Rules.
Dependencies: It consist of two parts – Target file and the set of file on which target depends i.e. prerequisites.
Rules: This consist of the command that must be executed to obtain the Target file from the prerequisites.
Syntax is:
Target_name:<space or tab>Prerequisite1 [Prerequisite2 …]
<tab>Command_to_Execute
Important: The Rule should be on a line that begins with TAB.
Comments begin with #
Example:
myapp: main.o 2.o 3.o
    gcc -o myapp main.o 2.o 3.o

main.o: main.c a.h
    gcc -c main.c

2.o: 2.c a.h b.h
    gcc -c 2.c

3.o: 3.c b.h c.h
    gcc -c 3.c
Here myapp is the final executable. It depends on main.o, 2.o and 3.o.
main.o in turn depends on main.c and a.h. Similarly others.
Usually a Target called ‘all’ is used (as the first target). This is handy if more than one output files are required.
By default ‘make’ tries to fulfill the first ‘Target’. So here ‘make’ tries to obtain first target ‘myapp’. To obtain ‘myapp’, it requires main.o, 2.o and 3.o. So then it tries to obtain mao=in.o and so on.
We can also specify a specific target:
make main.o

Macros in Makefile

Defined by MACRONAME=value
The value of a macro is accessed by $(MACRONAME) or ${MACRONAME}. Some also support $MACRONAME.
$(MACRONAME) is replaced with the value of the macro. ‘value’ can be empty also.
Commonly used macro names:
CC                – Compiler
INCLUDE  – Directory in which include files are present
CFLAGS     – Compiler flags/options
Example:
all: myapp
CC=gcc
CFLAGS= -g -Wall -ansi
# dot specifies current directory
INCLUDE=.

myapp: main.o 2.o
    $(CC) -o myapp main.o 2.o

main.o: main.c a.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

2.o: 2.c a.h b.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
Here $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c is expanded to gcc -I. -g -Wall -ansi -c main.c
Predefined Macros:
$?              – List of prerequisites changed more recently than the target
$@            – Name of current target
$<             – Name of current prerequisite (Example – main.c)
$*              – Name of current prerequisite without suffix (Example – main for main.c)
Two other useful characters:
– (minus sign)     – Ignore errors. For example, -mkdir ignores any error if the directory already exists.
@                   – Tells ‘make’ not to print the command to output. We may use echo to print instructions.

Other Targets

I have always wondered what is ‘make clean‘ and ‘make install‘ that we type while compiling programs from source. Its only now that I understood that ‘clean’ and ‘install’ are just other targets. So ‘make clean’ and ‘make install’ tries to achieve ‘clean’ and ‘install’ targets.
Example:
...
...
clean:
    -rm main.o 2.o 3.o

install: myapp
    @if [ -d $(INSTDIR) ]; \
    then \
        cp myapp $(INSTDIR);\
        chmod a+x $(INSTDIR)/myapp;\
        echo "Installed in $(INSTDIR)";\
    else \
        echo "Error: $(INSTDIR) does not exist";\
    fi
...
...
‘clean’ has no dependencies. So it is executed whenever the target ‘clean’ is specified. The command ‘rm’ is preceded by ‘-‘ to ignore the errors. Similarly we have used ‘@’ before ‘if’.
Note: ‘[‘ is short for command ‘test‘. It is used for checking files. For more info, ‘man test

Built-in Rules

‘make’ has several Built-in rules. You can see them by typing ‘make -p‘.
So makefile can simply be
main.o: main.c a.h
2.o: 2.c a.h b.h
The built-in rules are applied by looking at the file extensions(suffixes). We can add new suffix rules.
Syntax:
<old_suffix>.<new_suffix>:
Example for converting .cpp to .o files:
.SUFFIXES: .cpp
.cpp.o:
    $(CC) -xc++ -I($INCLUDE) $(CFLAGS) -c $< 

Libraries and ‘make’

‘make’ has built-in rules to create and append to library(archive files created with ‘ar’ command). To refer to file in the library use following syntax:
libMyCol(foo.o)
This refers to file foo.o as stored in library libMyCol.a.
Example:
...
MYLIB = libMyCol.a
...
...
myapp: main.o $(MYLIB)
    $(CC) -o myapp main.o $(MYLIB)
...
...
$(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
...
...
clean:
    -rm main.o 2.o 3.o

install: myapp
    @if [ -d $(INSTDIR) ]; \
    then \
        cp myapp $(INSTDIR);\
        chmod a+x $(INSTDIR)/myapp;\
        echo "Installed in $(INSTDIR)";\
    else \
        echo "Error: $(INSTDIR) does not exist";\
    fi
...

A Useful Option

Want to create dependency list easily? Use -MM option with gcc. It produces a list which can be almost copy-pasted for use in a makefile!
Example:
gcc -MM main.c 2.c 3.c
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s