Make
Make
Spring 2024
Introduction to make
Abhijit Das
Pralay Mitra
Why make at all?
• These commands can be written in a shell script and executed to get the final product.
• For this small example, this is fine.
• If the source consists of thousands of files, compiling all of these is a slow process.
• Not all modules need recompilation after every change.
• If one makes (small/large) changes only in queue.c and/or queue.h, there is no need to
recompile stack.c.
• Make helps you in the selective (re)compilation process.
• But you must instruct how to do it.
Example: The dependencies
stack.c stack.h defs.h queue.h queue.c
stack.o queue.o
libstaque.a
• The dependency and compilation instructions are written in a file. The following names are
searched in the given order.
• GNUmakefile
• makefile
• Makefile
• make checks timestamps to decide which parts of the project need to be recompiled.
• The commands are executed if one or more dependency file(s) is/are modified after the target
was last built.
• Phony targets are always built.
Rule examples
library: stack.o queue.o
ar rcs libstaque.a stack.o queue.o
• library is a phony target that depends on stack.o and queue.o. The build involves invoking the
ar command.
• stack.o (a filename target) depends on the header files defs.h and stack.h.
• queue.o (another filename target) depends on the header files defs.h and queue.h.
• What make already knows is this:
• stack.o also depends on stack.c, and queue.o also depends on queue.c. There is no need to
specify these dependencies.
• stack.o can be obtained from stack.c and queue.o from queue.c by invoking gcc –c. It is not
needed to write the commands explicitly.
• What make does not know is what additional compilation flags you need with gcc –c.
Rule examples (continued)
• If you run make without any target name, the target of the first rule is built. For example, if
library is the first rule in our example, it is built if make is called without an explicit target name.
• You can specify the target additionally like:
make stack.o
or
make queue.o
Make variables
• Variables can be set using the assignment operator = (recursive) or := (evaluate once).
• a variable VAR can be accessed as $(VAR) or $–VAR˝.
• Default variables
• SHELL specifies which shell to use for running the commands.
• CC specifies the C compiler you want to use.
• CFLAGS stands for the additional compilation flags that you use during gcc –c.
SHELL = /bin/sh
CC = gcc
CFLAGS = -O2 -g -I.
AR = ar
LIBNAME = libstaque.a
OBJFILES = stack.o queue.o
library: $(OBJFILES)
$(AR) rcs $(LIBNAME) $(OBJFILES)
$(OBJFILES): defs.h
stack.o: stack.h
queue.o: queue.h
Run make
$ make
gcc -O2 -g -I. -c -o stack.o stack.c
gcc -O2 -g -I. -c -o queue.o queue.c
ar rcs libstaque.a stack.o queue.o
$ make
ar rcs libstaque.a stack.o queue.o
$ touch defs.h
$ make
gcc -O2 -g -I. -c -o stack.o stack.c
gcc -O2 -g -I. -c -o queue.o queue.c
ar rcs libstaque.a stack.o queue.o
$ touch queue.c
$ make
gcc -O2 -g -I. -c -o queue.o queue.c
ar rcs libstaque.a stack.o queue.o
$
Rules to install
• Often the final products (like libstaque.a and the header files) need to be installed in the system
area.
• Run make in the superuser mode as:
sudo make install
LIBDIR = /usr/local/lib
INCLUDEDIR = /usr/include
INCLUDESUBDIR = $(INCLUDEDIR)/staque
install: library
cp $(LIBNAME) $(LIBDIR)
-mkdir $(INCLUDESUBDIR)
cp defs.h stack.h queue.h $(INCLUDESUBDIR)
cp staque.h $(INCLUDEDIR)
• A dash before a command directs make to ignore errors. Here, if the directory
/usr/include/staque already exists, mkdir fails. But make moves forward ignoring the error.
Run make install
RM = rm -f
clean:
-$(RM) $(OBJFILES)
distclean:
-$(RM) $(OBJFILES) $(LIBNAME)
Difference between = and :=
makefile
SHELL = /bin/bash
st:
@echo Hi $(ST)
Writing makefile in pieces
Syntax
include file1 file2 file3 ...
Example
STARTMKF = defs.mk primitives.mk
include preamble.mk $(STARTMKF) util*.mk
• Suppose that there are four matches util1.mk, util2.mk, util3.mk, utilfinal.mk.
• The following seven files are included:
• preamble.mk
• defs.mk
• primitives.mk
• util1.mk
• util2.mk
• util3.mk
• utilfinal.mk
Recursive make
SHELL = /bin/sh
all:
cd static; make
cd shared; make
install:
cd static; make install
cd shared; make install
clean:
cd static; make clean
cd shared; make clean
Run recursive make
$ make
cd static; make
make[1]: Entering directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/static’
gcc -O2 -g -I. -c -o stack.o stack.c
gcc -O2 -g -I. -c -o queue.o queue.c
ar rcs libstaque.a stack.o queue.o
make[1]: Leaving directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/static’
cd shared; make
make[1]: Entering directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/shared’
gcc -O2 -g -fPIC -I. -c -o stack.o stack.c
gcc -O2 -g -fPIC -I. -c -o queue.o queue.c
gcc -shared -o libstaque.so stack.o queue.o
make[1]: Leaving directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/shared’
$ make clean
cd static; make clean
make[1]: Entering directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/static’
rm -f stack.o queue.o
make[1]: Leaving directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/static’
cd shared; make clean
make[1]: Entering directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/shared’
rm -f stack.o queue.o
make[1]: Leaving directory ’/home/abhij/IITKGP/course/lab/SPL/Spring23/prog/libstaque/shared’
$
Practice exercises
1. Some (but not all) targets in a software project needs rebuilding. You do not want to rebuild these targets actually.
Instead you only want to know what commands will be executed for updating the targets. Figure out how you can run
make to do this.
2. Investigate how you can force make to rebuild a target and all its dependencies even if one or more of these targets
are up-to-date.
3. Investigate the behavior of make in the presence of circular dependencies as in the following makefile.
a: b c
@echo "Building a"
b: c a
@echo "Building b"
c: a b
@echo "Building c"
4. Consider the following makefile containing an error (the command ecko). You run make, make -k, and make -i.
What differences do you see? Explain.
a: b c
@echo "Building a"
b:
@ecko "Building b"
c:
@echo "Building c"
Practice exercises
makefile1 makefile2
A = $(B) A := $(B)
B = "Hello" B = "Hello"
all: all:
@echo $(A) @echo $(A)
What difference(s) do make -f makefile1 and make -f makefile2 exhibit? Explain why.
6. [Target-specific variables] Consider the following makefile.
A = $(B)
one:
@echo $(A) $(B)
two:
@echo $(A) $(B)
You want make one to print abra abra, and make two to print cadabra cadabra. You are not allowed to change
the above five lines. Explain how you can add some extra lines to this makefile in order to achieve your goal.
Practice exercises
7. A software project builds a library libfoobar.a by compiling foo.c and bar.c. The file foo.c includes foo.h,
whereas the file bar.c includes bar.h. The header file foo.h in turn includes foobar.h, foo1.h, and foo2.h,
whereas the header file bar.h includes foobar.h, bar1.h, bar2.h, and bar2.h. All the files reside in the same
directory. Write a makefile to build the library. You do not have to actually write any .c or .h file
8. Suppose that you want to create a dynamic library of functions for working with rational numbers of the form a/b with
a any integer and with b a positive integer. To do this, you declare the rational data type (and nothing else) in a
header file rat.h. Then, you write three C files along with three corresponding header files.
rbasic.c (and rbasic.h): This defines a gcd() function and another function rconv() that converts a rational
number a/b to the lowest terms satisfying gcd(a, b) = 1.
rarith.c (and rarith.h): This defines basic arithmetic functions on rational numbers: radd(), rsub(), rmul(),
and rdiv().
rmath.c (and rmath.h): This defines the functions r2dbl() [convert a/b to a double], rsqrt() [double-valued
square root of a rational number], and rlog() [double-valued log of a rational number]. This file uses the math
library available in a standard system directory as libm.a.
All the files related to rational numbers reside in one directory. Write a makefile in the directory to generate the
dynamic library librational.so. You do not have to actually write any .c or .h file.
Practice exercises
9. You have a project whose files are stored in the following directory hierarchy. The root of the project is the directory
/home/foobar/project, which has two subdirectories basics and utilities. The utilities directory further
has two subdirectories online and offline. Each directory (including the project root) has a makefile which can be
used independently of one another. But to build the project, all the makefiles in all the directories must be used. Show
how you can build the project by executing a single make command from the command prompt (write the makefile
this make command will execute).
10. You need to build two separate libraries libfoo.a and libbar.a. The foo files reside in a subdirectory foo, and
the bar files reside in a subdirectory bar. Each subdirectory has its independent makefile to build the corresponding
library in the respective directory. The foo library is independent, but the compilation of the bar library uses the flags
-L../foo and -lfoo to use the functions in libfoo.a. The source files of the bar directory include appropriate
header files from the foo directory. But the makefile of the bar directory does not specify dependencies on any of the
foo files. So you need to build libfoo.a first and then libbar.a. Write a makefile to prepare the two libraries using
a single make command.
(a) How do you compile an application program in the current directory, that uses only the bar library?
(b) Suppose that the sources of the foo library are changed. If you run make from the current directory, will it rebuild
libbar.a? What if you recompile the application program after this make?
11. Repeat the last exercise with the exception that the dynamic libraries libfoo.so and libbar.so are to be built
(instead of the static ones).