Monday, June 30, 2014

How a Makefile Works

Make is a utility that automatically builds executable programs and libraries from source code by reading files called makefiles which specify how to derive the target program.

Besides building programs, Make can be used to manage any project where some files must be updated automatically from others whenever the others change.

The example below shows how to use a Makefile to automatically compile a C source code file "example.c" (only when it has been changed) and then execute the compiled file "./example".

./example.c

Create the source file.

#include
int main()
{
 printf ("Hello\n");
 return 0;
}


./Makefile

Create the Makefile.

exec: compile
 ./example

compile: \
example.c
 gcc example.c -o example
 echo "compiling..."
 touch compile


Makefile Notes

Make assumes that Makefile targets are files in the same directory in which the make command is executed.

If the target file is already there, and its dependencies didn't change, nothing will be done.

"compile" is a dependency of the exec target. "compile" will be executed prior to executing the exec command "./example"

"example.c" is a dependency of the compile target. (It could be on the same line as "compile", but we used the line continuation character "backslash" to put in on a separate line.)

"compile" will be executed only if "example.c" has been modified.

The touch command creates a "compile" target file (if it does not already exist) and effectively updates its last updated timestamp.

Make compares the timestamp of example.c to compile. If example.c has been modified after compile, it will run the "compile" target.

Preface each line of a target body with a single TAB character. (There is a TAB character in front of "./example" in the "exec" target.)

This is a lot of work for managing one source file, but is a huge time saver when there are many source files in a project. Make will only compile the files that have been changed since the last time the compile target has been executed.

Results


$ ls -lrt
total 16
-rw-r--r--  1 lex  staff   64 Jun 30 12:34 example.c
-rw-r--r--  1 lex  staff  108 Jun 30 12:35 Makefile


There are only two files in our directory now.

Run make command


$ make
gcc example.c -o example
echo "compiling..."
compiling...
touch compile
./example
Hello


Results


$ ls -lrt
total 40
-rw-r--r--  1 lex  staff    64 Jun 30 12:34 example.c
-rw-r--r--  1 lex  staff   108 Jun 30 12:35 Makefile
-rwxr-xr-x  1 lex  staff  8496 Jun 30 12:35 example
-rw-r--r--  1 lex  staff     0 Jun 30 12:35 compile


Now, there are 4 files.

Run the newly created executable


$ ./example
Hello


./example.c

Add ", World!" to the output.

#include
int main()
{
 printf ("Hello, World!\n");
 return 0;
}



$ ls -lrt
total 40
-rw-r--r--  1 lex  staff   108 Jun 30 12:35 Makefile
-rwxr-xr-x  1 lex  staff  8496 Jun 30 12:35 example
-rw-r--r--  1 lex  staff     0 Jun 30 12:35 compile
-rw-r--r--  1 lex  staff    72 Jun 30 12:38 example.c


Now, "example.c" has a more recent timestamp than the "compile" file.


$ make
gcc example.c -o example
echo "compiling..."
compiling...
touch compile
./example
Hello, World!


Summary

  • Make ran the "compile" target, because it was the first target listed in the Makefile
  • Make ran the "compile" target, because it was a dependency of the "exec" target
  • Make compared the compare file's timestamp to its dependency "example.c" and ran it because example.c has a more recent timestamp.


More Complicated Scenarios

This has been a very simple, contrived example to show how a Makefile works.

In order to handle more source files, make allows you to use macros, wildcard pattern matching and string substitution features to automatically include files without modifying your Makefile.

References

http://en.wikipedia.org/wiki/Make_(software)
http://www.gnu.org/software/make/manual/make.html

This work is licensed under the Creative Commons Attribution 3.0 Unported License.

No comments:

Post a Comment