## $Id: make.html,v 1.5 1997/07/08 16:35:59 libsq Exp $
## Dan Ellard -- 11/15/94

A Brief Introduction to make and the S-Q Makefiles

This is a brief document that I put together for CS50 in 1994. It describes make and how we use it for S-Q. For more complete documentation, refer to the man page for make, and any tutorials you can get your hands on, or ask me. This document only scratches the surface- you don't need to know much about make for this course, but it is a rich and complex system that is well worth investigating.

I've tried to make this information accurate, but I don't know have an encyclopedic knowledge of make, and there are many different versions of make that are all slightly different. Please let me know if you find any glaring errors. -DJE


An Introduction to make

make is a program (and a programming language) intended to help in the development of programs or other files that are constructed or derived from one or more source files. Most importantly, make provides a way to specify the relationship between the source files necessary to build a derived object (such as a program), and specify, in a relatively high-level manner, the steps that should be taken derive the object from its source.

For example, to build an executable program from a group of .c files, each of the .c files needs to be compiled with gcc to a .o file, and then the .o files need to be linked together (possibly with some additional libraries) to form the executable. Keeping this all straight is a chore for the programmer, and he or she is likely to make mistakes. make can help automate this process, reducing the likelihood of error and simplifying the task.

It is worth noting that make has implicit rules or shortcuts (that can be extended) for many common actions (such as how to turn .c files into .o files), so it is relatively easy to do things such as use make to compile C programs. However, make can also be used for many other similar purposes.

How Does make Work?

When you run make, you usually need to give it a file containing a bunch of definitions and rules (in effect, a program written in the make language). These are described in the next section. By default, make searches the current directory for a file named makefile, and if there isn't a file by that name, looks for a file named Makefile (and possibly others, depending on the version of make). You can also specify the file to use explicitly, via the -f option to make, which is very handy if you wish to have several makefiles.

make has innumerable options; see the man page for more information.

The parameters to make are the names of the targets to build- the names of the things you want make to do for you. For example, by this point you've invoked make a zillion times with a command like:

	make hello

This tells make to look in the makefile, and do whatever actions are associated with building hello. You can also tell make to do several things, which it will do in the order you specify, i.e.

	make clean hello

This command will first do whatever actions are associated with making "clean", and then building "hello" (stopping if any command along the way fails).

Parts of a Makefile

There are several kinds of lines in a Makefile:

  1. Comments. Any line that starts with a '#' in column 0 is a comment, and is ignored.

  2. Variable definitions. A line of the form:
    	NAME = value
    	

    defines NAME in subsequent statements to have the given value. Note that the value is the entire contents of the line after the "=", except surrounding whitespace, and lines can be continued with the "\" continuation character. Therefore, the lines:

    	FOO	= bar \
    		qux \
    		baz
    	

    are treated as a single line, which sets variable FOO to "bar qux baz".

    Once a variable is defined, its value can be extracted with the $(variable) or ${variable} operators. Note that if the parenthesis are left off, then just the first character of the variable name is used. Therefore, $(FOO) gets the value of variable FOO, while $FOO gets the value of variable F (if any) and prepends it to the string "OO". Exactly what happens in this case varies from make to make, but is almost never what you really wanted to do.

    Note that trying to get the value of a variable which has not been defined is not an error; instead, the value returned is the empty string. This can be somewhat confusing if you define one variable in terms of another, and forget to set the second.

    Most decent versions of make also allow variables to be defined by substitution of some pattern (such as the suffix) of each element in another variable. For example, your Makefile could have the lines:

    	MY_C_FILES	= foo.c bar.c baz.c
    	MY_O_FILES	= $(MY_C_FILES:.c=.o)
    	

    The first line defines MY_C_FILES to be "foo.c bar.c baz.c", while the second line says "let MY_O_FILES be all the 'words' in MY_C_FILES which end in .c, except replace the .c with a .o". This is very handy, since if you have a lot of .c files, and you want to turn them all into .o files, it could be painful to type out the whole list of files again.

    Note that shell environment variables are accessible as make variables; a bunch of the variables set in the in the libsq environment are used by the S-Q makefiles.

  3. Dependency rules.

    Dependency rules are the heart of make; some makefiles contain nothing else. The purpose of a dependency rule is to define the dependency of some target on some sources, to let make know that it needs to have (or make) all the sources before it can make the target.

    make is clever about how it interprets its dependency rules, and goes to some length to figure out how to minimize the amount of work it does. This is why when you type 'make' twice in a row, make doesn't do anything except perhaps tell you that your program is 'up to date' (assuming that your program compiled correctly the first time). This is because make keeps track of when each of the files your program depends upon were last edited or modified in any way, so that it can figure out whether or not it actually needs to rebuild anything. If you haven't edited any of your files since the last time you compiled your program, and you haven't deleted your copy of the program, then make assumes that the compiler will just create exactly the same program you already have, and knows not to bother doing anything. This can save you a tremendous amount of compiling time.

    So what does a dependency rule look like? A dependency rule starts with a line of the form:

    	target: sources
    	

    Note- whitespace is important! On many versions of make, the name of the target *must* start in column 0 (i.e. the leftmost position), and most be followed immediately by a colon, and then the sources. On some systems, there must be some sort of whitespace, such as a tab, between colon and the sources.

    The sources may be left out, if the target doesn't depend on any source files.

    Of course, both the target and the sources can be variables (but if so, the the target variable must evaluate to a single word, not a list).

    It is also possible to define, as part of the dependency rule, how to make the target from the sources. This is done by putting the appropriate commands on the lines following the

    	target:	sources
    	

    line, indented by one tab. (whitespace is crucial here again) The dependency rule is terminated by a blank line, or the start of another dependency rule. The commands can be any legal shell commands (substitution of make variables is done before make passes the commands to the shell). You can define which shell make uses by setting the SHELL variable; the default is generally sh.

    For example, imagine that you had a file foo.c, and wanted to make a .o file out of it, using the C compiler in its simplest form. This would be summarized by the following rule:

    	foo.o:	foo.c
    		gcc -o foo.o foo.c
    	

    make also maintains a bunch of variables that change value depending on what rule you're in, or other things. These include:

    $?
    The list of all targets that are "out of date" with respect to the current target. For example, if A depends on B, C, and D, and C is up-to-date and B and D are not, then inside a rule to build A, $? would be equal to "B D".

    $@
    The name of the current target. This is particularly useful for cutting and pasting rules; you don't need to edit each rule to change the target name.

    Therefore, the last rule could be rewritten as:

    	foo.o:	foo.c
    		gcc -o $@ $?
    	

  4. Derivation rules.

    Derivation rules tell make how to "automatically" make a file with a particular suffix out of a file with another suffix, you can set up rules that look similar to dependency rules, but don't contain the names of specific files. For example, to tell make to make .o files from .c files using the C compiler in the same manner as before, you could write:

    	.c.o:
    		gcc -o $@ $?
    	

  5. What am I forgetting?

    Probably something important.


