------------------------------- Page    i -------------------------------

                          Make - A Program for

                      Maintaining Computer Programs




                                                 S. I. Feldman



                                                 Edited for UTS

------------------------------- Page   ii -------------------------------

                            TABLE OF CONTENTS


1.    Abstract  . . . . . . . . . . . . . . . . . . . . . . . . . . .   1

2.    Introduction  . . . . . . . . . . . . . . . . . . . . . . . . .   1

3.    Basic Features  . . . . . . . . . . . . . . . . . . . . . . . .   2

4.    Description Files and Substitutions . . . . . . . . . . . . . .   5

5.    Command Usage . . . . . . . . . . . . . . . . . . . . . . . . .   6

6.    Implicit Rules  . . . . . . . . . . . . . . . . . . . . . . . .   8

7.    Example . . . . . . . . . . . . . . . . . . . . . . . . . . . .   9

8.    Suggestions and Warnings  . . . . . . . . . . . . . . . . . . .  12

9.    SCCS Files  . . . . . . . . . . . . . . . . . . . . . . . . . .  12

10.   Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . .  13

11.   References  . . . . . . . . . . . . . . . . . . . . . . . . . .  13

Appendix A.    Suffixes and Transformation Rules  . . . . . . . . . .  14


                                                            Last Page  15

-------------------------------- Page  1 --------------------------------

1.    ABSTRACT

In a programming project, it is easy to lose track of which files need to
be reprocessed or recompiled after  a change is made in some part of  the
source.  Make provides a simple mechanism for maintaining up-to-date ver-
sions of programs that result from many operations on several  files.  It
is possible to tell  make the  sequence of commands  that create  certain
files, and  the list  of files  that require  other files  to be  current
before the operations can be done.  Whenever a change is made in any part
of the program,  the make  command will create  the proper files  simply,
correctly, and with a minimum amount of effort.

The basic operation of make is to find the name of a needed target in the
description, ensure that all of  the files on which it depends exist  and
are up to date, and  then create the target if  it has not been  modified
since its generators were.  The description file really defines the graph
of dependencies; Make does a depth-first  search of this graph to  deter-
mine what work is really necessary.

Make also provides a simple  macro substitution facility and the  ability
to encapsulate commands in a single file for convenient administration.




2.    INTRODUCTION

It is common practice to divide large programs into smaller, more manage-
able pieces.  The pieces may require different treatments: some may  need
to be run through a macro processor, some may  need to be processed by  a
sophisticated program generator (e.g., yacc [1] or lex [2]).  The outputs
of these generators may then have to be compiled with special options and
with certain definitions and declarations.  The code resulting from these
transformations  may  then  need  to  be  loaded  together  with  certain
libraries under  the  control of  special options.   Related  maintenance
activities involve running complicated test scripts and installing  vali-
dated modules.   Unfortunately, it  is easy  for a  programmer to  forget
which files  depend  on which  others,  which files  have  been  modified
recently, and the exact sequence of operations needed to make or exercise
a new version  of the  program.  After a  long editing  session, one  may
easily lose  track of  which  files have  been changed  and which  object
modules are still valid, since a change  to a declaration can obsolete  a
dozen other files.  Forgetting to compile a routine that has been changed
or that uses changed declarations will result in a program that will  not
work, and  a bug  that can be  hard to  track down.  On  the other  hand,
recompiling everything in sight just to be safe is wasteful.

-------------------------------- Page  2 --------------------------------

The program described in this report mechanizes many of the activities of
program development  and maintenance.   If the  information on  interfile
dependences and command sequences is stored in a file, the simple command

     make

is frequently enough to update  the interesting files, regardless of  the
number that have been edited since the last 'make'.  The description file
is usually easy to write, and changes infrequently.  It is usually easier
to type the make command than to issue even one of the needed operations,
so the typical cycle of program development operations becomes

     think -- edit -- make -- test  . . .

Make is most useful  for medium-sized programming  projects; it does  not
solve the problems of maintaining multiple source versions or of describ-
ing huge programs.




