M A K E Make helps you maintain, update, or regenerate sets of programs or files. It is a great time-saver during program development. No Unix system could survive without make. Software distributed to Unix systems invariably comes packaged so that it can be installed by make. Introduction ------------ Make "knows" how to generate simple types of programs, e.g. C or Fortran programs residing in a single source file which do not use libraries. Consider the following example % ls -l total 2 -rw-r--r-- 1 boisvert 61 Nov 7 14:46 daniel.f -rw-r--r-- 1 boisvert 69 Nov 7 14:45 jack.c % make jack cc -sun4 -o jack jack.c % make daniel f77 -sun4 -o daniel daniel.f % ls -l total 178 -rwxr-xr-x 1 boisvert 147456 Nov 7 15:00 daniel -rw-r--r-- 1 boisvert 61 Nov 7 14:46 daniel.f -rwxr-xr-x 1 boisvert 24576 Nov 7 15:00 jack -rw-r--r-- 1 boisvert 69 Nov 7 14:45 jack.c Here asked make to generate the files jack and daniel. It observed that there were files named jack.c and daniel.f in the current directory, and hence used the C and Fortran compilers, respectively, to generate the executables. Make also knows how to make .o files from .c files or .f files (e.g., "make jack.o"). Make does not like to do unnecessary work. Continuing the above example, if we ask make to generate the files jack and daniel again, it balks. % make jack daniel `jack' is up to date. `daniel' is up to date. Consider the first case. Make determines that the file jack depends upon jack.c. It then looks at the modification times of these two file (i.e. the date and times printed by ls -l). It sees that jack.c has not been changed since jack was last generated. It concludes that there is no reason to regenerate it and reports that jack is "up to date". The following terminology is used : the files that you wish to maintain are called targets. The files needed to generate them are called dependencies. The Unix commands used to generate a target from its dependencies are called rules. We see here that make knows rules for generating targets based on simple dependencies. Maintaining Sets of Programs ---------------------------- In most cases the relationship between a target and its dependencies is more complex. For example, you might have a main program and many subprograms, each of which occupies a separate file. Cnsider the case of an executable, prime, which depends upon three files : prime.f, sieve.f, and addlst.f. % ls -l total 4 -rw-r--r-- 1 boisvert 200 Nov 7 15:15 addlst.f -rw-r--r-- 1 boisvert 570 Nov 7 15:16 prime.f -rw-r--r-- 1 boisvert 437 Nov 7 15:15 sieve.f % make prime f77 -sun4 -o prime prime.f ld: Undefined symbol _addlst_ _sieve_ *** Error code 1 make: Fatal error: Command failed for target `prime' Make simply has no way to know which .f files must be combined to get a complete program. In cases like this you must teach make how to generate the target. You do this by providing a file named Makefile in the current directory. For each target you want to maintain you place an entry of the form TARGET : DEPENDENCIES RULES in this file. Rule lines must start with a tab character. If there is no entry for a target, then make applies its default rules (called implicit rules). Here is a simple Makefile for the above example: prime : prime.f sieve.f addlst.f f77 -o prime prime.f sieve.f addlst.f Supplied with this Makefile make knows what to do. % make prime f77 -o prime prime.f sieve.f addlst.f Some Shortcuts -------------- When trying to debug Makefiles one often uses the Unix touch command. "touch name" changes the modification time of the named file to be the current time. This is a way to fool make into thinking that you have changed a file. Another useful tool is the -n option on the make command. With this option, make prints all the commands it intends to execute, but doesn't actually execute them. We will make use of these things in the examples that follow. If you type "make", make will work on the first target in the Makefile. Dependency Trees ---------------- The rules for generating prime in the above example were not very efficient. If a change is made to any of the .f files, then, using the rules we supplied in the Makefile, make will recompile ALL of them, even though object files are available for the unchanged source. % ls -l total 160 -rw-r--r-- 1 boisvert 73 Nov 7 15:18 Makefile -rw-r--r-- 1 boisvert 200 Nov 7 15:53 addlst.f -rw-r--r-- 1 boisvert 452 Nov 7 15:53 addlst.o -rwxr-xr-x 1 boisvert 147456 Nov 7 15:53 prime -rw-r--r-- 1 boisvert 570 Nov 7 15:16 prime.f -rw-r--r-- 1 boisvert 1144 Nov 7 15:53 prime.o -rw-r--r-- 1 boisvert 437 Nov 7 15:15 sieve.f -rw-r--r-- 1 boisvert 404 Nov 7 15:53 sieve.o % touch addlst.f % ls -l addlst.f -rw-r--r-- 1 boisvert 200 Nov 7 15:54 addlst.f % make -n prime f77 -o prime prime.f sieve.f addlst.f A more exact representation of the dependencies among these files is contained in the following Makefile: prime : prime.o sieve.o addlst.o f77 -o prime prime.o sieve.o addlst.o prime.o : prime.f f77 -c prime.f sieve.o : sieve.f f77 -c sieve.f addlst.o : addlst.f f77 -c addlst.f (Note that the entries for the last three targets could have been omitted from the Makefile since they correspond to make's default rules for making .o files from .f files.) Here prime depends upon prime.o, sieve.o and addlst.o, which in turn depend upon prime.f, sieve.f, and addlst.f, respectively. We see that, in general, each target generates a tree of dependencies. The dependency tree for prime is .---- prime ----. / | \ prime.o sieve.o addlst.o | | | prime.f sieve.f addlst.f When asked to make a target, make inspects the entire dependency tree. Beginning at the leaves of the tree, it compares the dates of each target-dependency pair, regenerating out-of-date-targets according to the rules in the Makefile, until it reaches the root. With this Makefile, the example at the beginning of this section becomes % make -n prime f77 -c addlst.f f77 -o prime prime.o sieve.o addlst.o Macros ------ There is a very useful macro substitution facility in make. One defines a macro by a simple assignment statement: MACRONAME = string "string" may have embedded blanks. A macro is substituted anywhere in the Makefile using the notation $(MACRONAME) We simplify our example using macros as follows: OBJECTS = prime.o sieve.o addlst.o prime : $(OBJECTS) f77 -o prime $(OBJECTS) There are many predefined macros. These macros are used heavily in make's implicit rules. One can use this fact to modify the behavior of make's implicit rules. For example, the implicit rule used by make to generate .o files from .f files is something like eted. More Information ---------------- We have only scratched the surface of make. The man page for make contains alot more details, although it may be difficult to digest. Books have been published which provide a more gentle introduction to make. Among them are Steve Talbot, Managing Projects with Make, O'Reilly & Associates, (632 Petaluma Ave, Sebatopol, CA, 95472), 1990. Stephen G. Kochan and Patrick H. Wood, Topics in C Programming, Hayden Books, (4300 West 62nd St. ,Indianapolis, IN 46268), 1987, Chapter 7. - Ron Boisvert NIST, Center for Computing and Applied Mathematics 23 Nov 1990