





   


                        MMaakkee ---- AA TTuuttoorriiaall


                           _A_d_a_m _d_e _B_o_o_r
                        Berkeley Softworks
                   2150 Shattuck Ave, Penthouse
                        Berkeley, CA 94704
                         adam@bsw.uu.net
                        ...!uunet!bsw!adam



   11..  IInnttrroodduuccttiioonn
   This  is a historic document describing PMake, the predeces-
   sor of BSD and MirOS make, NetBSD(TM) nmake and some others.
   Please  keep  this in mind while reading the following docu-
   mentation.  In case of doubt please consider the manual page
   for your make executable.
   Make  is  a program for creating other programs, or anything
   else you can think of for it to do.  The basic  idea  behind
   Make  is  that,  for  any given system, be it a program or a
   document or whatever, there will be some files  that  depend
   on  the  state  of other files (on when they were last modi-
   fied). Make takes these dependencies, which you  must  spec-
   ify,  and  uses  them to build whatever it is you want it to
   build.
   OpenBSD's Make is based upon PMake, a parallel  make  origi-
   nally  developed for the distributed operating system called
   Sprite. PMake departs from usual Make practices  in  several
   ways.  A  large number of those quirks are not relevant in a
   modern POSIX world, and hence development of OpenBSD's  make
   has aimed at removing unwanted differences.  Useful features
   of OpenBSD's Make which are  not  POSIX  compliant  will  be
   flagged  with  a  little sign in the left margin, like this:
   Also note that this  tutorial  was  originally  written  for
   PMake, and hence may not be totally accurate.
   This  tutorial  is  divided  into three main sections corre-
   sponding to basic, intermediate and advanced Make usage.  If
   you already know Make well, you will only need to skim chap-
   ter 2.  Things in chapter 3 make  life  much  easier,  while
   those in chapter 4 are strictly for those who know what they
   -----------
   Permission  to  use,  copy, modify, and distribute
   this software and its documentation for  any  pur-
   pose  and  without fee is hereby granted, provided
   that the above copyright  notice  appears  in  all
   copies.   The  University  of California, Berkeley
   Softworks, and Adam de Boor  make  no  representa-
   tions  about  the suitability of this software for
   any purpose.   It  is  provided  "as  is"  without
   express or implied warranty.









   PSD:12-2                                  Make -- A Tutorial


   are doing. Chapter 5 has definitions for the  jargon  I  use
   and  chapter  6  contains possible solutions to the problems
   presented throughout the tutorial.

   22..  TThhee BBaassiiccss ooff MMaakkee
   Make takes as input a file that tells a) which files  depend
   on  which other files to be complete and b) what to do about
   files that are ``out-of-date.'' This  file  is  known  as  a
   ``makefile''  and  is usually kept in the top-most directory
   of the system to be built. While you can call  the  makefile
   anything  you want, Make will look for Makefile and makefile
   (in that order) in the current directory if you  don't  tell
   it  otherwise.   To specify a different makefile, use the --ff
   flag (e.g.  ``make -f program.mk'').
   A makefile has four different types of lines in it:
        +o File dependency specifications
        +o Creation commands
        +o Variable assignments
        +o Comments, include statements and  conditional  direc-
          tives
   Any  line  may be continued over multiple lines by ending it
   with a backslash.  The backslash, following newline and  any
   initial whitespace on the following line are compressed into
   a single space before the input line is examined by Make.

   22..11..  DDeeppeennddeennccyy LLiinneess
   As mentioned in the introduction, in any system,  there  are
   dependencies between the files that make up the system.  For
   instance, in a program made up of several C source files and
   one  header  file,  the  C files will need to be re-compiled
   should the header file be changed. For a document of several
   chapters  and  one  macro file, the chapters will need to be
   reprocessed if any of the macros changes.  These are  depen-
   dencies  and  are  specified by means of dependency lines in
   the makefile.
   On a dependency line, there are targets and  sources,  sepa-
   rated  by  a  one-  or  two-character operator.  The targets
   ``depend'' on the sources and are usually created from them.
   Any  number  of  targets  and  sources may be specified on a
   dependency line.  All the targets in the line  are  made  to
   depend  on all the sources.  Targets and sources need not be
   actual files, but every source must be either an actual file
   or  another target in the makefile.  If you run out of room,
   use a backslash at the end of the line to continue onto  the
   next one.
   Any  file  may be a target and any file may be a source, but
   the relationship between the two (or however many) is deter-
   mined  by the ``operator'' that separates them.  Three types
   of operators exist: one specifies that the  datedness  of  a
   target  is  determined  by  the  state of its sources, while
   another specifies other files (the sources) that need to  be
   dealt  with  before  the target can be re-created. The third
   operator is very similar to the first, with  the  additional
   condition  that  the  target  is  out-of-date  if  it has no









   Make -- A Tutorial                                  PSD:12-3


   sources. These operations are represented by the colon,  the
   exclamation  point  and  the double-colon, respectively, and
   are mutually exclusive. Their exact semantics  are  as  fol-
   lows:
   :    If  a colon is used, a target on the line is considered
        to be ``out-of-date'' (and in need of creation) if
        +o any of the sources has been  modified  more  recently
          than the target, or
        +o the target doesn't exist.
        Under  this operation, steps will be taken to re-create
        the target only if it is found  to  be  out-of-date  by
        using these two rules.
   !    If an exclamation point is used, the target will always
        be re-created, but this will not happen  until  all  of
        its  sources have been examined and re-created, if nec-
        essary.
   ::   If a double-colon is used, a target is out-of-date if:
        +o any of the sources has been  modified  more  recently
          than the target, or
        +o the target doesn't exist, or
        +o the target has no sources.
        If  the target is out-of-date according to these rules,
        it will be re-created.  This operator also  does  some-
        thing else to the targets, but I'll go into that in the
        next section (``Shell Commands'').
   Enough words, now for an example. Take that C program I men-
   tioned  earlier.  Say  there are three C files (a.c, b.c and
   c.c) each of which includes the file defs.h.  The  dependen-
   cies between the files could then be expressed as follows:

        program         : a.o b.o c.o
        a.o b.o c.o     : defs.h
        a.o             : a.c
        b.o             : b.c
        c.o             : c.c

   You  may  be wondering at this point, where a.o, b.o and c.o
   came in and why _t_h_e_y depend on defs.h and the C files don't.
   The  reason is quite simple: program cannot be made by link-
   ing together .c files -- it must  be  made  from  .o  files.
   Likewise,  if  you change defs.h, it isn't the .c files that
   need to be re-created, it's the .o files.  If you  think  of
   dependencies in these terms -- which files (targets) need to
   be created from which files (sources) -- you should have  no
   problems.
   An  important  thing  to  notice about the above example, is
   that all the .o files appear as targets  on  more  than  one
   line.  This  is  perfectly  all right: the target is made to
   depend on all the sources mentioned on  all  the  dependency
   lines. E.g.  a.o depends on both defs.h and a.c.
   The  order of the dependency lines in the makefile is impor-
   tant: the first target on the first dependency line  in  the
   makefile  will  be  the  one that gets made if you don't say
   otherwise.  That's why program comes first  in  the  example









   PSD:12-4                                  Make -- A Tutorial


   makefile, above.
   Both  targets  and  sources may contain the standard C-Shell
   wildcard characters ({, }, *, ?, [, and  ]),  but  the  non-
   curly-brace ones may only appear in the final component (the
   file portion) of the target or source. The  characters  mean
   the following things:
   {{}}   These  enclose  a  comma-separated  list of options and
        cause the pattern to be expanded once for each  element
        of  the  list. Each expansion contains a different ele-
        ment. For example, src/{whiffle,beep,fish}.c expands to
        the   three   words   src/whiffle.c,   src/beep.c,  and
        src/fish.c.  These braces may be nested and, unlike the
        other wildcard characters, the resulting words need not
        be actual files.  All  other  wildcard  characters  are
        expanded  using  the  files  that  exist  when  Make is
        started.
   **    This matches zero  or  more  characters  of  any  sort.
        src/*.c will expand to the same three words as above as
        long as src contains those three files  (and  no  other
        files that end in .c).
   ??    Matches any single character.
   [[]]   This  is known as a character class and contains either
        a list of single characters, or a series  of  character
        ranges (a-z, for example means all characters between a
        and z), or both. It matches any single  character  con-
        tained  in the list. E.g.  [A-Za-z] will match all let-
        ters, while [0123456789] will match all numbers.

   22..22..  SShheellll CCoommmmaannddss
   ``Isn't that nice,'' you say  to  yourself,  ``but  how  are
   files actually `re-created,' as he likes to spell it?''  The
   re-creation is accomplished by commands  you  place  in  the
   makefile.   These  commands  are  passed  to the MirBSD Korn
   shell (better known as ``/bin/mksh'') to be executed and are
   expected  to  do  what's necessary to update the target file
   (Make doesn't actually check to see if the target  was  cre-
   ated. It just assumes it's there).
   Shell  commands in a makefile look a lot like shell commands
   you would type at a terminal, with one important  exception:
   each  command in a makefile _m_u_s_t be preceded by at least one
   tab.
   Each target has associated with it a set of one or  more  of
   these  shell  commands.  The  creation  script  for a target
   should immediately follow the dependency line for that  tar-
   get.  While  any  given  target  may appear on more than one
   dependency line, only one of these dependency lines  may  be
   followed  by a creation script, unless the `::' operator was
   used on the dependency line.
   If the double-colon was used, each dependency line  for  the
   target  may be followed by a set of shell commands. This set
   of shell commands will only be executed if the target on the
   associated  dependency  line  is out-of-date with respect to
   the sources on that line, according to the rules I gave ear-
   lier.  I'll give you a good example of this later on.









   Make -- A Tutorial                                  PSD:12-5


   To expand on the earlier makefile, you might add commands as
   follows:

        program         : a.o b.o c.o
                cc a.o b.o c.o -o program
        a.o b.o c.o     : defs.h
        a.o             : a.c
                cc -c a.c
        b.o             : b.c
                cc -c b.c
        c.o             : c.c
                cc -c c.c

   Something you should remember when writing  a  makefile  is,
   the  commands  will  be executed if the _t_a_r_g_e_t on the depen-
   dency line is out-of-date, not the sources.  In  this  exam-
   ple,  the  command  ``cc -c a.c'' will be executed if a.o is
   out-of-date. Because of the `:' operator,  this  means  that
   should  a.c  _o_r defs.h have been modified more recently than
   a.o, the command will be executed (a.o  will  be  considered
   out-of-date).
   Remember  how  I said the only difference between a makefile
   shell command and a regular shell command  was  the  leading
   tab? I lied. There is another way in which makefile commands
   differ from regular ones.  The first  two  characters  after
   the  initial  whitespace are treated specially.  If they are
   any combination of `@' and `-', they cause Make to  do  dif-
   ferent things.
   In  most  cases,  shell  commands are printed before they're
   actually executed. This is to keep you  informed  of  what's
   going  on.  If an `@' appears, however, this echoing is sup-
   pressed. In the case of an echo command, say ``echo  Linking
   index,'' it would be rather silly to see

        echo Linking index
        Linking index

   so  Make  allows  you  to  place  an  `@' before the command
   (``@echo Linking index'') to prevent the command from  being
   printed.
   The  other  special character is the `-'. In case you didn't
   know, shell commands finish with a certain ``exit  status.''
   This  status  is  made  available by the operating system to
   whatever program invoked the command. Normally  this  status
   will  be  0  if everything went ok and non-zero if something
   went wrong. For this reason, Make will consider an error  to
   have occurred if one of the shells it invokes returns a non-
   zero status. When it detects an error, Make's  usual  action
   is  to  abort  whatever  it's doing and exit with a non-zero
   status itself (any other targets  that  were  being  created
   will  continue  being made, but nothing new will be started.
   Make will exit after the last job finishes).  This  behavior
   can  be altered, however, by placing a `-' at the front of a
   command  (``-mv  index  index.old''),  certain  command-line









   PSD:12-6                                  Make -- A Tutorial


   arguments,  or  doing other things, to be detailed later. In
   such a case, the non-zero status is simply ignored and  Make
   keeps chugging along.
   In  Make --jj mode, a set of shell commands attached to a tar-
   get is fed to a shell as a single script.  This  is  experi-
   mental  behavior from PMake's period which hasn't been fixed
   yet.
   Make has a --BB flag (it stands for backwards-compatible) that
   forces  each command to be given to a separate shell. Unfor-
   tunately, it also inhibits --jj.
   A target's shell script is fed to  the  shell  on  its  (the
   shell's)  input  stream.  This means that any commands, such
   as ci that need to get input from the  terminal  won't  work
   right -- they'll get the shell's input, something they prob-
   ably won't find to their liking. A simple way around this is
   to give a command like this:

        ci $(SRCS) < /dev/tty

   This would force the program's input to come from the termi-
   nal. If you can't do this for some reason, your  only  other
   alternative  is  to  use  Make  in its fullest compatibility
   mode. See CCoommppaattiibbiilliittyy in chapter 4.

   22..33..  VVaarriiaabblleess
   Make has the  ability  to  save  text  in  variables  to  be
   recalled  later  at  your convenience. Variables in Make are
   used much like variables in the  shell  and,  by  tradition,
   consist of all upper-case letters (you don't _h_a_v_e to use all
   upper-case letters.  In fact there's  nothing  to  stop  you
   from  calling a variable @^&$%$.  Just tradition). Variables
   are assigned-to using lines of the form

        VARIABLE = value

   appended-to by

        VARIABLE += value

   conditionally assigned-to (if  the  variable  isn't  already
   defined) by

        VARIABLE ?= value

   and  assigned-to  with expansion (i.e. the value is expanded
   (see below) before being assigned  to  the  variable--useful
   for placing a value at the beginning of a variable, or other
   things) by

        VARIABLE := value

   Any whitespace before _v_a_l_u_e is stripped off. When appending,
   a  space is placed between the old value and the stuff being
   appended.









   Make -- A Tutorial                                  PSD:12-7


   The final way a variable may be assigned to is using

        VARIABLE != shell-command

   In this case, _s_h_e_l_l_-_c_o_m_m_a_n_d has all its  variables  expanded
   (see  below)  and  is  passed off to a shell to execute. The
   output of the shell is then placed in the variable. Any new-
   lines  (other  than  the  final  one) are replaced by spaces
   before the assignment is made. This  is  typically  used  to
   find the current directory via a line like:

        CWD             != pwd

   NNoottee:: this command will be invoked each time the Makefile is
   parsed, regardless of whether or not the result  will  actu-
   ally  be used for making targets.  If the end result is only
   needed for shell commands, it is much cheaper to use

        VARIABLE = `shell-command`

   The value of a variable may be retrieved  by  enclosing  the
   variable  name  in parentheses or curly braces and prefixing
   the whole thing with a dollar sign.
   For example, to  set  the  variable  CFLAGS  to  the  string
   ``-I/usr/local/include -O,'' you would place a line

        CFLAGS = -I/usr/local/include -O

   in  the  makefile  and  use  the word $(CFLAGS) wherever you
   would like the string  -I/usr/local/include  -O  to  appear.
   This is called variable expansion.
   To keep Make from substituting for a variable it knows, pre-
   cede the dollar sign with another  dollar  sign.   (e.g.  to
   pass ${HOME} to the shell, use $${HOME}).  This causes Make,
   in effect, to expand the $ macro, which expands to a  single
   $.
   There  are  two  different times at which variable expansion
   occurs: When parsing a dependency line, the expansion occurs
   immediately upon reading the line. If any variable used on a
   dependency line is undefined, Make will print a message  and
   exit.   Variables  in  shell  commands are expanded when the
   command is executed.  Variables used inside another variable
   are  expanded  whenever  the outer variable is expanded (the
   expansion of an inner variable has no effect  on  the  outer
   variable. I.e. if the outer variable is used on a dependency
   line and in a shell command, and the inner variable  changes
   value between when the dependency line is read and the shell
   command is executed, two different values  will  be  substi-
   tuted for the outer variable).
   Variables come in four flavors, though they are all expanded
   the same and all look about the same. They are (in order  of
   expanding scope):
        +o Local variables.










   PSD:12-8                                  Make -- A Tutorial


        +o Command-line variables.
        +o Global variables.
        +o Environment variables.
   The  classification of variables doesn't matter much, except
   that the classes are searched from the top  (local)  to  the
   bottom  (environment)  when looking up a variable. The first
   one found wins.

   22..33..11..  LLooccaall VVaarriiaabblleess
   Each target can have as many as seven local variables. These
   are variables that are only ``visible'' within that target's
   shell commands and contain such things as the target's name,
   all  of  its  sources (from all its dependency lines), those
   sources that were out-of-date,  etc.   POSIX  defines  short
   names  for  these variables, which should be used for porta-
   bility.  OpenBSD's Make has longer synonyms, which  will  be
   used in the rest of this tutorial for clarity.
   Four local variables are defined for all targets. They are:
        .TARGET
             The name of the target (POSIX: @).
        .OODATE
             The  list  of the sources for the target that were
             considered out-of-date.  The order in the list  is
             not  guaranteed  to  be  the  same as the order in
             which the dependencies were given. (POSIX: ?)
        .ALLSRC
             The list of all sources for  this  target  in  the
             order  in  which they were given. (shorter: >, not
             POSIX).
        .PREFIX
             The target without  its  suffix  and  without  any
             leading  path.  E.g. for the target ../../lib/com-
             pat/fsRead.c, this variable would  contain  fsRead
             (POSIX: *) .
   Three other local variables are set only for certain targets
   under special  circumstances.  These  are  the  ``.IMPSRC,''
   ``.ARCHIVE,''  and  ``.MEMBER'' variables. When they are set
   and how they are used is described later.
   Four of these variables may be used in sources as well as in
   shell  commands.  These are ``.TARGET'', ``.PREFIX'', ``.AR-
   CHIVE'' and ``.MEMBER''. The variables in  the  sources  are
   expanded  once  for each target on the dependency line, pro-
   viding what is known as a ``dynamic source,''  allowing  you
   to specify several dependency lines at once. For example,

        $(OBJS)         : $(.PREFIX).c

   will  create  a  dependency between each object file and its
   corresponding C source file.

   22..33..22..  CCoommmmaanndd--lliinnee VVaarriiaabblleess
   Command-line variables are set when Make is first invoked by
   giving  a  variable  assignment as one of the arguments. For
   example,









   Make -- A Tutorial                                  PSD:12-9


        make "CFLAGS = -I/usr/local/include -O"

   would make CFLAGS be a command-line variable with the  given
   value.  Any  assignments to CFLAGS in the makefile will have
   no effect, because once it is set, there is (almost) nothing
   you  can  do  to  change a command-line variable (the search
   order, you see). Command-line variables may be set using any
   of  the  four  assignment  operators,  though  only = and ?=
   behave in a sane way, mostly because assignments to command-
   line  variables  are  performed before the makefile is read,
   thus the values set in the makefile are unavailable  at  the
   time.   +=  is  the  same as =, because the old value of the
   variable is sought only in the scope in which the assignment
   is  taking  place  (you don't want to know).  := and ?= will
   work if the only variables used are in the environment.   !=
   is sort of pointless to use from the command line, since the
   same effect can no doubt be accomplished using  the  shell's
   own  command  substitution  mechanisms  (backquotes  and all
   that).

   22..33..33..  GGlloobbaall VVaarriiaabblleess
   Global variables are those set or appended-to in  the  make-
   file.   There are two classes of global variables: those you
   set and those Make sets.  As I said before, the ones you set
   can have any name you want them to have, except they may not
   contain a colon or an exclamation point.  The variables Make
   sets  (almost) always begin with a period and always contain
   upper-case letters, only. The variables are as follows:
        MAKE The name by which Make was invoked  is  stored  in
             this variable.
        .MAKEFLAGS
             All   the  relevant  flags  with  which  Make  was
             invoked. This does not include such things as  --ff.
   Two  other  variables, ``.INCLUDES'' and ``.LIBS,'' are cov-
   ered in the section on special targets in chapter 3.
   Global variables may be deleted using lines of the form:

        .undef _v_a_r_i_a_b_l_e

   The `.'  must be the first character on the line. Note  that
   this may only be done on global variables.

   22..33..44..  EEnnvviirroonnmmeenntt VVaarriiaabblleess
   Environment  variables  are passed by the shell that invoked
   Make and are given by Make to each shell  it  invokes.  They
   are  expanded  like  any  other variable, but they cannot be
   altered in any way.
   Using all these variables, you can compress the sample make-
   file even more:














   PSD:12-10                                 Make -- A Tutorial


        OBJS            = a.o b.o c.o
        program         : $(OBJS)
                cc $(.ALLSRC) -o $(.TARGET)
        $(OBJS)         : defs.h
        a.o             : a.c
                cc -c a.c
        b.o             : b.c
                cc -c b.c
        c.o             : c.c
                cc -c c.c


   22..44..  CCoommmmeennttss
   Comments in a makefile start with a `#' character and extend
   to the end of the line. They may appear  anywhere  you  want
   them, except in a shell command (though the shell will treat
   it as a comment, too). If, for some reason, you need to  use
   the  `#'  in a variable or on a dependency line, put a back-
   slash in front of it.  Make will compress  the  two  into  a
   single `#'.

   22..55..  PPaarraalllleelliissmm
   PMake was specifically designed to re-create several targets
   at once, when possible, when using the --jj flag (see  below),
   but you do have to be careful at times.
   There  are several problems you are likely to encounter. One
   is that some makefiles (and programs) are written in such  a
   way  that  it  is  impossible  for two targets to be made at
   once. The program xstr, for  example,  always  modifies  the
   files  strings  and x.c.  There is no way to change it. Thus
   you cannot run two of them at once without  something  being
   trashed.  Similarly,  if  you  have commands in the makefile
   that always send output to the same file, you  will  not  be
   able  to make more than one target at once unless you change
   the file you use. You can, for instance, add a $$$$  to  the
   end  of the file name to tack on the process ID of the shell
   executing the command (each $$ expands to a single  $,  thus
   giving you the shell variable $$).
   The  other problem comes from improperly-specified dependen-
   cies that worked in sequential mode.  While I don't want  to
   go into depth on how Make works (look in chapter 4 if you're
   interested), I will warn you that  files  in  two  different
   ``levels''  of the dependency tree may be examined in a dif-
   ferent order in parallel mode than in sequential  mode.  For
   example, given the makefile

        a               : b c
        b               : d

   Make  may  examine  the targets in the order c, d, b, a.  If
   the makefile's author expected Make to abort before making c
   if an error occurred while making b, or if b needed to exist
   before c was made, s/he will  be  sorely  disappointed.  The
   dependencies  are  incomplete,  since in both these cases, c









   Make -- A Tutorial                                 PSD:12-11


   would depend on b.  So watch out.
   Another problem you may face is that, while Make is  set  up
   to  handle the output from multiple jobs in a graceful fash-
   ion, the same is not so for input.  It has no way  to  regu-
   late  input to different jobs, so if you use the redirection
   from /dev/tty I mentioned earlier, you must be  careful  not
   to run two of the jobs at once.

   22..66..  WWrriittiinngg aanndd DDeebbuuggggiinngg aa MMaakkeeffiillee
   Now  you  know  most of what's in a makefile, what do you do
   next? There are two choices: (1) use one of the  uncommonly-
   available makefile generators or (2) write your own makefile
   (I leave out the third choice of  ignoring  Make  and  doing
   everything  by  hand  as  being  beyond the bounds of common
   sense).
   When faced with the writing of a  makefile,  it  is  usually
   best  to start from first principles: just what _a_r_e you try-
   ing to do? What do you want the makefile finally to produce?
   To  begin with a somewhat traditional example, let's say you
   need to write a makefile to create  a  program,  expr,  that
   takes standard infix expressions and converts them to prefix
   form (for no readily  apparent  reason).  You've  got  three
   source  files,  in  C,  that  make  up  the program: main.c,
   parse.c, and output.c.  Harking  back  to  my  pithy  advice
   about  dependency  lines,  you  write  the first line of the
   file:

        expr            : main.o parse.o output.o

   because you remember expr is made  from  .o  files,  not  .c
   files. Similarly for the .o files you produce the lines:

        main.o          : main.c
        parse.o         : parse.c
        output.o        : output.c
        main.o parse.o output.o : defs.h

   Great.  You've  now got the dependencies specified. What you
   need now is commands. These commands, remember, must produce
   the  target  on  the  dependency  line, usually by using the
   sources you've listed.  You remember about local  variables?
   Good, so it should come to you as no surprise when you write

        expr            : main.o parse.o output.o
                cc -o $(.TARGET) $(.ALLSRC)

   Why use the variables? If  your  program  grows  to  produce
   postfix  expressions  too (which, of course, requires a name
   change or two), it is one fewer place you have to change the
   file.  You  cannot  do  this  for the object files, however,
   because they depend on their corresponding source files  _a_n_d
   defs.h, thus if you said











   PSD:12-12                                 Make -- A Tutorial


             cc -c $(.ALLSRC)

   you'd get (for main.o):

             cc -c main.c defs.h

   which  is  wrong.  So  you round out the makefile with these
   lines:

        main.o          : main.c
                cc -c main.c
        parse.o         : parse.c
                cc -c parse.c
        output.o        : output.c
                cc -c output.c

   The makefile is now complete and will, in fact,  create  the
   program  you  want it to without unnecessary compilations or
   excessive typing on your part. There are  two  things  wrong
   with  it,  however (aside from it being altogether too long,
   something I'll address in chapter 3):
   1)   The string  ``main.o  parse.o  output.o''  is  repeated
        twice,  necessitating  two changes when you add postfix
        (you were planning on that, weren't you?). This  is  in
        direct  violation  of  de  Boor's First Rule of writing
        makefiles:
        _A_n_y_t_h_i_n_g _t_h_a_t _n_e_e_d_s _t_o _b_e _w_r_i_t_t_e_n _m_o_r_e  _t_h_a_n  _o_n_c_e
        _s_h_o_u_l_d _b_e _p_l_a_c_e_d _i_n _a _v_a_r_i_a_b_l_e_.
        I  cannot emphasize this enough as being very important
        to the maintenance of a makefile and its program.
   2)   There is no way to alter the way compilations are  per-
        formed  short  of  editing  the makefile and making the
        change in all places. This  is  evil  and  violates  de
        Boor's  Second  Rule,  which  follows directly from the
        first:
        _A_n_y _f_l_a_g_s  _o_r  _p_r_o_g_r_a_m_s  _u_s_e_d  _i_n_s_i_d_e  _a  _m_a_k_e_f_i_l_e
        _s_h_o_u_l_d  _b_e  _p_l_a_c_e_d  _i_n  _a  _v_a_r_i_a_b_l_e _s_o _t_h_e_y _m_a_y _b_e
        _c_h_a_n_g_e_d_,  _t_e_m_p_o_r_a_r_i_l_y  _o_r  _p_e_r_m_a_n_e_n_t_l_y_,  _w_i_t_h  _t_h_e
        _g_r_e_a_t_e_s_t _e_a_s_e_.
   The makefile should more properly read:

        OBJS            = main.o parse.o output.o
        expr            : $(OBJS)
                $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
        main.o          : main.c
                $(CC) $(CFLAGS) -c main.c
        parse.o         : parse.c
                $(CC) $(CFLAGS) -c parse.c
        output.o        : output.c
                $(CC) $(CFLAGS) -c output.c
        $(OBJS)         : defs.h

   Alternatively,  if you like the idea of dynamic sources men-
   tioned in section 2.3.1, you could write it like this:









   Make -- A Tutorial                                 PSD:12-13


        OBJS            = main.o parse.o output.o
        expr            : $(OBJS)
                $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
        $(OBJS)         : $(.PREFIX).c defs.h
                $(CC) $(CFLAGS) -c $(.PREFIX).c

   These two rules and examples lead to de Boor's First  Corol-
   lary:
        _V_a_r_i_a_b_l_e_s _a_r_e _y_o_u_r _f_r_i_e_n_d_s_.
   Once  you've written the makefile comes the sometimes-diffi-
   cult task of making sure the darn  thing  works.  Your  most
   helpful tool to make sure the makefile is at least syntacti-
   cally correct is the --nn flag, which allows  you  to  see  if
   Make  will  choke  on  the makefile. The second thing the --nn
   flag lets you do is see what Make would do without it  actu-
   ally  doing  it,  thus  you can make sure the right commands
   would be executed were you to give Make its head.
   When you find your makefile isn't behaving as you hoped, the
   first  question that comes to mind (after ``What time is it,
   anyway?'') is ``Why not?'' In answering this, one flag  will
   serve  you  well: ``-d m.''  This causes Make to tell you as
   it examines each target in the makefile and indicate why  it
   is  deciding  whatever  it is deciding. You can then use the
   information printed for other targets to see where you  went
   wrong.
   Something  to be especially careful about is circular depen-
   dencies.  E.g.

        a         : b
        b         : c d
        d         : a

   In this case, because of the way Make works, c is  the  only
   thing  Make  will  examine, because d and a will effectively
   fall off the edge of the universe, making it  impossible  to
   examine  b  (or  them, for that matter).  Make will tell you
   (if run in its normal mode) all the targets involved in  any
   cycle it looked at (i.e. if you have two cycles in the graph
   (naughty, naughty), but only try to make a target in one  of
   them, Make will only tell you about that one. You'll have to
   try to make the other to find the second cycle). When run as
   Make, it will only print the first target in the cycle.

   22..77..  IInnvvookkiinngg MMaakkee
   Make  comes  with  a  wide  variety of flags to choose from.
   They may appear in any order, interspersed with command-line
   variable  assignments  and targets to create.  Some of these
   flags are as follows:
   --dd _w_h_a_t
        This causes Make to spew out debugging information that
        may  prove  useful  to you. If you can't figure out why
        Make is doing what it's doing, you might try using this
        flag.  The _w_h_a_t parameter is a string of single charac-
        ters that tell Make what aspects you are interested in.









   PSD:12-14                                 Make -- A Tutorial


        Most  of what I describe will make little sense to you,
        unless you've dealt with  Make  before.  Just  remember
        where this table is and come back to it as you read on.
        The characters and the information they produce are  as
        follows:
        a    Archive searching and caching.
        c    Conditional evaluation.
        d    The searching and caching of directories.
        j    Various  snippets  of  information  related to the
             running of the multiple shells.  Not  particularly
             interesting.
        m    The  making  of  each target: what target is being
             examined; when it was last modified; whether it is
             out-of-date; etc.
        p    Makefile parsing.
        r    Remote execution.
        s    The  application  of  suffix-transformation rules.
             (See chapter 3)
        t    The maintenance of the list of targets.
        v    Variable assignment.
        Of these all, the m and s letters will be  most  useful
        to  you.   If the --dd is the final argument or the argu-
        ment from which it would get  these  key  letters  (see
        below  for  a  note about which argument would be used)
        begins with a --, all of these debugging flags  will  be
        set, resulting in massive amounts of output.
   --ff _m_a_k_e_f_i_l_e
        Specify  a makefile to read different from the standard
        makefiles  (Makefile  or  makefile).   If  _m_a_k_e_f_i_l_e  is
        ``-'', Make uses the standard input. This is useful for
        making quick and dirty makefiles...
   --ii   If you give this flag, Make will ignore non-zero status
        returned  by any of its shells. It's like placing a `-'
        before all the commands in the makefile.
   --kk   This is similar to --ii in that it allows  Make  to  con-
        tinue  when it sees an error, but unlike --ii, where Make
        continues blithely as if nothing went wrong, --kk  causes
        it  to  recognize  the  error and only continue work on
        those things that don't depend on  the  target,  either
        directly  or indirectly (through depending on something
        that depends on it), whose creation returned the error.
        The `k' is for ``keep going''...
   --mm _d_i_r_e_c_t_o_r_y
        Tells  Make  another place to search for included make-
        files via the <...> style.  Several --mm options  can  be
        given to form a search path.  If this construct is used
        the default system makefile search path  is  completely
        overridden.  To be explained in chapter 3, section 3.2.
   --nn   This flag tells Make not to execute the commands needed
        to  update  the  out-of-date  targets  in the makefile.
        Rather, Make will simply print the  commands  it  would
        have executed and exit. This is particularly useful for
        checking the correctness of a makefile. If Make doesn't
        do  what  you  expect  it  to,  it's  a good chance the









   Make -- A Tutorial                                 PSD:12-15


        makefile is wrong.
   --qq   If you give Make this flag, it will not try to  re-cre-
        ate  anything.  It will just see if anything is out-of-
        date and exit non-zero if so.
   --rr   When Make starts up, it reads a default  makefile  that
        tells  it what sort of system it's on and gives it some
        idea of what to do if you don't tell it anything.  I'll
        tell  you about it in chapter 3. If you give this flag,
        Make won't read the default makefile.
   --ss   This causes Make to not print commands  before  they're
        executed. It is the equivalent of putting an `@' before
        every command in the makefile.
   --tt   Rather than try to re-create a target, Make will simply
        ``touch'' it so as to make it appear up-to-date. If the
        target didn't exist before, it will when Make finishes,
        but  if  the  target  did exist, it will appear to have
        been updated.
   --BB   Forces OpenBSD Make to be as POSIX-compatible as possi-
        ble.  This includes:
        +o Executing one shell per shell command
        +o Using sequential mode.
   --DD _v_a_r_i_a_b_l_e
        Allows  you  to  define a variable to have ``1'' as its
        value.  The variable is a global variable, not  a  com-
        mand-line  variable.  This  is useful mostly for people
        who are used to the  C  compiler  arguments  and  those
        using conditionals, which I'll get into in section 4.3
   --II _d_i_r_e_c_t_o_r_y
        Tells  Make  another place to search for included make-
        files. Yet another thing to be explained in  chapter  3
        (section 3.2, to be precise).
   --PP   When  creating  targets in parallel, several shells are
        executing at once, each wanting to write  its  own  two
        cent's-worth  to  the screen.  This output must be cap-
        tured by Make in some  way  in  order  to  prevent  the
        screen from being filled with garbage even more indeci-
        pherable than you usually see. Make  has  two  ways  of
        doing this, one of which provides for much cleaner out-
        put and a clear separation between the output  of  dif-
        ferent jobs, the other of which provides a more immedi-
        ate response so one can tell what is really  happening.
        The  former  is done by notifying you when the creation
        of a target starts, capturing the output and  transfer-
        ring  it  to  the  screen all at once when the job fin-
        ishes. The latter is done by catching the output of the
        shell  (and  its  children)  and  buffering it until an
        entire line is received, then printing that  line  pre-
        ceded  by  an indication of which job produced the out-
        put. Since I prefer this second method, it is  the  one
        used  by  default. The first method will be used if you
        give the --PP flag to Make.
   Flags without arguments may follow a single `-'.  E.g.











   PSD:12-16                                 Make -- A Tutorial


        make -f server.mk -DDEBUG -I/chip2/X/server/include -n

   will cause Make to read server.mk  as  the  input  makefile,
   define  the variable DEBUG as a global variable and look for
   included makefiles in the directory /chip2/X/server/include.

   22..88..  SSuummmmaarryy
   A makefile is made of four types of lines:
        +o Dependency lines
        +o Creation commands
        +o Variable assignments
        +o Comments,  include  statements and conditional direc-
          tives
   A dependency line is a list of one or more targets, an oper-
   ator  (`:',  `::',  or  `!'),  and  a  list  of zero or more
   sources. Sources may contain  wildcards  and  certain  local
   variables.
   A  creation command is a regular shell command preceded by a
   tab. In addition, if the first two characters after the  tab
   (and other whitespace) are a combination of `@' or `-', Make
   will cause the command to not be printed (if  the  character
   is  `@')  or errors from it to be ignored (if `-').  A blank
   line, dependency line or variable  assignment  terminates  a
   creation  script.  There may be only one creation script for
   each target with a `:' or `!'  operator.
   Variables are places to store text. They may be uncondition-
   ally  assigned-to  using the `=' operator, appended-to using
   the `+=' operator, conditionally (if the variable  is  unde-
   fined)  assigned-to  with the `?=' operator, and assigned-to
   with variable expansion with the `:=' operator.  The  output
   of  a  shell command may be assigned to a variable using the
   `!=' operator.   Variables  may  be  expanded  (their  value
   inserted)  by  enclosing  their name in parentheses or curly
   braces, preceded by a dollar sign.  A  dollar  sign  may  be
   escaped with another dollar sign. Variables are not expanded
   if Make doesn't know about them. There are seven local vari-
   ables:  .TARGET,  .ALLSRC,  .OODATE,  .PREFIX, .IMPSRC, .AR-
   CHIVE, and .MEMBER.  Four of them  (.TARGET,  .PREFIX,  .AR-
   CHIVE,  and  .MEMBER)  may  be  used  to  specify  ``dynamic
   sources.''  Variables are good. Know them. Love  them.  Live
   them.
   Debugging  of  makefiles  is best accomplished using the --nn,
   and --dd mm flags.

   22..99..  EExxeerrcciisseess
                               TTBBAA

   33..  SShhoorrtt--ccuuttss aanndd OOtthheerr NNiiccee TThhiinnggss
   Based on what I've told you so far, you may have gotten  the
   impression  that Make is just a way of storing away commands
   and making sure you don't forget to compile something. Good.
   That's  just  what  it is.  However, the ways I've described
   have been inelegant, at best, and painful, at  worst.   This
   chapter  contains  things that make the writing of makefiles









   Make -- A Tutorial                                 PSD:12-17


   easier and the makefiles themselves shorter  and  easier  to
   modify  (and,  occasionally,  simpler).  In  this chapter, I
   assume you are somewhat more familiar with Unix than  I  did
   in  chapter 2, just so you're on your toes.  So without fur-
   ther ado...

   33..11..  TTrraannssffoorrmmaattiioonn RRuulleess
   As you know, a file's name consists of  two  parts:  a  base
   name,  which gives some hint as to the contents of the file,
   and a suffix, which usually  indicates  the  format  of  the
   file.  Over the years, as UNIX(R) has developed, naming con-
   ventions, with regard to suffixes, have also developed  that
   have  become  almost as incontrovertible as Law. E.g. a file
   ending in .c is assumed to contain C source code; one with a
   .o  suffix  is assumed to be a compiled object file that may
   be linked into any program; a file with a .ms suffix is usu-
   ally a text file to be processed by Troff with the -ms macro
   package, and so on.  One of the best aspects of  Make  comes
   from  its understanding of how the suffix of a file pertains
   to its contents and Make's ability to do things with a  file
   based  solely  on  its suffix. This ability comes from some-
   thing known as a transformation rule. A transformation  rule
   specifies  how  to change a file with one suffix into a file
   with another suffix.
   A transformation rule looks much  like  a  dependency  line,
   except  the  target  is  made  of  two  known suffixes stuck
   together. Suffixes are made known to Make by placing them as
   sources  on  a  dependency  line whose target is the special
   target .SUFFIXES.  E.g.

        .SUFFIXES       : .o .c
        .c.o            :
                $(CC) $(CFLAGS) -c $(.IMPSRC)

   The creation script attached to the target is used to trans-
   form  a file with the first suffix (in this case, .c) into a
   file with the second suffix (here, .o).   In  addition,  the
   target inherits whatever attributes have been applied to the
   transformation rule.  The simple rule given above says  that
   to  transform  a C source file into an object file, you com-
   pile it using cc with the  -c  flag.   This  rule  is  taken
   straight from the system makefile. Many transformation rules
   (and suffixes) are defined there, and I refer you to it  for
   more examples.
   There  are  several  things to note about the transformation
   rule given above:
        1)   The .IMPSRC variable.  This variable is set to the
             ``implied source'' (the file from which the target
             is being created; the one with the first  suffix),
             which, in this case, is the .c file.
        2)   The CFLAGS variable. Almost all of the transforma-
             tion rules in the system makefile are set up using
             variables  that  you can alter in your makefile to
             tailor the rule to your needs. In  this  case,  if









   PSD:12-18                                 Make -- A Tutorial


             you  want all your C files to be compiled with the
             --gg flag, to provide information for dbx, you would
             set  the CFLAGS variable to contain -g (``CFLAGS =
             -g'') and Make would take care of the rest.
   To give you a quick example, the makefile in 2.3.4 could  be
   changed to this:

        OBJS            = a.o b.o c.o
        program         : $(OBJS)
                $(CC) -o $(.TARGET) $(.ALLSRC)
        $(OBJS)         : defs.h

   The  transformation rule I gave above takes the place of the
   6 lines1

        a.o             : a.c
                cc -c a.c
        b.o             : b.c
                cc -c b.c
        c.o             : c.c
                cc -c c.c

   Now you may be wondering about the dependency between the .o
   and .c files -- it's not mentioned anywhere in the new make-
   file. This is because it isn't needed: one of the effects of
   applying a transformation rule is the target comes to depend
   on  the  implied  source. That's why it's called the implied
   _s_o_u_r_c_e.
   For a more detailed example. Say you have  a  makefile  like
   this:

        a.out           : a.o b.o
                $(CC) $(.ALLSRC)

   and a directory set up like this:

        total 4
        -rw-rw-r--  1 deboor         34 Sep  7 00:43 Makefile
        -rw-rw-r--  1 deboor        119 Oct  3 19:39 a.c
        -rw-rw-r--  1 deboor        201 Sep  7 00:43 a.o
        -rw-rw-r--  1 deboor         69 Sep  7 00:43 b.c

   While  just  typing  ``make''  will do the right thing, it's
   much more informative to type ``make -d s''.  This will show
   you  what  Make  is up to as it processes the files. In this
   case, Make prints the following:





   -----------
     1 This is also somewhat cleaner, I  think,  than
   the dynamic source solution presented in 2.6









   Make -- A Tutorial                                 PSD:12-19


        Suff_FindDeps (a.out)
             using existing source a.o
             applying .o -> .out to "a.o"
        Suff_FindDeps (a.o)
             trying a.c...got it
             applying .c -> .o to "a.c"
        Suff_FindDeps (b.o)
             trying b.c...got it
             applying .c -> .o to "b.c"
        Suff_FindDeps (a.c)
             trying a.y...not there
             trying a.l...not there
             trying a.c,v...not there
             trying a.y,v...not there
             trying a.l,v...not there
        Suff_FindDeps (b.c)
             trying b.y...not there
             trying b.l...not there
             trying b.c,v...not there
             trying b.y,v...not there
             trying b.l,v...not there
        --- a.o ---
        cc  -c a.c
        --- b.o ---
        cc  -c b.c
        --- a.out ---
        cc a.o b.o

   Suff_FindDeps is the name of a  function  in  Make  that  is
   called  to  check  for  implied  sources  for a target using
   transformation rules.  The  transformations  it  tries  are,
   naturally enough, limited to the ones that have been defined
   (a transformation may be defined multiple times, by the way,
   but only the most recent one will be used). You will notice,
   however, that there is a definite order to the suffixes that
   are  tried.  This  order is set by the relative positions of
   the suffixes on the .SUFFIXES line -- the earlier  a  suffix
   appears, the earlier it is checked as the source of a trans-
   formation. Once a suffix has been defined, the only  way  to
   change  its  position  in the pecking order is to remove all
   the suffixes (by having a .SUFFIXES dependency line with  no
   sources)  and  redefine  them in the order you want. (Previ-
   ously-defined transformation  rules  will  be  automatically
   redefined as the suffixes they involve are re-entered.)
   Another way to affect the search order is to make the depen-
   dency explicit. In the above example, a.out depends  on  a.o
   and  b.o.   Since  a  transformation exists from .o to .out,
   Make uses that, as indicated by the ``using existing  source
   a.o'' message.
   The  search  for  a transformation starts from the suffix of
   the target and continues through all the defined transforma-
   tions, in the order dictated by the suffix ranking, until an
   existing file with the same base (the target name minus  the
   suffix and any leading directories) is found. At that point,









   PSD:12-20                                 Make -- A Tutorial


   one or more transformation rules will  have  been  found  to
   change the one existing file into the target.
   For example, ignoring what's in the system makefile for now,
   say you have a makefile like this:

        .SUFFIXES       : .out .o .c .y .l
        .l.c            :
                lex $(.IMPSRC)
                mv lex.yy.c $(.TARGET)
        .y.c            :
                yacc $(.IMPSRC)
                mv y.tab.c $(.TARGET)
        .c.o            :
                cc -c $(.IMPSRC)
        .o.out          :
                cc -o $(.TARGET) $(.IMPSRC)

   and the single file jive.l.  If you were to type ``make  -rd
   ms  jive.out,''  you  would  get  the  following  output for
   jive.out:

        Suff_FindDeps (jive.out)
             trying jive.o...not there
             trying jive.c...not there
             trying jive.y...not there
             trying jive.l...got it
             applying .l -> .c to "jive.l"
             applying .c -> .o to "jive.c"
             applying .o -> .out to "jive.o"

   and this is why: Make starts with the target jive.out,  fig-
   ures  out  its  suffix  (.out)  and  looks for things it can
   transform to a .out file. In this case, it only finds .o, so
   it  looks  for  the file jive.o.  It fails to find it, so it
   looks for transformations into a .o file. Again it has  only
   one  choice:  .c.   So it looks for jive.c and, as you know,
   fails to find it. At this point it has two choices:  it  can
   create the .c file from either a .y file or a .l file. Since
   .y came first on the .SUFFIXES line, it  checks  for  jive.y
   first, but can't find it, so it looks for jive.l and, lo and
   behold, there it is.  At this point, it has defined a trans-
   formation  path  as  follows:  .l  ->  .c  -> .o -> .out and
   applies the transformation rules accordingly. For  complete-
   ness,  and  to  give you a better idea of what Make actually
   did with this three-step transformation, this is  what  Make
   printed for the rest of the process:

















   Make -- A Tutorial                                 PSD:12-21


        Suff_FindDeps (jive.o)
             using existing source jive.c
             applying .c -> .o to "jive.c"
        Suff_FindDeps (jive.c)
             using existing source jive.l
             applying .l -> .c to "jive.l"
        Suff_FindDeps (jive.l)
        Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
        Examining jive.c...non-existent...out-of-date
        --- jive.c ---
        lex jive.l
        ... meaningless lex output deleted ...
        mv lex.yy.c jive.c
        Examining jive.o...non-existent...out-of-date
        --- jive.o ---
        cc -c jive.c
        Examining jive.out...non-existent...out-of-date
        --- jive.out ---
        cc -o jive.out jive.o

   One  final  question remains: what does Make do with targets
   that have no known suffix? Make simply pretends it  actually
   has an empty suffix and searches for transformations accord-
   ingly. Those special transformation rules involve  just  one
   source suffix, like this:

        .o        :
             cc -o $(.TARGET) $(.IMPSRC)


   33..22..  IInncclluuddiinngg OOtthheerr MMaakkeeffiilleess
   Just  as for programs, it is often useful to extract certain
   parts of a makefile into another file and just include it in
   other  makefiles somehow. Many compilers allow you say some-
   thing like

        #include "defs.h"

   to include the contents of defs.h in the source  file.  Make
   allows  you  to  do  the  same thing for makefiles, with the
   added ability to use variables in the filenames.  An include
   directive in a makefile looks either like this:

        .include <file>

   or this

        .include "file"

   The  difference  between  the two is where Make searches for
   the file: the first way, Make will look for the file only in
   the  system  makefile  directory (or directories) The system
   makefile directory search path can be overridden via the  --mm
   option.   For  files  in  double-quotes,  the search is more









   PSD:12-22                                 Make -- A Tutorial


   complex:
        1)   The directory of the makefile that's including the
             file.
        2)   The  current  directory  (the  one  in  which  you
             invoked Make).
        3)   The directories given by you using  --II  flags,  in
             the order in which you gave them.
        4)   Directories  given  by .PATH dependency lines (see
             chapter 4).
        5)   The system makefile directory.
   in that order.
   You are free to use Make  variables  in  the  filename--Make
   will  expand  them  before  searching for the file. You must
   specify the searching method with either angle  brackets  or
   double-quotes _o_u_t_s_i_d_e of a variable expansion. I.e. the fol-
   lowing

        SYSTEM    = <command.mk>

        #include $(SYSTEM)

   won't work.

   33..33..  SSaavviinngg CCoommmmaannddss
   There may come a time when you will  want  to  save  certain
   commands  to  be  executed when everything else is done. For
   instance: you're making several different libraries  at  one
   time and you want to create the members in parallel. Problem
   is, ranlib is another one of those programs  that  can't  be
   run  more  than  once in the same directory at the same time
   (each one creates a file  called  __.SYMDEF  into  which  it
   stuffs  information  for the linker to use. Two of them run-
   ning at once will overwrite each other's file and the result
   will  be  garbage for both parties). You might want a way to
   save the ranlib commands til the end so they can be run  one
   after  the  other,  thus  keeping  them  from  trashing each
   other's file. Make allows you to do  this  by  inserting  an
   ellipsis  (``...'')  as a command between commands to be run
   at once and those to be run later.
   So for the ranlib case above, you might do this:

        lib1.a          : $(LIB1OBJS)
                rm -f $(.TARGET)
                ar cr $(.TARGET) $(.ALLSRC)
                ...
                ranlib $(.TARGET)

        lib2.a          : $(LIB2OBJS)
                rm -f $(.TARGET)
                ar cr $(.TARGET) $(.ALLSRC)
                ...
                ranlib $(.TARGET)

   This would save both









   Make -- A Tutorial                                 PSD:12-23


        ranlib $(.TARGET)

   commands until the end, when they would run  one  after  the
   other  (using the correct value for the .TARGET variable, of
   course).
   Commands saved in this manner are only executed if Make man-
   ages to re-create everything without an error.

   33..44..  TTaarrggeett AAttttrriibbuutteess
   Make  allows  you  to give attributes to targets by means of
   special sources.  Like  everything  else  Make  uses,  these
   sources  begin  with  a period and are made up of all upper-
   case letters. There are various reasons for using them,  and
   I  will try to give examples for most of them. Others you'll
   have to find uses for yourself. Think of it as ``an exercise
   for  the  reader.''  By  placing one (or more) of these as a
   source on a dependency line, you are ``marking the target(s)
   with  that  attribute.'' That's just the way I phrase it, so
   you know.
   Any attributes given as sources for  a  transformation  rule
   are  applied  to  the target of the transformation rule when
   the rule is applied.
   .DONTCARE   If a target is marked with  this  attribute  and
               Make  can't figure out how to create it, it will
               ignore this  fact  and  assume  the  file  isn't
               really  needed  or actually exists and Make just
               can't find it. This may  prove  wrong,  but  the
               error  will  be  noted  later  on, not when Make
               tries to  create  the  target  so  marked.  This
               attribute  also prevents Make from attempting to
               touch the target if it is given the --tt flag.
   .EXEC       This attribute causes its  shell  script  to  be
               executed  while having no effect on targets that
               depend on it. This makes the target into a  sort
               of  subroutine.   An  example. Say you have some
               LISP files that need to be compiled  and  loaded
               into  a  LISP process. To do this, you echo LISP
               commands into a file and  execute  a  LISP  with
               this  file  as its input when everything's done.
               Say also that you have to load other files  from
               another system before you can compile your files
               and further, that you don't want to  go  through
               the loading and dumping unless one of _y_o_u_r files
               has changed. Your makefile might look  a  little
               bit  like this (remember, this is an educational
               example, and don't worry about the COMPILE rule,
               all will soon become clear, grasshopper):
















   PSD:12-24                                 Make -- A Tutorial


                    system          : init a.fasl b.fasl c.fasl
                            for i in $(.ALLSRC);
                            do
                                    echo -n '(load "' >> input
                                    echo -n ${i} >> input
                                    echo '")' >> input
                            done
                            echo '(dump "$(.TARGET)")' >> input
                            lisp < input

                    a.fasl          : a.l init COMPILE
                    b.fasl          : b.l init COMPILE
                    c.fasl          : c.l init COMPILE
                    COMPILE         : .USE
                            echo '(compile "$(.ALLSRC)")' >> input
                    init            : .EXEC
                            echo '(load-system)' > input

               .EXEC  sources,  don't appear in the local vari-
               ables of targets that depend on  them  (nor  are
               they  touched  if  Make  is  given the --tt flag).
               Note that all the rules, not just that for  sys-
               tem,  include  init as a source. This is because
               none of the other targets can be made until init
               has been made, thus they depend on it.
   .EXPORT     This  is  used  to mark those targets whose cre-
               ation should be sent to another  machine  if  at
               all  possible. This may be used by some exporta-
               tion schemes if the  exportation  is  expensive.
               You  should  ask your system administrator if it
               is necessary.
   .EXPORTSAME Tells the export system that the job  should  be
               exported  to  a machine of the same architecture
               as the current  one.  Certain  operations  (e.g.
               running text through nroff) can be performed the
               same on any architecture (CPU and operating sys-
               tem  type),  while others (e.g. compiling a pro-
               gram with cc) must be  performed  on  a  machine
               with  the same architecture. Not all export sys-
               tems will support this attribute.
   .IGNORE     Giving a target  the  .IGNORE  attribute  causes
               Make  to  ignore errors from any of the target's
               commands, as if they all had `-' before them.
   .INVISIBLE  This allows you  to  specify  one  target  as  a
               source for another without the one affecting the
               other's local variables.  Useful  if,  say,  you
               have  a  makefile that creates two programs, one
               of which is used to create the other, so it must
               exist before the other is created. You could say

                    prog1           : $(PROG1OBJS) prog2 MAKEINSTALL
                    prog2           : $(PROG2OBJS) .INVISIBLE MAKEINSTALL

               where MAKEINSTALL is some complex .USE rule (see









   Make -- A Tutorial                                 PSD:12-25


               below) that depends on the .ALLSRC variable con-
               taining the right things. Without the .INVISIBLE
               attribute   for   prog2,  the  MAKEINSTALL  rule
               couldn't be applied. This is not as useful as it
               should  be, and the semantics may change (or the
               whole thing  go  away)  in  the  not-too-distant
               future.
   .JOIN       This  is  another  way  to avoid performing some
               operations in parallel while  permitting  every-
               thing else to be done so. Specifically it forces
               the target's shell script to be executed only if
               one  or  more of the sources was out-of-date. In
               addition, the target's name, in both its .TARGET
               variable and all the local variables of any tar-
               get that depends on it, is replaced by the value
               of its .ALLSRC variable.  As an example, suppose
               you have a program that has four libraries  that
               compile in the same directory along with, and at
               the same time as, the program.  You  again  have
               the  problem  with  ranlib that I mentioned ear-
               lier, only this time it's more severe: you can't
               just  put  the  ranlib  off to the end since the
               program will need those libraries before it  can
               be re-created. You can do something like this:

                    program         : $(OBJS) libraries
                            cc -o $(.TARGET) $(.ALLSRC)

                    libraries       : lib1.a lib2.a lib3.a lib4.a .JOIN
                            ranlib $(.OODATE)

               In this case, Make will re-create the $(OBJS) as
               necessary, along with lib1.a, lib2.a, lib3.a and
               lib4.a.   It  will  then  execute  ranlib on any
               library that was changed and set program's .ALL-
               SRC  variable  to contain what's in $(OBJS) fol-
               lowed by ``lib1.a lib2.a  lib3.a  lib4.a.''   In
               case you're wondering, it's called .JOIN because
               it  joins  together  different  threads  of  the
               ``input  graph''  at  the target marked with the
               attribute.   Another   aspect   of   the   .JOIN
               attribute is it keeps the target from being cre-
               ated if the --tt flag was given.
   .MAKE       The .MAKE attribute marks its target as being  a
               recursive  invocation of Make.  This forces Make
               to execute the script associated with the target
               (if it's out-of-date) even if you gave the --nn or
               --tt flag. By doing this, you can start at the top
               of a system and type

                    make -n

               and  have it descend the directory tree (if your
               makefiles are set up correctly),  printing  what









   PSD:12-26                                 Make -- A Tutorial


               it  would  have  executed if you hadn't included
               the --nn flag.
   .NOEXPORT   If possible, Make will  attempt  to  export  the
               creation of all targets to another machine (this
               depends on how Make was configured).  Sometimes,
               the  creation  is  so simple, it is pointless to
               send it to another machine. If you give the tar-
               get  the  .NOEXPORT  attribute,  it  will be run
               locally, even if you've  given  Make  the  --LL  00
               flag.
   .NOTMAIN    Normally, if you do not specify a target to make
               in any other way, Make will take the first  tar-
               get  on  the first dependency line of a makefile
               as the target to create. That target is known as
               the  ``Main  Target''  and is labeled as such if
               you print the  dependencies  out  using  the  --pp
               flag.  Giving a target this attribute tells Make
               that the target is definitely _n_o_t the Main  Tar-
               get.   This  allows  you  to place targets in an
               included makefile and have Make create something
               else by default.
   .PRECIOUS   When  Make is interrupted (you type control-C at
               the keyboard), it will attempt to clean up after
               itself  by  removing any half-made targets. If a
               target has  the  .PRECIOUS  attribute,  however,
               Make  will  leave  it  alone. An additional side
               effect of the `::' operator is to mark the  tar-
               gets as .PRECIOUS.
   .SILENT     Marking  a  target with this attribute keeps its
               commands from being printed  when  they're  exe-
               cuted,  just  as  if they had an `@' in front of
               them.
   .USE        By giving a target this attribute, you  turn  it
               into Make's equivalent of a macro. When the tar-
               get is used as a source for another target,  the
               other  target acquires the commands, sources and
               attributes (except .USE) of the source.  If  the
               target  already  has commands, the .USE target's
               commands are added to the end. If more than  one
               .USE-marked  source  is  given  to a target, the
               rules are applied sequentially.
               The typical .USE rule (as I call them) will  use
               the sources of the target to which it is applied
               (as stored in the .ALLSRC variable for the  tar-
               get)  as  its  ``arguments,''  if you will.  For
               example, you probably noticed that the  commands
               for creating lib1.a and lib2.a in the example in
               section 3.3 were exactly the same. You  can  use
               the  .USE attribute to eliminate the repetition,
               like so:













   Make -- A Tutorial                                 PSD:12-27


                    lib1.a          : $(LIB1OBJS) MAKELIB
                    lib2.a          : $(LIB2OBJS) MAKELIB

                    MAKELIB         : .USE
                            rm -f $(.TARGET)
                            ar cr $(.TARGET) $(.ALLSRC)
                            ...
                            ranlib $(.TARGET)

               Several system makefiles  (not  to  be  confused
               with  The  System  Makefile)  make  use of these
               .USE rules to make your life easier (they're  in
               the  default, system makefile directory...take a
               look).  Note that the .USE  rule  source  itself
               (MAKELIB) does not appear in any of the target's
               local variables.  There is no limit to the  num-
               ber  of  times  I could use the MAKELIB rule. If
               there were more libraries, I could continue with
               ``lib3.a  :  $(LIB3OBJS) MAKELIB'' and so on and
               so forth.

   33..55..  SSppeecciiaall TTaarrggeettss
   As there were in Make, so there  are  certain  targets  that
   have  special  meaning to Make. When you use one on a depen-
   dency line, it is the only target that  may  appear  on  the
   left-hand-side  of  the operator.  As for the attributes and
   variables, all the special targets begin with a  period  and
   consist  of  upper-case letters only.  I won't describe them
   all in detail because some of them are  rather  complex  and
   I'll  describe them in more detail than you'll want in chap-
   ter 4.  The targets are as follows:
   .BEGIN    Any commands attached to this target are  executed
             before  anything  else is done. You can use it for
             any initialization that needs doing.
   .DEFAULT  This is sort of a .USE rule for any  target  (that
             was  used only as a source) that Make can't figure
             out any other way to create. It's only ``sort of''
             a .USE rule because only the shell script attached
             to the .DEFAULT target is used. The .IMPSRC  vari-
             able of a target that inherits .DEFAULT's commands
             is set to the target's own name.
   .END      This serves a function similar to .BEGIN, in  that
             commands  attached  to it are executed once every-
             thing has been re-created (so long  as  no  errors
             occurred).  It  also  serves the extra function of
             being a place on which Make can hang commands  you
             put  off to the end. Thus the script for this tar-
             get will be executed before any  of  the  commands
             you save with the ``...''.
   .EXPORT   The  sources  for  this  target  are passed to the
             exportation system compiled into Make.  Some  sys-
             tems  will  use  these  sources to configure them-
             selves. You should ask your  system  administrator
             about this.









   PSD:12-28                                 Make -- A Tutorial


   .IGNORE   This  target  marks  each  of its sources with the
             .IGNORE  attribute.  If  you  don't  give  it  any
             sources,  then  it is like giving the --ii flag when
             you invoke Make -- errors are ignored for all com-
             mands.
   .INCLUDES The  sources  for this target are taken to be suf-
             fixes that indicate a file that can be included in
             a  program  source  file.   The  suffix  must have
             already been declared with .SUFFIXES (see  below).
             Any  suffix so marked will have the directories on
             its search path (see .PATH, below) placed  in  the
             .INCLUDES  variable,  each  preceded by a --II flag.
             This variable can then be used as an argument  for
             the  compiler in the normal fashion. The .h suffix
             is already marked in this way in the system  make-
             file.  E.g. if you have

                  .SUFFIXES       : .bitmap
                  .PATH.bitmap    : /usr/local/X/lib/bitmaps
                  .INCLUDES       : .bitmap

             Make  will place ``-I/usr/local/X/lib/bitmaps'' in
             the .INCLUDES variable and you can then say

                  cc $(.INCLUDES) -c xprogram.c

             (Note: the  .INCLUDES  variable  is  not  actually
             filled  in  until  the  entire  makefile  has been
             read.)
   .INTERRUPT
             When Make is interrupted, it will execute the com-
             mands in the script for this target, if it exists.
   .LIBS     This does for libraries what  .INCLUDES  does  for
             include  files,  except  the  flag  used is --LL, as
             required by those linkers that allow you  to  tell
             them where to find libraries. The variable used is
             .LIBS.  Be forewarned that Make may not have  been
             compiled  to  do this if the linker on your system
             doesn't accept the --LL flag, though the .LIBS vari-
             able  will always be defined once the makefile has
             been read.
   .MAIN     If you didn't give a target (or targets) to create
             when you invoked Make, it will take the sources of
             this target as the targets to create.
   .MAKEFLAGS
             This target provides a way for you to always spec-
             ify  flags for Make when the makefile is used. The
             flags are just as they would be typed to the shell
             (except  you  can't  use  shell  variables  unless
             they're in the environment), though the --ff and  --rr
             flags have no effect.
   .NULL     This allows you to specify what suffix Make should
             pretend a file has if, in fact, it  has  no  known
             suffix.  Only one suffix may be so designated. The









   Make -- A Tutorial                                 PSD:12-29


             last source on the dependency line is  the  suffix
             that  is  used (you should, however, only give one
             suffix...).
   .PATH     If you give sources for  this  target,  Make  will
             take  them  as  directories in which to search for
             files it cannot find in the current directory.  If
             you  give no sources, it will clear out any direc-
             tories added to the search path before. Since  the
             effects  of  this all get very complex, I'll leave
             it til chapter four to give you a complete  expla-
             nation.
   .PATH_s_u_f_f_i_x
             This does a similar thing to .PATH, but it does it
             only for files with the given suffix.  The  suffix
             must  have  been  defined  already. Look at SSeeaarrcchh
             PPaatthhss (section 4.1) for more information.
   .PRECIOUS Similar  to  .IGNORE,  this  gives  the  .PRECIOUS
             attribute  to  each source on the dependency line,
             unless there are no sources,  in  which  case  the
             .PRECIOUS  attribute  is  given to every target in
             the file.
   .RECURSIVE
             This target applies the .MAKE attribute to all its
             sources.  It does nothing if you don't give it any
             sources.
   .SHELL    Make is not constrained to only using  the  Bourne
             shell to execute the commands you put in the make-
             file. You can tell it some other shell to use with
             this  target.  Check  out  AA SShheellll iiss aa SShheellll iiss aa
             SShheellll (section 4.4) for more information.
   .SILENT   When you use .SILENT as a target, it  applies  the
             .SILENT attribute to each of its sources. If there
             are no sources on the dependency line, then it  is
             as  if  you  gave Make the --ss flag and no commands
             will be echoed.
   .SUFFIXES This is used to give new file suffixes for Make to
             handle. Each source is a suffix Make should recog-
             nize. If you give a .SUFFIXES dependency line with
             no  sources,  Make  will forget about all the suf-
             fixes it knew (this also nukes the  null  suffix).
             For  those  targets  that  need  to  have suffixes
             defined, this is how you do it.
   In addition to these targets, a line of the form

        _a_t_t_r_i_b_u_t_e : _s_o_u_r_c_e_s

   applies the _a_t_t_r_i_b_u_t_e to all the targets listed as  _s_o_u_r_c_e_s.

   33..66..  MMooddiiffyyiinngg VVaarriiaabbllee EExxppaannssiioonn
   Variables need not always be expanded verbatim. Make defines
   several modifiers that may be applied to a variable's  value
   before  it  is  expanded. You apply a modifier by placing it
   after the variable name with a colon between the  two,  like
   so:









   PSD:12-30                                 Make -- A Tutorial


        ${_V_A_R_I_A_B_L_E:_m_o_d_i_f_i_e_r}

   Each  modifier  is  a single character followed by something
   specific to the modifier itself.  You may apply as many mod-
   ifiers  as  you want -- each one is applied to the result of
   the previous and is separated from the previous  by  another
   colon.
   There  are seven ways to modify a variable's expansion, most
   of which come from the C shell variable modification charac-
   ters:
        M_p_a_t_t_e_r_n
             This is used to select only those words (a word is
             a series of characters that are neither spaces nor
             tabs)  that  match the given _p_a_t_t_e_r_n.  The pattern
             is a wildcard pattern like that used by the shell,
             where  * means 0 or more characters of any sort; ?
             is any single character; [abcd] matches any single
             character  that  is  either  `a',  `b', `c' or `d'
             (there may be any number of characters between the
             brackets); [0-9] matches any single character that
             is between `0' and `9' (i.e. any digit. This  form
             may  be freely mixed with the other bracket form),
             and `\' is used to escape any  of  the  characters
             `*',  `?',  `['  or  `:',  leaving them as regular
             characters to match themselves  in  a  word.   For
             example,  the system makefile <makedepend.mk> uses
             ``$(CFLAGS:M-[ID]*)'' to extract all the -I and -D
             flags that would be passed to the C compiler. This
             allows it to properly  locate  include  files  and
             generate the correct dependencies.
        N_p_a_t_t_e_r_n
             This  is identical to :M except it substitutes all
             words that don't match the given pattern.
        S/_s_e_a_r_c_h_-_s_t_r_i_n_g/_r_e_p_l_a_c_e_m_e_n_t_-_s_t_r_i_n_g/[g]
             Causes the first occurrence  of  _s_e_a_r_c_h_-_s_t_r_i_n_g  in
             the variable to be replaced by _r_e_p_l_a_c_e_m_e_n_t_-_s_t_r_i_n_g,
             unless the g flag is given at the  end,  in  which
             case  all  occurrences of the string are replaced.
             The substitution is performed on each word in  the
             variable  in  turn. If _s_e_a_r_c_h_-_s_t_r_i_n_g begins with a
             ^, the string must match starting at the beginning
             of  the  word. If _s_e_a_r_c_h_-_s_t_r_i_n_g ends with a $, the
             string must match to the end of  the  word  (these
             two may be combined to force an exact match). If a
             backslash precedes these two characters,  however,
             they  lose  their special meaning. Variable expan-
             sion also occurs in the normal fashion inside both
             the   _s_e_a_r_c_h_-_s_t_r_i_n_g  and  the  _r_e_p_l_a_c_e_m_e_n_t_-_s_t_r_i_n_g,
             eexxcceepptt that a backslash is  used  to  prevent  the
             expansion  of  a $, not another dollar sign, as is
             usual.  Note that _s_e_a_r_c_h_-_s_t_r_i_n_g is just a  string,
             not  a  pattern,  so  none  of  the usual regular-
             expression/wildcard characters  have  any  special
             meaning  save ^ and $.  In the replacement string,









   Make -- A Tutorial                                 PSD:12-31


             the & character is replaced by  the  _s_e_a_r_c_h_-_s_t_r_i_n_g
             unless  it  is  preceded  by a backslash.  You are
             allowed to  use  any  character  except  colon  or
             exclamation  point  to  separate  the two strings.
             This so-called delimiter character may  be  placed
             in either string by preceding it with a backslash.
        T    Replaces each word in the  variable  expansion  by
             its  last  component  (its ``tail''). For example,
             given

                  OBJS = ../lib/a.o b /usr/lib/libm.a
                  TAILS = $(OBJS:T)

             the  variable  TAILS  would  expand  to  ``a.o   b
             libm.a.''
        H    This  is  similar to :T, except that every word is
             replaced  by  everything   but   the   tail   (the
             ``head'').  Using the same definition of OBJS, the
             string  ``$(OBJS:H)''  would  expand  to  ``../lib
             /usr/lib.''   Note  that  the  final  slash on the
             heads is removed and anything without  a  head  is
             replaced by the empty string.
        E    :E  replaces  each  word  by  its suffix (``exten-
             sion''). So  ``$(OBJS:E)''  would  give  you  ``.o
             .a.''
        R    This replaces each word by everything but the suf-
             fix (the ``root''  of  the  word).   ``$(OBJS:R)''
             expands to `` ../lib/a b /usr/lib/libm.''
   In addition, the System V style of substitution is also sup-
   ported.  This looks like:

        $(_V_A_R_I_A_B_L_E:_s_e_a_r_c_h_-_s_t_r_i_n_g=_r_e_p_l_a_c_e_m_e_n_t)

   It must be the last modifier in the  chain.  The  search  is
   anchored  at the end of each word, so only suffixes or whole
   words may be replaced.

   33..77..  MMoorree oonn DDeebbuuggggiinngg

   33..88..  MMoorree EExxeerrcciisseess
   (3.1)
        You've got a set programs, each  of  which  is  created
        from  its  own  assembly-language  source  file (suffix
        .asm).  Each program can be  assembled  into  two  ver-
        sions,  one  with  error-checking code assembled in and
        one without. You could assemble them  into  files  with
        different  suffixes (.eobj and .obj, for instance), but
        your linker only understands files that  end  in  .obj.
        To  top it all off, the final executables _m_u_s_t have the
        suffix .exe.  How  can  you  still  use  transformation
        rules to make your life easier (Hint: assume the error-
        checking versions have ec tacked onto their prefix)?
   (3.2)
        Assume, for a moment or two, you want to perform a sort









   PSD:12-32                                 Make -- A Tutorial


        of  ``indirection''  by  placing the name of a variable
        into another one, then you want to get the value of the
        first  by  expanding the second somehow. Unfortunately,
        Make doesn't allow constructs like

             $($(FOO))

        What do you do? Hint: no further variable expansion  is
        performed  after  modifiers  are  applied,  thus if you
        cause a $ to occur in the expansion, that's  what  will
        be in the result.

   44..  MMaakkee ffoorr GGooddss
   This  chapter  is  devoted  to those facilities in Make that
   allow you to do a great deal in a makefile with very  little
   work,  as  well  as  do  some things you couldn't do in Make
   without a great deal of work (and perhaps the use  of  other
   programs).  The problem with these features, is they must be
   handled with care, or you will end up with a mess.
   Once more, I assume  a  greater  familiarity  with  UNIX  or
   Sprite than I did in the previous two chapters.

   44..11..  SSeeaarrcchh PPaatthhss
   Make  supports the dispersal of files into multiple directo-
   ries by allowing you to specify places to look  for  sources
   with .PATH targets in the makefile. The directories you give
   as sources for these targets make up a ``search path.'' Only
   those  files used exclusively as sources are actually sought
   on a search path, the assumption being that anything  listed
   as  a  target in the makefile can be created by the makefile
   and thus should be in the current directory.
   There are two types of search paths in Make: one is used for
   all  types  of  files  (including included makefiles) and is
   specified with a plain .PATH target (e.g.  ``.PATH : RCS''),
   while  the  other  is specific to a certain type of file, as
   indicated by the file's suffix. A specific  search  path  is
   indicated by immediately following the .PATH with the suffix
   of the file. For instance

        .PATH.h         : /sprite/lib/include /sprite/att/lib/include

   would   tell   Make   to    look    in    the    directories
   /sprite/lib/include   and  /sprite/att/lib/include  for  any
   files whose suffix is .h.
   The current directory is always consulted first to see if  a
   file exists. Only if it cannot be found there are the direc-
   tories in the specific search path, followed by those in the
   general search path, consulted.
   A  search  path is also used when expanding wildcard charac-
   ters. If the pattern has a recognizable suffix  on  it,  the
   path  for that suffix will be used for the expansion. Other-
   wise the default search path is employed.
   When a file is found in some directory other than  the  cur-
   rent  one, all local variables that would have contained the









   Make -- A Tutorial                                 PSD:12-33


   target's name (.ALLSRC, and .IMPSRC)  will  instead  contain
   the  path to the file, as found by Make.  Thus if you have a
   file ../lib/mumble.c and a makefile

        .PATH.c         : ../lib
        mumble          : mumble.c
                $(CC) -o $(.TARGET) $(.ALLSRC)

   the command executed to create mumble would be ``cc -o  mum-
   ble  ../lib/mumble.c.''   (As  an aside, the command in this
   case isn't strictly necessary, since it will be found  using
   transformation rules if it isn't given. This is because .out
   is the null suffix by default and  a  transformation  exists
   from .c to .out.  Just thought I'd throw that in.)
   If a file exists in two directories on the same search path,
   the file in the first directory on the path will be the  one
   Make  uses.  So  if you have a large system spread over many
   directories, it would behoove you to follow a naming conven-
   tion that avoids such conflicts.
   Something  you  should  know  about the way search paths are
   implemented is that each directory is read, and its contents
   cached,  exactly  once -- when it is first encountered -- so
   any changes to the directories while Make  is  running  will
   not  be  noted when searching for implicit sources, nor will
   they be found when Make attempts to discover when  the  file
   was  last  modified, unless the file was created in the cur-
   rent directory. While people have suggested that Make should
   read  the directories each time, my experience suggests that
   the caching seldom causes problems. In addition, not caching
   the  directories  slows  things  down  enormously because of
   Make's attempts to apply transformation rules  through  non-
   existent  files  -- the number of extra file-system searches
   is truly staggering, especially if many files  without  suf-
   fixes  are used and the null suffix isn't changed from .out.

   44..22..  AArrcchhiivveess aanndd LLiibbrraarriieess
   UNIX and Sprite allow you to merge  files  into  an  archive
   using  the ar command. Further, if the files are relocatable
   object files, you can run ranlib  on  the  archive  and  get
   yourself  a  library  that you can link into any program you
   want. The main problem with  archives  is  they  double  the
   space  you  need  to store the archived files, since there's
   one copy in the archive and one  copy  out  by  itself.  The
   problem  with  libraries is you usually think of them as -lm
   rather than /usr/lib/libm.a and the  linker  thinks  they're
   out-of-date if you so much as look at them.
   Make  solves  the  problem  with archives by allowing you to
   tell it to examine the files in the  archives  (so  you  can
   remove  the  individual  files  without having to regenerate
   them later). To handle the problem with libraries, Make adds
   an additional way of deciding if a library is out-of-date:
   +o If  the table of contents is older than the library, or is
     missing, the library is out-of-date.










   PSD:12-34                                 Make -- A Tutorial


   A library is any target that looks like ``-lname''  or  that
   ends  in  a  suffix  that  was marked as a library using the
   .LIBS target.  .a is so marked in the system makefile.
   Members of an archive  are  specified  as  ``_a_r_c_h_i_v_e(_m_e_m_b_e_r[
   _m_e_m_b_e_r...])''.   Thus  ``'libdix.a(window.o)'' specifies the
   file window.o in the archive libdix.a.   You  may  also  use
   wildcards to specify the members of the archive. Just remem-
   ber that most the wildcard characters will only find  _e_x_i_s_t_-
   _i_n_g files.
   A  file that is a member of an archive is treated specially.
   If the file doesn't exist, but it is  in  the  archive,  the
   modification  time  recorded  in the archive is used for the
   file when determining if the file is out-of-date. When  fig-
   uring  out  how  to  make an archived member target (not the
   file itself, but the file in the archive -- the _a_r_c_h_i_v_e(_m_e_m_-
   _b_e_r)  target), special care is taken with the transformation
   rules, as follows:
   +o _a_r_c_h_i_v_e(_m_e_m_b_e_r) is made to depend on _m_e_m_b_e_r.
   +o The transformation from the _m_e_m_b_e_r's  suffix  to  the  _a_r_-
     _c_h_i_v_e's suffix is applied to the _a_r_c_h_i_v_e(_m_e_m_b_e_r) target.
   +o The  _a_r_c_h_i_v_e(_m_e_m_b_e_r)'s .TARGET variable is set to the name
     of the _m_e_m_b_e_r if _m_e_m_b_e_r is actually a target, or the  path
     to the member file if _m_e_m_b_e_r is only a source.
   +o The  .ARCHIVE  variable  for the _a_r_c_h_i_v_e(_m_e_m_b_e_r) target is
     set to the name of the _a_r_c_h_i_v_e.
   +o The .MEMBER variable is set to the  actual  string  inside
     the  parentheses.  In most cases, this will be the same as
     the .TARGET variable.
   +o The _a_r_c_h_i_v_e(_m_e_m_b_e_r)'s place in the local variables of  the
     targets  that  depend  on  it is taken by the value of its
     .TARGET variable.
   Thus, a program library could be created with the  following
   makefile:

        .o.a            :
                ...
                rm -f $(.TARGET:T)
        OBJS            = obj1.o obj2.o obj3.o
        libprog.a       : libprog.a($(OBJS))
                ar cru $(.TARGET) $(.OODATE)
                ranlib $(.TARGET)

   This  will  cause  the three object files to be compiled (if
   the corresponding  source  files  were  modified  after  the
   object  file  or, if that doesn't exist, the archived object
   file), the out-of-date ones archived in libprog.a,  a  table
   of  contents  placed  in  the archive and the newly-archived
   object files to be removed.
   All this is used in the makelib.mk system makefile to create
   a single library with ease. This makefile looks like this:













   Make -- A Tutorial                                 PSD:12-35


        #
        # Rules for making libraries. The object files that make up the library are
        # removed once they are archived.
        #
        # To make several libraries in parallel, you should define the variable
        # "many_libraries". This will serialize the invocations of ranlib.
        #
        # To use, do something like this:
        #
        # OBJECTS = <files in the library>
        #
        # fish.a: fish.a($(OBJECTS)) MAKELIB
        #
        #

        #ifndef _MAKELIB_MK
        _MAKELIB_MK    =

        #include  <po.mk>

        .po.a .o.a     :
             ...
             rm -f $(.MEMBER)

        ARFLAGS        ?= crl

        #
        # Re-archive the out-of-date members and recreate the library's table of
        # contents using ranlib. If many_libraries is defined, put the ranlib off
        # til the end so many libraries can be made at once.
        #
        MAKELIB        : .USE .PRECIOUS
             ar $(ARFLAGS) $(.TARGET) $(.OODATE)
        #ifndef no_ranlib
        # ifdef many_libraries
             ...
        # endif many_libraries
             ranlib $(.TARGET)
        #endif no_ranlib

        #endif _MAKELIB_MK


   44..33..  OOnn tthhee CCoonnddiittiioonn......
   Like  the C compiler before it, Make allows you to configure
   the makefile, based on the current environment, using condi-
   tional statements. A conditional looks like this:
















   PSD:12-36                                 Make -- A Tutorial


        #if _b_o_o_l_e_a_n _e_x_p_r_e_s_s_i_o_n
        _l_i_n_e_s
        #elif _a_n_o_t_h_e_r _b_o_o_l_e_a_n _e_x_p_r_e_s_s_i_o_n
        _m_o_r_e _l_i_n_e_s
        #else
        _s_t_i_l_l _m_o_r_e _l_i_n_e_s
        #endif

   They  may  be  nested to a maximum depth of 30 and may occur
   anywhere (except in a comment, of course).  The  ``#''  must
   the very first character on the line.
   Each  _b_o_o_l_e_a_n  _e_x_p_r_e_s_s_i_o_n is made up of terms that look like
   function calls, the standard C boolean operators &&, ||, and
   !,  and  the standard relational operators ==, !=, >, >=, <,
   and <=, with == and != being overloaded to allow string com-
   parisons  as well.  && represents logical AND; || is logical
   OR and !  is logical NOT.  The arithmetic and string  opera-
   tors  take  precedence  over  all  three of these operators,
   while NOT takes precedence over AND, which takes  precedence
   over  OR.   This precedence may be overridden with parenthe-
   ses, and an expression may be parenthesized to your  heart's
   content.   Each  term looks like a call on one of four func-
   tions:
   make     The syntax is make(_t_a_r_g_e_t) where _t_a_r_g_e_t is a target
            in  the  makefile. This is true if the given target
            was specified on the command line, or as the source
            for a .MAIN target (note that the sources for .MAIN
            are only used if no targets were given on the  com-
            mand line).
   defined  The  syntax  is  defined(_v_a_r_i_a_b_l_e)  and  is true if
            _v_a_r_i_a_b_l_e is defined. Certain variables are  defined
            in  the system makefile that identify the system on
            which Make is being run.
   exists   The syntax is exists(_f_i_l_e) and is true if the  file
            can  be found on the global search path (i.e.  that
            defined by .PATH targets, not by  .PATH_s_u_f_f_i_x  tar-
            gets).
   empty    This  syntax  is  much  like the others, except the
            string inside the parentheses is of the  same  form
            as you would put between parentheses when expanding
            a variable, complete with modifiers and everything.
            The  function  returns true if the resulting string
            is empty (NOTE: an undefined variable in this  con-
            text will cause at the very least a warning message
            about a malformed conditional,  and  at  the  worst
            will cause the process to stop once it has read the
            makefile. If you want to check for a variable being
            defined    or    empty,    use    the    expression
            ``!defined(_v_a_r) || empty(_v_a_r)'' as  the  definition
            of || will prevent the empty() from being evaluated
            and causing an error,  if  the  variable  is  unde-
            fined).  This can be used to see if a variable con-
            tains a given word, for example:










   Make -- A Tutorial                                 PSD:12-37


                 #if !empty(_v_a_r:M_w_o_r_d)

   The arithmetic and string operators may only be used to test
   the  value of a variable. The lefthand side must contain the
   variable expansion, while the righthand side contains either
   a  string, enclosed in double-quotes, or a number. The stan-
   dard C numeric conventions (except for specifying  an  octal
   number) apply to both sides. E.g.

        #if $(OS) == 4.3

        #if $(MACHINE) == "sun3"

        #if $(LOAD_ADDR) < 0xc000

   are  all  valid conditionals. In addition, the numeric value
   of a variable can be tested as a boolean as follows:

        #if $(LOAD)

   would see if LOAD contains a non-zero value and

        #if !$(LOAD)

   would test if LOAD contains a zero value.
   In addition to the bare ``#if,'' there are other forms  that
   apply  one of the first two functions to each term. They are
   as follows:

             ifdef     defined
             ifndef    !defined
             ifmake    make
             ifnmake   !make

   There are also the ``else if'' forms: elif, elifdef,  elifn-
   def, elifmake, and elifnmake.
   For  instance,  if you wish to create two versions of a pro-
   gram, one of which is optimized (the production version) and
   the  other  of which is for debugging (has symbols for dbx),
   you have two choices: you can create two makefiles,  one  of
   which  uses the -g flag for the compilation, while the other
   uses the -O flag, or you can use  another  target  (call  it
   debug) to create the debug version. The construct below will
   take care of this for you. I have also made it  so  defining
   the  variable DEBUG (say with make -D DEBUG) will also cause
   the debug version to be made.

        #if defined(DEBUG) || make(debug)
        CFLAGS         += -g
        #else
        CFLAGS         += -O
        #endif

   There are, of course, problems with this approach. The  most









   PSD:12-38                                 Make -- A Tutorial


   glaring  annoyance  is  that if you want to go from making a
   debug version to making a production version,  you  have  to
   remove  all the object files, or you will get some optimized
   and some debug versions in the same program. Another  annoy-
   ance  is you have to be careful not to make two targets that
   ``conflict'' because of some conditionals in  the  makefile.
   For instance

        #if make(print)
        FORMATTER = ditroff -Plaser_printer
        #endif
        #if make(draft)
        FORMATTER = nroff -Pdot_matrix_printer
        #endif

   would  wreak  havoc  if you tried ``make draft print'' since
   you would use the same formatter for each target. As I said,
   this all gets somewhat complicated.

   44..44..  AA SShheellll iiss aa SShheellll iiss aa SShheellll
   In  normal  operation,  the  Bourne  Shell  (better known as
   ``sh'') is used to execute the commands  to  re-create  tar-
   gets.  Make also allows you to specify a different shell for
   it to use when executing these commands. There  are  several
   things Make must know about the shell you wish to use. These
   things are specified as the sources for the .SHELL target by
   keyword, as follows:
   ppaatthh==_p_a_t_h
        Make needs to know where the shell actually resides, so
        it can execute it. If  you  specify  this  and  nothing
        else,  Make will use the last component of the path and
        look in its table of the shells it knows  and  use  the
        specification  it  finds,  if any. Use this if you just
        want to use a different version  of  the  Bourne  or  C
        Shell (yes, Make knows how to use the C Shell too).
   nnaammee==_n_a_m_e
        This  is the name by which the shell is to be known. It
        is a single word and, if no other keywords  are  speci-
        fied  (other  than  ppaatthh), it is the name by which Make
        attempts to find a specification for it  (as  mentioned
        above).  You  can use this if you would just rather use
        the C Shell than the Bourne Shell (``.SHELL: name=csh''
        will do it).
   qquuiieett==_e_c_h_o_-_o_f_f _c_o_m_m_a_n_d
        As  mentioned  before,  Make  actually controls whether
        commands are printed by introducing commands  into  the
        shell's  input  stream. This keyword, and the next two,
        control what those commands are. The qquuiieett  keyword  is
        the command used to turn echoing off. Once it is turned
        off, echoing is expected to remain off until the  echo-
        on command is given.
   eecchhoo==_e_c_h_o_-_o_n _c_o_m_m_a_n_d
        The  command  Make  should give to turn echoing back on
        again.









   Make -- A Tutorial                                 PSD:12-39


   ffiilltteerr==_p_r_i_n_t_e_d _e_c_h_o_-_o_f_f _c_o_m_m_a_n_d
        Many shells will echo the echo-off command when  it  is
        given. This keyword tells Make in what format the shell
        actually prints the  echo-off  command.  Wherever  Make
        sees  this string in the shell's output, it will delete
        it and any following whitespace, up  to  and  including
        the  next  newline.  See the example at the end of this
        section for more details.
   eecchhooFFllaagg==_f_l_a_g _t_o _t_u_r_n _e_c_h_o_i_n_g _o_n
        Unless a target has been marked .SILENT, Make wants  to
        start the shell running with echoing on. To do this, it
        passes this flag to the shell as one of its  arguments.
        If  either this or the next flag begins with a `-', the
        flags will be passed to the  shell  as  separate  argu-
        ments. Otherwise, the two will be concatenated (if they
        are used at the same time, of course).
   eerrrrFFllaagg==_f_l_a_g _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_n
        Likewise, unless  a  target  is  marked  .IGNORE,  Make
        wishes  error-checking to be on from the very start. To
        this end, it will pass this flag to  the  shell  as  an
        argument.  The  same  rules for an initial `-' apply as
        for the eecchhooFFllaagg.
   cchheecckk==_c_o_m_m_a_n_d _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_n
        Just as for echo-control, error-control is achieved  by
        inserting  commands into the shell's input stream. This
        is the command to make the shell check for  errors.  It
        also  serves  another purpose if the shell doesn't have
        error-control as commands, but I'll get into that in  a
        minute.  Again, once error checking has been turned on,
        it is expected to remain on  until  it  is  turned  off
        again.
   iiggnnoorree==_c_o_m_m_a_n_d _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_f_f
        This  is  the  command Make uses to turn error checking
        off. It has another use if the shell doesn't do  error-
        control, but I'll tell you about that...now.
   hhaassEErrrrCCttll==_y_e_s _o_r _n_o
        This  takes  a value that is either yyeess or nnoo.  Now you
        might think that the existence of the cchheecckk and  iiggnnoorree
        keywords  would be enough to tell Make if the shell can
        do error-control, but you'd be wrong. If  hhaassEErrrrCCttll  is
        yyeess,  Make  uses  the  check  and  ignore commands in a
        straight-forward manner.  If this is nnoo, however, their
        use  is  rather different. In this case, the check com-
        mand is used as a template, in which the string  %%ss  is
        replaced by the command that's about to be executed, to
        produce a command for the shell that will echo the com-
        mand to be executed. The ignore command is also used as
        a template, again with %%ss replaced by the command to be
        executed,  to  produce  a command that will execute the
        command to be executed and ignore any error it returns.
        When these strings are used as templates, you must pro-
        vide newline(s) (``\n'') in the appropriate place(s).
   The strings that follow these keywords may  be  enclosed  in
   single  or  double  quotes (the quotes will be stripped off)









   PSD:12-40                                 Make -- A Tutorial


   and may contain the usual C backslash-characters (\n is new-
   line,  \r  is  return, \b is backspace, \' escapes a single-
   quote inside single-quotes, \" escapes a double-quote inside
   double-quotes). Now for an example.
   This  is  actually the contents of the <shx.mk> system make-
   file, and causes Make to use the Bourne Shell in such a  way
   that  each command is printed as it is executed. That is, if
   more than one command is given  on  a  line,  each  will  be
   printed separately.  Similarly, each time the body of a loop
   is executed, the commands within that loop will be  printed,
   etc. The specification runs like this:

        #
        # This is a shell specification to have the bourne shell echo
        # the commands just before executing them, rather than when it reads
        # them. Useful if you want to see how variables are being expanded, etc.
        #
        .SHELL    : path=/bin/mksh \
             quiet="set -" \
             echo="set -x" \
             filter="+ set - " \
             echoFlag=x \
             errFlag=e \
             hasErrCtl=yes \
             check="set -e" \
             ignore="set +e"

   It tells Make the following:
   +o The  shell  is located in the file /bin/mksh.  It need not
     tell Make that the name of the shell is mksh as  Make  can
     figure that out for itself (it's the last component of the
     path).
   +o The command to stop echoing is set -.
   +o The command to start echoing is set -x.
   +o When the echo off command  is  executed,  the  shell  will
     print  +  set  -  (The  `+'  comes  from using the -x flag
     (rather than the -v flag Make usually  uses)).  Make  will
     remove  all occurrences of this string from the output, so
     you don't notice extra commands you didn't put there.
   +o The flag the Bourne Shell will take to  start  echoing  in
     this  way  is the -x flag. The Bourne Shell will only take
     its flag arguments concatenated as its first argument,  so
     neither  this  nor the eerrrrFFllaagg specification begins with a
     -.
   +o The flag to use to turn error-checking on from  the  start
     is -e.
   +o The shell can turn error-checking on and off, and the com-
     mands to do so are set +e and set -e, respectively.
   This will cause Make to execute the two commands

        echo "+ _c_m_d"
        sh -c '_c_m_d || true'

   for each command for which errors are  to  be  ignored.  (In









   Make -- A Tutorial                                 PSD:12-41


   case you are wondering, the thing for ignore tells the shell
   to execute another  shell  without  error  checking  on  and
   always exit 0, since the |||| causes the exit 0 to be executed
   only if the first command exited non-zero, and if the  first
   command  exited  zero,  the shell will also exit zero, since
   that's the last command it executed).

   44..55..  CCoommppaattiibbiilliittyy
   There are three (well, 3 1/2) levels  of  backwards-compati-
   bility  built  into  Make.  Most makefiles will need none at
   all. Some may need a little bit of work to operate correctly
   when  run  in  parallel. Each level encompasses the previous
   levels (e.g.  --BB (one shell per  command)  implies  --VV)  The
   three  levels are described in the following three sections.

   44..55..11..  DDEEFFCCOONN 33 ---- VVaarriiaabbllee EExxppaannssiioonn
   As noted before, Make will not expand a variable  unless  it
   knows  of  a value for it. This can cause problems for make-
   files that expect to leave  variables  undefined  except  in
   special  circumstances (e.g. if more flags need to be passed
   to the C compiler or the output from a text processor should
   be  sent  to  a  different  printer).  If  the variables are
   enclosed in curly braces (``${PRINTER}''),  the  shell  will
   let them pass. If they are enclosed in parentheses, however,
   the shell will declare a syntax error and the make will come
   to a grinding halt.
   You  have  two  choices:  change  the makefile to define the
   variables (their values can be  overridden  on  the  command
   line,  since  that's  where  they would have been set if you
   used Make, anyway) or always give the --VV flag (this  can  be
   done with the .MAKEFLAGS target, if you want).

   44..55..22..  DDEEFFCCOONN 22 ---- TThhee NNuummbbeerr ooff tthhee BBeeaasstt
   Then  there  are the makefiles that expect certain commands,
   such as changing to a different  directory,  to  not  affect
   other  commands in a target's creation script. You can solve
   this is either by going back to executing one shell per com-
   mand  (which  is  what the --BB flag forces Make to do), which
   slows the process down a good bit and requires  you  to  use
   semicolons  and escaped newlines for shell constructs, or by
   changing the makefile to execute the offending command(s) in
   a  subshell  (by  placing the line inside parentheses), like
   so:

        install :: .MAKE
             (cd src; $(.MAKE) install)
             (cd lib; $(.MAKE) install)
             (cd man; $(.MAKE) install)

   This will always execute the three makes  (even  if  the  --nn
   flag  was  given)  because  of the combination of the ``::''
   operator and the .MAKE attribute. Each command  will  change
   to  the proper directory to perform the install, leaving the
   main shell in the directory in which it started.









   PSD:12-42                                 Make -- A Tutorial


   44..55..33..  DDEEFFCCOONN 11 ---- IImmiittaattiioonn iiss tthhee NNoott tthhee HHiigghheesstt FFoorrmm ooff
   FFllaatttteerryy
   The final category of makefile is the one where  every  com-
   mand requires input, the dependencies are incompletely spec-
   ified, or you simply cannot create more than one target at a
   time,  as  mentioned  earlier. In addition, you may not have
   the time or desire to upgrade the makefile to  run  smoothly
   with  Make.  If  you  are the conservative sort, this is the
   compatibility mode for you. It is entered by giving Make the
   --BB flag.  This includes:
   +o No parallel execution.
   +o Targets are made in the exact order specified by the make-
     file. The sources for each target are made in strict left-
     to-right order, etc.
   +o A  single  Bourne  shell  is used to execute each command,
     thus the shell's $$ variable is useless, changing directo-
     ries doesn't work across command lines, etc.
   +o If  no  special  characters  exist in a command line, Make
     will break the command into words itself and  execute  the
     command  directly,  without  executing  a shell first. The
     characters that cause Make to execute a shell are:  #,  =,
     |,  ^, (, ), {, }, ;, &, <, >, *, ?, [, ], :, $, `, and \.
     You should notice that these are all the  characters  that
     are  given  special  meaning by the shell (except ' and  ,
     which Make deals with all by its lonesome).

   44..66..  TThhee WWaayy TThhiinnggss WWoorrkk
   When Make reads the makefile, it parses sources and  targets
   into  nodes  in  a  graph. The graph is directed only in the
   sense that Make knows which way is up.  Each  node  contains
   not  only  links  to all its parents and children (the nodes
   that depend on it and those on  which  it  depends,  respec-
   tively), but also a count of the number of its children that
   have already been processed.
   The most important thing to know about how  Make  uses  this
   graph  is  that the traversal is breadth-first and occurs in
   two passes.
   After Make has parsed the makefile, it begins with the nodes
   the user has told it to make (either on the command line, or
   via a .MAIN target, or by the target being the first in  the
   file  not  labeled  with the .NOTMAIN attribute) placed in a
   queue. It continues to take the node off the  front  of  the
   queue,  mark it as something that needs to be made, pass the
   node  to  Suff_FindDeps  (mentioned  earlier)  to  find  any
   implicit  sources  for  the  node,  and place all the node's
   children that have yet to be marked at the end of the queue.
   If  any  of  the children is a .USE rule, its attributes are
   applied to the parent, then its commands are appended to the
   parent's list of commands and its children are linked to its
   parent. The parent's unmade children counter is then  decre-
   mented  (since  the  .USE node has been processed). You will
   note that this allows a .USE node to have children that  are
   .USE  nodes  and  the rules will be applied in sequence.  If
   the node has no children, it is placed at the end of another









   Make -- A Tutorial                                 PSD:12-43


   queue  to  be examined in the second pass. This process con-
   tinues until the first queue is empty.
   At this point, all the leaves of the graph are in the exami-
   nation queue. Make removes the node at the head of the queue
   and sees if it is out-of-date. If it is, it is passed  to  a
   function  that  will execute the commands for the node asyn-
   chronously. When the commands have completed, all the node's
   parents  have their unmade children counter decremented and,
   if the counter is then 0, they are placed on the examination
   queue.  Likewise, if the node is up-to-date. Only those par-
   ents that were marked on the downward pass are processed  in
   this way. Thus Make traverses the graph back up to the nodes
   the user instructed it to create. When the examination queue
   is  empty and no shells are running to create a target, Make
   is finished.
   Once all targets have been processed, Make executes the com-
   mands  attached  to  the  .END  target, either explicitly or
   through the use of an ellipsis in a shell script.  If  there
   were no errors during the entire process but there are still
   some targets unmade (Make keeps a running count of how  many
   targets are left to be made), there is a cycle in the graph.
   Make does a depth-first traversal of the graph to  find  all
   the  targets  that  weren't  made and prints them out one by
   one.

   55..  AAnnsswweerrss ttoo EExxeerrcciisseess
   (3.1)
        This is something of a  trick  question,  for  which  I
        apologize.  The trick comes from the UNIX definition of
        a suffix, which Make  doesn't  necessarily  share.  You
        will  have  noticed  that all the suffixes used in this
        tutorial (and in UNIX in general) begin with  a  period
        (.ms,  .c,  etc.). Now, Make's idea of a suffix is more
        like English's: it's the characters at  the  end  of  a
        word.  With this in mind, one possible solution to this
        problem goes as follows:

             .SUFFIXES       : ec.exe .exe ec.obj .obj .asm
             ec.objec.exe .obj.exe :
                     link -o $(.TARGET) $(.IMPSRC)
             .asmec.obj      :
                     asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC)
             .asm.obj        :
                     asm -o $(.TARGET) $(.IMPSRC)

   (3.2)
        The trick to this one  lies  in  the  ``:=''  variable-
        assignment  operator  and the ``:S'' variable-expansion
        modifier.  Basically what  you  want  is  to  take  the
        pointer variable, so to speak, and transform it into an
        invocation of the variable  at  which  it  points.  You
        might try something like











   PSD:12-44                                 Make -- A Tutorial


             $(PTR:S/^/\$(/:S/$/))

        which  places  ``$('' at the front of the variable name
        and ``)'' at the end, thus  transforming  ``VAR,''  for
        example,  into  ``$(VAR),'' which is just what we want.
        Unfortunately (as you know if you've tried it),  since,
        as  it says in the hint, Make does no further substitu-
        tion on the result of a modified expansion, that's  _a_l_l
        you get. The solution is to make use of ``:='' to place
        that string into yet another variable, then invoke  the
        other variable directly:

             *PTR            := $(PTR:S/^/\$(/:S/$/)/)

        You can then use ``$(*PTR)'' to your heart's content.

   66..  GGlloossssaarryy ooff JJaarrggoonn
   aattttrriibbuuttee::  A property given to a target that causes Make to
        treat it differently.
   ccoommmmaanndd ssccrriipptt:: The lines immediately following a dependency
        line that specify commands to execute to create each of
        the targets on the dependency line. Each  line  in  the
        command script must begin with a tab.
   ccoommmmaanndd--lliinnee  vvaarriiaabbllee::  A  variable  defined in an argument
        when Make is first executed.  Overrides all assignments
        to the same variable name in the makefile.
   ccoonnddiittiioonnaall::  A  construct  much  like  that  used in C that
        allows a makefile to be configured on the fly based  on
        the local environment, or on what is being made by that
        invocation of Make.
   ccrreeaattiioonn ssccrriipptt:: Commands  used  to  create  a  target.  See
        ``command script.''
   ddeeppeennddeennccyy::  The relationship between a source and a target.
        This comes in three flavors, as indicated by the opera-
        tor  between  the  target  and  the source. `:' gives a
        straight time-wise dependency (if the target  is  older
        than  the source, the target is out-of-date), while `!'
        provides simply an ordering and  always  considers  the
        target out-of-date. `::' is much like `:', save it cre-
        ates multiple instances  of  a  target  each  of  which
        depends on its own list of sources.
   ddyynnaammiicc  ssoouurrccee::  This  refers  to a source that has a local
        variable invocation in it. It allows  a  single  depen-
        dency  line to specify a different source for each tar-
        get on the line.
   gglloobbaall vvaarriiaabbllee:: Any variable defined in a  makefile.  Takes
        precedence  over  variables defined in the environment,
        but not over command-line or local variables.
   iinnppuutt ggrraapphh:: What Make constructs from a makefile.  Consists
        of  nodes  made of the targets in the makefile, and the
        links between them (the dependencies).  The  links  are
        directed  (from  source to target) and there may not be
        any cycles (loops) in the graph.










   Make -- A Tutorial                                 PSD:12-45


   llooccaall vvaarriiaabbllee:: A variable defined by Make visible only in a
        target's  shell  script.   There  are seven local vari-
        ables, not all of which are defined for  every  target:
        .TARGET,  .ALLSRC, .OODATE, .PREFIX, .IMPSRC, .ARCHIVE,
        and .MEMBER.  .TARGET, .PREFIX, .ARCHIVE,  and  .MEMBER
        may  be  used  on  dependency lines to create ``dynamic
        sources.''
   mmaakkeeffiillee:: A file that describes how a system  is  built.  If
        you  don't  know  what  it  is after reading this tuto-
        rial....
   mmooddiiffiieerr:: A letter, following a colon, used to alter  how  a
        variable is expanded.  It has no effect on the variable
        itself.
   ooppeerraattoorr:: What separates a source from a target (on a depen-
        dency  line) and specifies the relationship between the
        two. There are three: `:', `::', and `!'.
   sseeaarrcchh ppaatthh:: A list of directories in which a file should be
        sought. Make's view of the contents of directories in a
        search path does not change once the makefile has  been
        read.  A  file is sought on a search path only if it is
        exclusively a source.
   sshheellll:: A program to which commands are passed  in  order  to
        create targets.
   ssoouurrccee:: Anything to the right of an operator on a dependency
        line. Targets on the dependency line are  usually  cre-
        ated from the sources.
   ssppeecciiaall  ttaarrggeett::  A  target  that  causes Make to do special
        things when it's encountered.
   ssuuffffiixx:: The tail end of a file name. Usually begins  with  a
        period, .c or .ms, e.g.
   ttaarrggeett::  A  word to the left of the operator on a dependency
        line. More generally, any file that Make might  create.
        A file may be (and often is) both a target and a source
        (what it is depends on how Make is looking at it at the
        time  --  sort  of  like  the  wave/particle duality of
        light, you know).
   ttrraannssffoorrmmaattiioonn rruullee:: A special construct in a makefile  that
        specifies  how to create a file of one type from a file
        of another, as indicated by their suffixes.
   vvaarriiaabbllee eexxppaannssiioonn:: The process of substituting the value of
        a  variable  for  a  reference  to it. Expansion may be
        altered by means of modifiers.
   vvaarriiaabbllee:: A place  in  which  to  store  text  that  may  be
        retrieved later. Also used to define the local environ-
        ment. Conditionals exist that test whether  a  variable
        is defined or not.

















   PSD:12-46                                 Make -- A Tutorial



                           TTaabbllee ooff CCoonntteennttss


        1.    Introduction  . . . . . . . . . . . . . . . .   1
        2.    The Basics of Make  . . . . . . . . . . . . .   2
        2.1.  Dependency Lines  . . . . . . . . . . . . . .   2
        2.2.  Shell Commands  . . . . . . . . . . . . . . .   4
        2.3.  Variables . . . . . . . . . . . . . . . . . .   6
        2.3.1.Local Variables . . . . . . . . . . . . . . .   8
        2.3.2.Command-line Variables  . . . . . . . . . . .   8
        2.3.3.Global Variables  . . . . . . . . . . . . . .   9
        2.3.4.Environment Variables . . . . . . . . . . . .   9
        2.4.  Comments  . . . . . . . . . . . . . . . . . .  10
        2.5.  Parallelism . . . . . . . . . . . . . . . . .  10
        2.6.  Writing and Debugging a Makefile  . . . . . .  11
        2.7.  Invoking Make . . . . . . . . . . . . . . . .  13
        2.8.  Summary . . . . . . . . . . . . . . . . . . .  16
        2.9.  Exercises . . . . . . . . . . . . . . . . . .  16
        3.    Short-cuts and Other Nice Things  . . . . . .  16
        3.1.  Transformation Rules  . . . . . . . . . . . .  17
        3.2.  Including Other Makefiles . . . . . . . . . .  21
        3.3.  Saving Commands . . . . . . . . . . . . . . .  22
        3.4.  Target Attributes . . . . . . . . . . . . . .  23
        3.5.  Special Targets . . . . . . . . . . . . . . .  27
        3.6.  Modifying Variable Expansion  . . . . . . . .  29
        3.7.  More on Debugging . . . . . . . . . . . . . .  31
        3.8.  More Exercises  . . . . . . . . . . . . . . .  31
        4.    Make for Gods . . . . . . . . . . . . . . . .  32
        4.1.  Search Paths  . . . . . . . . . . . . . . . .  32
        4.2.  Archives and Libraries  . . . . . . . . . . .  33
        4.3.  On the Condition...   . . . . . . . . . . . .  35
        4.4.  A Shell is a Shell is a Shell . . . . . . . .  38
        4.5.  Compatibility . . . . . . . . . . . . . . . .  41
        4.5.1.DEFCON 3 -- Variable Expansion  . . . . . . .  41
        4.5.2.DEFCON 2 -- The Number of the Beast . . . . .  41
        4.5.3.DEFCON 1 -- Imitation is the Not the Highest
        Form of Flattery  . . . . . . . . . . . . . . . . .  42
        4.6.  The Way Things Work . . . . . . . . . . . . .  42
        5.    Answers to Exercises  . . . . . . . . . . . .  43
        6.    Glossary of Jargon  . . . . . . . . . . . . .  44



