An Example Makefile

Let's write the "hello world" program in make:

hello:
	@echo "hello"

What's that "@" before the echo for? Well, ordinarily, make tells you all the commands that it's going to do before it does them (so you can see what it was doing when the compiler starts spitting out zillions of error messages, for example), but in this case we don't want it to. make allows this by not printing out every command that begins with a @.


A More Realistic Example Makefile

As an example, here is an example Makefile based on the Makefile given out for use in the cs50 animals assignment in 1994.
## define EXE to be "animals".  We'll use EXE to represent the name
##	of the program that we're building.
EXE	= animals

## define SRC to be "animals.c foo.c bar.c".  This is the list of
##	.c files we need to build our animals program.  Note that the
##	.h files necessary are specified elsewhere, not here.
SRC	= animals.c foo.c bar.c

## Tell make to use gcc to compile C programs, not the default HP
##	compiler, which is pretty cheesy.  CC is a variable that
##	make uses internally.
CC		= gcc

## Specify some options to the C compiler:
##	CC_OPTIONS	- symbolizes the optimization/debugging flags
##				to pass to gcc.
##	INCLUDES	- the list of all the extra directories to search
##				for #include files.  Note that gcc always
##				looks in the current directory and the
##				default system include directories.
##				This tells it to use the CS50 libraries
##				as well.
##	CFLAGS		- just CC_OPTIONS and INCLUDES pasted together.
##	LIBS		- what libraries to search (in the form that gcc
##				understands).  Again, gcc knows where to
##				find all the default libraries, but we
##				need to tell it about the CS50 libraries.
CC_OPTIONS	= -g -Wall
INCLUDES	= -I$(CS50_INC)
CFLAGS		= $(CC_OPTIONS) $(INCLUDES)
LIBS		= -L$(CS50_LIB) -lcs50

## Define OBJ, which symbolizes all the .o files that the compiler needs
##	to create and then link together in order to create the program.
OBJ		= $(SRC:.c=.o)

## Here's where the action is:
##	This gets expanded (after substituting the variables) to
##
## animals:	animals.o foo.o bar.o
##	gcc -g -Wall -o animals animals.o foo.o bar.o -L ~lib50/usr/lib -lcs50
##
##	Somehow, this seems more clear:
## 
$(EXE):		$(OBJ)
	$(CC) $(CC_OPTIONS) -o $@ $(OBJ) $(LIBS)

## Another target, to clean up files you don't want any more.
##	Notice that the "clean" target doesn't depend on anything, so
##	any time you type "make clean", it will try to delete animals,
##	all the .o files, a.out, and the core dump (if any).
clean:
	rm -f $(EXE) *.o a.out core

## The depend target is somewhat subtle.  It runs a program named
##	makedepend (which is not part of make, but is pretty common)
##	that scans the given sources files (in this case, $(SRC))
##	and builds a list of all the files in the current directory
##	or any of the directories in $(INCLUDE) for files which are
##	#include'd in each source file.  makedepend also uses this
##	information to actually rewrite your Makefile for you (which
##	is what that ominous warning at the end of the Makefile is
##	all about).  After you run this in your own animals directory,
##	take a look and see what it does!
##
depend:		Makefile $(SRC)
	makedepend $(INCLUDES) $(SRC)

## end of Makefile
# DO NOT DELETE THIS LINE -- make depend depends on it.


Please bring any errors or inconsistencies in this document to the attention of the author.

Dan Ellard