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 bemain.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