3.    BASIC FEATURES

The basic operation of make is to update  a target file by ensuring  that
all of  the files  on which it  depends exist  and are up  to date,  then
creating the target  if it  has not  been modified  since its  dependents
were.  Make does a depth-first  search of the graph of dependences.   The
operation of the command depends on the ability to find the date and time
that a file was last modified.

To illustrate, let us consider a simple example: A program named prog  is
made by compiling and  loading three C  language files x.c, y.c, and  z.c
with the lpw library.  By  convention, the output  of the C  compilations
will be found in  files named x.o, y.o,  and z.o.  Assume that the  files
x.c and y.c share some  declarations in a file named  defs that z.c  does
not.  That is, x.c and y.c have the line

     #include "defs"

The following text describes the relationships and operations:

     prog:  x.o y.o z.o              cc x.o y.o z.o -lpw -o prog

     x.o y.o:  defs

If this information were stored in a file named makefile, the command

-------------------------------- Page  3 --------------------------------

     make

would perform the operations needed  to re-create prog after any  changes
had been made to any of the four source files x.c, y.c, z.c, or defs.

Make  operates  using  three  sources  of  information:  a  user-supplied
description file (as  above), file names  and 'last-modified' times  from
the file system, and built-in rules to bridge  some of the gaps.  In  our
example, the first line says that prog depends on three '.o' files.  Once
these object files are  current, the  second line describes  how to  load
them to create prog.  The third line says that x.o  and y.o depend on the
file defs.  From the  file system,  make discovers that  there are  three
'.c' files  corresponding to  the needed  '.o' files,  and uses  built-in
information on how to generate an object from a source file (i.e.,  issue
a 'cc -c' command).

The following  long-winded description  file  is equivalent  to  the  one
above, but takes no advantage of make's innate knowledge:

     prog:  x.o y.o z.o              cc x.o y.o z.o -lpw -o prog

     x.o:  x.c defs              cc -c x.c
     y.o:  y.c defs              cc -c y.c
     z.o:  z.c              cc -c z.c


If no source  or object file  has changed  since the last  time prog  was
made, all of the files would be current, and the command

     make

would just announce this fact and stop.   If, however, the defs file  had
been edited, x.c and y.c (but not z.c) would be recompiled, and then prog
would be created  from the  new '.o'  files.  If  only the  file y.c  had
changed, only it would be recompiled, but it would still  be necessary to
reload prog.

If no target name  is given on  the make command  line, the first  target
mentioned in the description is created; otherwise the specified  targets
are made.  The command

     make x.o

would recompile x.o if x.c or defs had changed.

If the file  exists after  the commands are  executed, its  time of  last
modification is used in further decisions; otherwise the current time  is
used.  It is often useful to include  rules with mnemonic names and  com-
mands that do not produce a file with that name.   These entries can take

-------------------------------- Page  4 --------------------------------

advantage of  make's ability  to generate  files and  substitute  macros.
Thus, an entry 'save' might  be included to copy a certain set of  files,
or an entry 'cleanup' might  throw away unneeded intermediate files.   In
other cases one may maintain  a zero length file purely to keep track  of
the time at which certain actions  were taken.  This technique is  useful
for maintaining remote archives and listings.

Make has a simple  macro mechanism for  substituting in dependency  lines
and command strings.  Macros are defined by command arguments or descrip-
tion file lines with embedded equal signs.  A macro is invoked by preced-
ing the name by a dollar sign; macro names longer  than one character may
be parenthesized.  The name of the  macro is either the single  character
after the dollar sign or  a name, possibly parenthesized.  The  following
are valid macro invocations:

     $(CFLAGS)
     $CFLAGS
     $2
     $(xy)
     $Z
     $(Z)

