make intro

A brief introduction to make and makefiles

The make utility is a very flexible tool for, installing downloaded programs and compiling your own code, among other things. Here are some very crude instructions for how to use make to streamline the compilation of your code into executable files.

Getting started.

Suppose you have a C++ program, "", that you want to compile and run. If the code is self-contained, in that it has definitions for all functions called within it, then it can be compile into an executable program "prog" in the usual way:

g++ -o prog 
The un*x make command allows you to generate executable in another way. In a file called "makefile" (or "Makefile"), put the following text:
# a makefile. this is a comment!

CXX = g++

prog: prog.o
       ${CXX} prog.o -o prog

(Important note: there is a TAB character (before the ${CXX})-- NOT spaces.) Then, when "" and "makefile" are both in your working directory, just type
make prog
into your shell. The source code will automatically be compiled. A couple things to note: First, you specify which C++ compiler to use by defining a variable "CXX" -- here we set it to the Gnu g++ compiler. Second, make is run using a command line argument "prog". This is known as a "target", and it must match the command line argument. As you will see below, there can be more than one target per makefile.

The role of the information on the line (or lines) just below the target tells the makefile what to do when you type the command "make prog" into your shell. The information must be in the form of valid shell commands. In this case, it says to compile an object code "prog.o" using the C++ compiler specified by a (shell variable) "CXX". This may seem a little strange, because we usually work with source code (, not object code (prog.o) -- source code that has been compiled but not "linked" together with the stuff that the system needs to make an executable program. However make is tuned to understand what you want to do, as we now describe.

The filename following the target "prog" in the makefile is "prog.o", and this is called a "dependency". The make utility, in evaluating a target, automatically checks to see if there is a file with the same name as the target ("prog"), and if so, it compares the last-modified time of the target with a file named the same as the dependency ("prog.o"). If the dependency either does not exist, or if it is newer than the target file, then make goes on to the next line and does whatever is specified -- here, it does a compilation of "prog.o". Since the dependency here is an object file, a special feature of make kicks in: it goes off and looks for "" to see if it is more recent than the object code -- if so, then make uses the "CXX" compiler to regenerate the object code from the source code, then it goes ahead and compiles "prog.o" into executeble "prog" as directed.

In general, the dependencies are targets themselves, and make will look for targets with those names to figure out what to do with them, until it gets to dependencies which are only filenames and not targets.

Incidentally, simply typing "make" (without the "prog" command-line argument) into your shell sets the default target to be the first one defined in your makefile.

NB: I cannot overemphasize that the line or set of lines immediately following a target definition ("prog: prog.o" in this example) MUST BEGIN WITH A TAB CHARACTER and NOT A BUNCH OF SPACES. For this reason, DO NOT simply cut-n-paste text from your browser into your makefile -- your browser will typically convert the TAB character into a bunch of spaces and the makefile will not work.

The basics

The advantage of using a makefile may not be apparent in the case of a compilation involving a single file. But when many files are involved, you can set make up to do efficient compilation, perfect for development work. You can also count on make to be very helpful in compiling complicated public-domain packages (e.g., gimp?). We won't go to the limit of many, many files, as is sometimes needed to compile certain utilities that you can get on the web, but will instead only consider compilations involving only a few codes. I would argue that even in the case of a very few files, you should use make when you are sharing code with others. This is a courtesy, since you shield your collaborators (or your grader) from any and all arcane compilation issues.

Let's assume that we have to compile and link the following things:

    source code in your working directory:              -- program with main()        -- extra code, functios called in
        some_hdr.h          -- header file with prototypes, class defs, etc.
                               #include'd in
    name of a library of C++ function, contained in directory /home/me/my/lib/:

    a header file with some_lib's function prototypes, in /home/me/my/include/:

The idea is that you wrote,, and, and there is some library of code which your uses. The header some_lib.h contains prototypes for these libarary functions. The first three files are in your working dir but the library-related files may be in some other directory.

Here is how to set up a makefile (called "makefile" or "Makefile") which allows you to easily compile your program "prog": "" will be linked:

# a fancier makefile
CXX = g++
CXXFLAGS = -Wall -I/home/me/my/include/
LDFLAGS = -L/home/me/my/lib/ -lsome_lib

prog: prog.o more_code.o
      ${CXX} ${CXXFLAGS} prog.o more_code.o ${LDFLAGS} -o prog

prog.o: some_hdr.h

      rm  myprog.o more_code.o prog

Some quick comments on the above version: The make-specific variables (the ones in capital letters) follow a name convention such that: "CXX" refers to the compiler ("g++" in this case, same as before); "CXXFLAGS" contains compiler directives/options (here, -Wall means warn user of all potential errors, i.e., "whine as much as possible", and the "-I.." tells the compiler where to look for header files if they are not found in your working directory or the standard places where [e.g.] iostream is found); and "LDFLAGS" is a list of link/load directives, often used to tell the compiler to look for missing function definitions in libraries (here, we use the fictitious library "some_lib", and use the "-L.." directive to specify which directory to look for libraries if they arent in the standard places.).

Also, note that there are three targets in this makefile. The first, "prog", is the target used to generate executable code. It gets invoked when the user types

     make prog
(or simply "make" since the first target is the default for make if no target is given). The target "prog" has two dependencies, object files prog.o and more_code.o. If either of these files -- or their corresponding *.cc codes -- has been modified since the file "prog" was last made, make will recompile it.

The second, "prog.o", sets a dependency of prog.o (and, implicitly, on the include file some_hdr.h. That is, it instructs make to compare the last-modified times of some_hdr.h and prog.o to see if needs to be recompiled using a new version of the header file.

The third target, "clean", has no dependencies. When the user types

     make clean
make removes the object code and the executable code, as instructed in the line following the target label.

Once again, the blank space in front of the "${CXX}" is a TAB character -- make will whine if it doesn't find one there.

An example.

Let's now consider an example, one taken from the Computational Physics course. In assignment 1, you are asked to submit several C++ programs,,, and One of these codes needs a header file called mpmath.h, is located in the class directory /u/course/p5730/include. You could submit it, along with the .cc files, or you could use the version in the class include directory (by compiling with the -I directive, as mentioned above). Here is a version of the makefile that would work:

# a01 makefile

P6730 = /u/course/p5730

CXX = g++
CXXFLAGS = -O4 -I${P6730}/include

floatbits: floatbits.o
	${CXX} ${CXXFLAGS} floatbits.o -o floatbits

pidigits: pidigits.o
	${CXX} ${CXXFLAGS} pidigits.o -o pidigits -lgmp

sinfast: sinfast.o
	${CXX} ${CFLAGS} sinfast.o -o sinfast

	make floatbits

	make pidigits

	make sinfast

	make ex1 ex2 ex3

	rm -f sinfast sinfast.o floatbits floatbits.o pidigits pidigits.o

You can get this code directly from the file, in directory ~p5730/makefiles. Just "cp" it to "makefile" in your working directory.

Notice that in addition to targets "floatbits", "pidigits", and "sinfast", there is a target "assgn" such that

     make assgn
compiles all the source code for this assignment (using targets for individual exercises, e.g., "ex1"). You should make sure that this works by copying your files-to-submit to a temporary directory and testing it. The TA will type "make assgn" to generate your executable programs for grading, and you really do want this to work!

Final comments.

The make utility can be used with great generality to help organize not only software development with many files, but the installation of complicated software involving multiple compilations, the setup of info/man/help pages, system-specific files, and so forth. It can be used to generate and organize code which is not even executable, such as LaTeX/Postscrip/PDF document files (Prof. Detar has a nice setup for dealing with the many documents required for grant proposals).

Thus we have only scratched the surface of make's capabilities. To find out more, search the web. For example, you can try this introduction to make and makefiles or the manual for gnu make, by Richard M. Stallman and Roland McGrath. These may be weighted more for C, rather than C++, but the overall ideas are the same.

bcb 5-Jan-01.