The make command in Linux is one of the most frequently used commands by system administrators and programmers. While it helps administrators in compiling and installing many open source utilities through the command line, programmers use it to manage the compilation of their large and complicated projects.
In this tutorial, we will discuss the internal working of the make command along with some practical examples.
How make Command Works
For those who are unaware, the make command accepts targets as command line arguments. These targets are usually specified in a file named 'Makefile', which also contains the associated action corresponding to the targets. For more information, read our series of articles on how Makefiles work.
When the make command is executed for the very first time, it scans the Makefile to find the target (supplied to it) and then reads its dependencies. If these dependencies are targets themselves, it scans the Makefile for these targets and builds their dependencies (if any), and then builds them. Once the main dependencies are build, it then builds the main target (that was passed to the make command).
Now, suppose you make change to only one source file and you execute the make command again, it will only compile the object files corresponding to that source file, and hence will save a lot of time in compiling the final executable.
make Command Examples
Here are the details of the testing environment used for this tutorial:
- OS – Ubuntu
- Shell – Bash
- Application – GNU Make
Following are the contents of the project :
$ ls anotherTest.c Makefile test.c test.h
and here are the contents of the Makefile :
all: test test: test.o anotherTest.o gcc -Wall test.o anotherTest.o -o test test.o: test.c gcc -c -Wall test.c anotherTest.o: anotherTest.c gcc -c -Wall anotherTest.c clean: rm -rf *.o test
Now, let's have a look at some of the examples of make command usage in Linux.
1. A simple example
To compile the project, you can either simply use 'make' or can use the target 'all' with the make command.
$ make gcc -c -Wall test.c gcc -c -Wall anotherTest.c gcc -Wall test.o anotherTest.o -o test
So you can see that the make command first creates the dependencies and then the actual target.
If you take a look at the directory contents, there would be .o files and executable files in it :
$ ls anotherTest.c anotherTest.o Makefile test test.c test.h test.o
Now, suppose you make some change in test.c and re-compile the project with make :
$ make gcc -c -Wall test.c gcc -Wall test.o anotherTest.o -o test
So you can see that only test.o is recompiled, while anotherTest.o is not recompiled.
Now, to clean all the object files along with the 'test' executable, you can use the target 'clean' :
$ make clean rm -rf *.o test $ ls anotherTest.c Makefile test.c test.h
So you can see that all the .o files and the executable 'test' were deleted.
2. Always make all the targets through -B option
By now, you'd probably be aware that the make command does not compile those files that have not changed since last time. But, if you want to override the make's default behavior, you can use the -B option.
Here is an example :
$ make make: Nothing to be done for `all'. $ make -B gcc -c -Wall test.c gcc -c -Wall anotherTest.c gcc -Wall test.o anotherTest.o -o test
So you can see that while 'make' command did not compile any file, 'make -B' forcibly compiled all the objects along with the final executable.
3. Print debugging information using -d option
If you want a detailed account of what the make command actually does when it is executed, use the -d option.
Here is an example :
$ make -d | more GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program built for x86_64-pc-linux-gnu Reading makefiles... Reading makefile `Makefile'... Updating makefiles.... Considering target file `Makefile'. Looking for an implicit rule for `Makefile'. Trying pattern rule with stem `Makefile'. Trying implicit prerequisite `Makefile.o'. Trying pattern rule with stem `Makefile'. Trying implicit prerequisite `Makefile.c'. Trying pattern rule with stem `Makefile'. Trying implicit prerequisite `Makefile.cc'. Trying pattern rule with stem `Makefile'. Trying implicit prerequisite `Makefile.C'. Trying pattern rule with stem `Makefile'. Trying implicit prerequisite `Makefile.cpp'. Trying pattern rule with stem `Makefile'. --More--
It's a long output. You can see that I used the more command to view the output page by page. You have to see it for yourself to get a better idea of the details this option is capable to produce.
4. Change the directory using -C option
You can provide a different directory path to the make command, which it would switch to before looking for a Makefile.
Here is an example. Suppose you are currently in this directory :
$ ls file file2 frnd frnd1.cpp log1.txt log3.txt log5.txt file1 file name with spaces frnd1 frnd.cpp log2.txt log4.txt
But you want to run make with the Makefile kept at ../make-dir/. So here is what you've to do :
$ make -C ../make-dir/ make: Entering directory `/home/himanshu/practice/make-dir' make: Nothing to be done for `all'. make: Leaving directory `/home/himanshu/practice/make-dir'
So you can see that the make command switched to the specified directory, executed there and then switched back.
5. Treat any other file as 'Makefile' through -f option
If you want to rename your Makefile to let's say 'my_makefile' or any other name, and want the make command to treat it like the default 'Makefile', you can use the -f option for this.
Here is an example :
make -f my_makefile
This way the make command will pick and scan my_makefile instead of Makefile.