The first two  and the  last two  invocations show  that parenthesis  are
optional.Both the parenthesized and  the non-parenthesized versions  have
identical meanings.   $$ is  a  dollar sign.   All of  these  macros  are
assigned values during input, as shown below.  Four special macros change
values during the execution  of the command:  $*, $@, $?,  and $<.   They
will be discussed later.  The following fragment shows the use of macros:

     OBJECTS = x.o y.o z.o
     LIBES = -lpw
     prog: $(OBJECTS)              cc $(OBJECTS) $(LIBES) -o prog
     ...

The command

     make

loads the three object files with the lpw library.  The command

     make "LIBES = -ll -lpw"

loads them with  both the  lex ('-ll')  and the  PWB ('-lpw')  libraries,
since macro definitions on the  command line override definitions in  the
description.  (It is necessary to quote arguments with embedded blanks in
UTS commands.)

The following sections detail the form of description files and the  com-
mand line, and discuss options and built-in rules in more detail.

-------------------------------- Page  5 --------------------------------

4.    DESCRIPTION FILES AND SUBSTITUTIONS

A description file  contains three  types of  information: macro  defini-
tions, dependency information, and executable commands.  There is also  a
comment convention: all characters after a  sharp (#) are ignored, as  is
the sharp  itself.  Blank  lines  and lines  beginning with  a sharp  are
totally ignored.  If a noncomment line is  too long, it can be  continued
using a backslash.  If the  last character of a line is a backslash,  the
backslash, new-line, and following blanks and tabs are replaced by a sin-
gle blank.

A macro definition is a line containing an  equal sign not preceded by  a
colon or a tab.  The  name (string of letters and digits) to the left  of
the equal sign (trailing blanks  and tabs are  stripped) is assigned  the
string of characters following  the equal sign  (leading blanks and  tabs
are stripped).  The following are valid macro definitions:

     2 = xyz
     abc = -ll -ly -lpw
     LIBES =

The last definition assigns LIBES the null string.  A macro that is never
explicitly defined has the null  string as value.  Macro definitions  may
also appear on the make command line (see below).

Other lines give information about target files.  The general form of  an
entry is:

     target1 [target2 ...] :[:] [dependent1 ...] [; commands] [# ...]
     [<TAB> commands] [# ...]
     ...

Items inside brackets may be omitted.  Targets and dependents are strings
of letters, digits, periods, and slashes.  (Shell metacharacters '*'  and
'?' are expanded.)  A command is any string of characters not including a
sharp (except in quotes) or new-line.  Commands may appear either after a
semicolon on a  dependency line or  on lines  beginning with a  tab or  a
blank immediately following a dependency line.

A dependency line may have either a single  or a double colon.  A  target
name may appear on more than one dependency line, but  all of those lines
must be of the same (single or double colon) type.

 1.  For the usual single colon  case, at most  one such dependency  line
     may have a command  sequence associated with  it.  If the target  is
     out of date with any  of the dependents on any  of the lines, and  a
     command sequence is specified (even a null one following a semicolon
     or tab), it is executed;  otherwise a default  creation rule may  be
     invoked.

-------------------------------- Page  6 --------------------------------

 2.  In the double colon case, a command sequence may be associated  with
     each dependency line; if the  target is out of date with any of  the
     files on a particular line, the associated commands are executed.  A
     built-in rule may also be  executed.  This detailed form is of  par-
     ticular value in updating archive-type files.

     If a target must be created,  the sequence of commands is  executed.
     Normally, each command line is printed and then passed to a separate
     invocation of the shell after substituting for macros.  (The  print-
     ing is suppressed in silent mode or if the command  line begins with
     an @ sign.)  Make normally stops if any command signals an error  by
     returning a nonzero  error code.   (Errors are ignored  if the  '-i'
     flags has been specified on the make command line, if the fake  tar-
     get name '.IGNORE' appears in  the description file, or if the  com-
     mand string in the description file begins with a hyphen.  Some  UTS
     commands return meaningless status.)   Because each command line  is
     passed to a separate  invocation of  the shell, care  must be  taken
     with certain  commands (e.g.,  cd and shell  control commands)  that
     have meaning only  within a  single shell process;  the results  are
     forgotten before the next line is executed.

     Before issuing any command, certain  macros are set.   $@ is set  to
     the name of the file to be 'made'.  $? is set to the string of names
     that were found to be younger than  the target.  If the command  was
     generated by an  implicit rule (see  below), $< is  the name of  the
     related file that caused the action, and $* is the prefix shared  by
     the current and the dependent file names.

     If a  file must  be  made but  there  are no  explicit  commands  or
     relevant built-in  rules,  the  commands associated  with  the  name
     '.DEFAULT' are used.  If there is no  such name, make prints a  mes-
     sage and stops.




5.    COMMAND USAGE

The make command takes four kinds of arguments: macro definitions, flags,
description file names, and target file names.

     make [flags] [macro definitions] [targets]

The following summary of the operation of the command explains how  these
arguments are interpreted.

-------------------------------- Page  7 --------------------------------

First, all  macro definition  arguments  (arguments with  embedded  equal
signs) are analyzed and the assignments made.  Command line macros  over-
ride corresponding definitions found in the description files.

Next, the flag arguments are examined.  The permissible flags are

-z   Print a detailed trace of make's analysis of your description file.

-i   Ignore error  codes returned  by  invoked commands.   This  mode  is
     entered if the fake target name '.IGNORE' appears in the description
     file.

-s   Silent mode.  Do  not print  command lines  before executing.   This
     mode is also entered  if the fake  target name '.SILENT' appears  in
     the description file.

-r   Do not use the built-in rules.

-n   No execute mode.   Print commands,  but do not  execute them.   Even
     lines beginning with an '@' sign are printed.

-t   Touch the target files (causing them to  be up to date) rather  than
     issue the usual commands.

-q   Question.  The make command  returns a zero  or nonzero status  code
     depending on whether the target file is up to date.

-p   Print out the complete set of macro definitions and target  descrip-
     tions

-d   Debug mode.  Print out detailed information on files and times exam-
     ined.

-v   Version numbers.  Base  decisions for  gets on  revision level  com-
     parisons with the  macro on  the command line  if one exists  rather
     than by modification time.

-f   Description file name.  The next argument is assumed to be the  name
     of a  description file.   A file  name of '-'  denotes the  standard
     input.  If there are no '-f'  arguments, the file named makefile  or
     Makefile in  the current  directory is  read.  The  contents of  the
     description files override the built-in rules if they are present.

-I file   Include  the file  name specified  along with  the  description
     file.

Finally, the remaining arguments are assumed  to be the names of  targets
to be made; they are  done in left to right order.  If there are no  such
arguments, the first name in  the description files  that does not  begin

-------------------------------- Page  8 --------------------------------

with a period is 'made'.




6.    IMPLICIT RULES

The make  program uses  a table  of  interesting suffixes  and a  set  of
transformation rules to supply default dependency information and implied
commands.  (The Appendix describes these  tables and means of  overriding
them.)  The default suffix list is:

     .o      object file
     .c      C source file
     .h      header file
     .p      PL/I source file
     .d      DTL source file
     .t      test file
     .s      assembler source file
     .y      yacc/C source grammar
     .l      lex source grammar
     .q      quickscreen source file

The following diagram  summarizes the default  transformation paths.   If
there are two paths connecting a pair of suffixes, the longer one is used
only if the intermediate file exists or is named in the description.   An
exception is  an SCCS  transformation,  such as  .c->.c, .s->.s,  .q->.q,
.y->.y or .l->.l.

                                .o



       .c           .s    .y    .l             .h


 .y  .l  .q  .c     .s    .y    .l             .h


 .y  .l  .q

If the file x.o were needed and there were  an x.c in the description  or
directory, it would be compiled.  If there were also an x.l, that grammar
would be run through Lex before compiling the result.  However, if  there
were no x.c but there were an x.l, make would  discard the intermediate C
language file and use the direct link in the graph above.

-------------------------------- Page  9 --------------------------------

It is possible to change the names of some  of the compilers used in  the
default, or the flag arguments with which they are invoked by knowing the
macro names used.  The compiler  names are the macros  AS, CC, YACC,  QS,
and LEX.  The command

     make CC=newcc

will cause the 'newcc'  command to be  used instead of  the usual C  com-
piler.  The macros CFLAGS, QFLAGS, YFLAGS, and LFLAGS may be set to cause
these commands to be issued with optional flags.  Thus,

     make "CFLAGS=-O"

causes the optimizing C compiler to be used.




7.    EXAMPLE

As an example of the  use of make, we will  present the description  file
that maintains the make command itself.  The code for make is spread over
several C source files  and a  yacc grammar.  The  description file  con-
tains:

-------------------------------- Page 10 --------------------------------

#
# Description file for the "make" command
#

OBJECTS = main.o doname.o misc.o files.o dosys.o gram.o
LIBES=
LINT = lint -pxa
CC = cc
CFLAGS = -s -n

make:  $(OBJECTS)
        $(CC) $(CFLAGS) $(OBJECTS) $(LIBES) -o make


gram.y : s.gram.y
        get s.gram.y
gram.c : gram.y
        yacc gram.y ; mv y.tab.c gram.c
gram.o : gram.c defs
        $(CC) -c gram.c

main.c : s.main.c
        get s.main.c
main.o : main.c defs
        $(CC) -c main.c

doname.c : s.doname.c
        get s.doname.c
doname.o : doname.c defs
        $(CC) -c doname.c

misc.c : s.misc.c
        get s.misc.c
misc.o : misc.c defs
        $(CC) -c misc.c

files.c : s.files.c
        get s.files.c
files.o : files.c defs
        $(CC) -c files.c

dosys.c : s.dosys.c
        get s.dosys.c
dosys.o : dosys.c defs
        $(CC) -c dosys.c


install : make
        mv make /usr/bin/make

-------------------------------- Page 11 --------------------------------

        ls -l /usr/bin/make

clean:
        -rm *.o gram.c
        -sc *

lint :  dosys.c doname.c files.c main.c misc.c ident.c gram.c
        $(LINT) dosys.c doname.c files.c main.c misc.c ident.c gram.c
        rm gram.c

Make usually prints out  each command before  issuing it.  The  following
output results from typing the simple command

     make

in a directory containing only the SCCS source and the description file:

     get s.main.c
     cc  -c main.c
     get s.doname.c
     cc  -c doname.c
     get s.misc.c
     cc  -c misc.c
     get s.files.c
     cc  -c files.c
     get s.dosys.c
     cc  -c dosys.c
     get s.gram.y
     yacc  gram.y; mv y.tab.c gram.c
     cc  -c gram.c
     cc  -s -n main.o doname.o misc.o files.o dosys.o gram.o -o make

It is not necessary to include all  the file names and explicit  commands
as make can find  them by using its suffix  rules and then it will  issue
the needed commands.  However,  it is a  good idea to  be as explicit  as
possible with dependencies and commands.

The last  few entries  in  the description  file are  useful  maintenance
sequences.  The  'install'  entry  moves  the just  made  make  into  the
/usr/bin directory and gives  a long  listing of it.   The 'clean'  entry
cleans up  all files no  longer needed.   They can be  invoked by  saying
'make install' and 'make clean'.

-------------------------------- Page 12 --------------------------------

8.    SUGGESTIONS AND WARNINGS

The most common difficulties arise from make's specific meaning of depen-
dency.  If file x.c  has a '#include  "defs"' line, then the object  file
x.o depends on defs; the source file x.c does not.  (If defs is  changed,
it is not necessary to do anything to the file x.c, while it is necessary
to re-create x.o.)

To discover what make would do, the '-n' option is useful.  The command

     make -n

orders make to print out the commands  it would issue without taking  the
time to execute them.  If a change to a file  is absolutely certain to be
benign (e.g.,  adding a  new definition  to an  include file),  the  '-t'
(touch) option can save much  time: instead of issuing a large number  of
superfluous recompilations, make  updates the modification  times on  the
affected file.  Thus, the command

     make -ts

('touch silently') causes the relevant files to appear up to date.  Obvi-
ous care is necessary, since  this mode of operation subverts the  inten-
tion of make and destroys all memory of the previous relationships.

The debugging flag ('-d') causes make to print out a detailed description
of what it is  doing, including the  file times.  The output is  verbose,
and recommended only as a last resort.

The trace flag ('-z'), causes make to print its analysis of the  descrip-
tion file.  It primarily notes implicit and explicit dependencies,  nest-
ing, and implicit and explicit command executions.




9.    SCCS FILES

When there exist dependencies on SCCS files, one would ideally like  make
to do  "gets", do  the  necessary compiles  and other  actions, and  then
remove the "gotten" file.  However, because of make's current prefix  and
suffix limitations, such capabilities are not entirely available.  Make's
SCCS capabilities are limited.   It is recommended  that dependencies  be
kept simple; use of explicit commands is encouraged.  One-step  transfor-
mation path "gets" can be handled implicitly, however.

-------------------------------- Page 13 --------------------------------

Specifically, try such constructions as:

                   frog.o:  frog.c
                   frog.c:  s.frog.c


The following will not work correctly:

                   frog.o:  s.frog.c
          or
                   frog.o:  frog.c

                            where frog.c does not exist
                            and s.frog.c does




10.   ACKNOWLEDGMENTS

I would like to thank S. C. Johnson for suggesting this approach to  pro-
gram maintenance control.   I would like  to thank S.  C. Johnson and  H.
Gajewska for being the prime guinea pigs during development of make.




11.   REFERENCES

 [1]  S. C. Johnson, Yacc -- Yet Another Compiler-Compiler,

 [2]  M. E. Lesk, Lex -- A Lexical Analyzer Generator.

-------------------------------- Page 14 --------------------------------

APPENDIX A.    SUFFIXES AND TRANSFORMATION RULES

The make  program  itself does  not  know what  file  name  suffixes  are
interesting or how to transform  a file with one suffix into a file  with
another suffix.  This information is stored in an internal table that has
the form of a description file.  If the '-r' flag  is used, this table is
not used.

The list of suffixes is  really the dependency list  for the name  '.SUF-
FIXES'; make looks for a  file with any of the suffixes on the list.   If
such a file exists, and if there is a transformation rule for that combi-
nation, make acts  as described earlier.   The transformation rule  names
are the  concatenation of  the two  suffixes.  The  name of  the rule  to
transform a '.r'  file to  a '.o' file  is thus '.r.o'.   If the rule  is
present and no explicit  command sequence  has been given  in the  user's
description files, the command sequence for the rule '.r.o' is  used.  If
a command is generated by using one  of these suffixing rules, the  macro
$* is given the value of the stem (everything but the suffix) of the name
of the file to be  made, and the macro  $< is the  name of the  dependent
that caused the action.

The order of the  suffix list is  significant, since it  is scanned  from
left to right, and the first name that is formed that has both a file and
a rule associated with it is used.  If new names are to be appended,  the
user can just add an  entry for '.SUFFIXES' in his own description  file;
the dependents  will be  added to  the usual  list.  A  '.SUFFIXES'  line
without any dependents  deletes the  current list.  (It  is necessary  to
clear the current list if the order of names is to be changed.)

The following is an excerpt from the default rules file:

           .SUFFIXES : .o .q .y .l .c .s .h .p .d .t
           YACC=yacc
           YFLAGS=
           LEX=lex
           LFLAGS=
           CC=cc
           AS=as
           CFLAGS=
           .c.o :
                  $(CC) $(CFLAGS) -c $<
           .s.o :
                  $(AS) -o $@ $<
           .y.o :
                  $(YACC) $(YFLAGS) $<
                  $(CC) $(CFLAGS) -c y.tab.c
                  rm y.tab.c
                  mv y.tab.o $@
           .y.c :

-------------------------------- Page 15 --------------------------------

                  $(YACC) $(YFLAGS) $<
                  mv y.tab.c $@
