








                       PPMMaakkee ---- 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

                           _R_e_v_i_s_i_o_n_s _b_y
                         _A_n_d_r_e_a_s _S_t_o_l_c_k_e
             International Computer Science Institute
                  1947 Center Street, Suite 600
                        Berkeley, CA 94704
                    stolcke@icsi.berkeley.edu






   5 February 2001


















   -----------
      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, Adam de Boor,  the  International  Com-
   puter  Science Institute, and Andreas Stolcke make
   no representations about the suitability  of  this
   software  for any purpose.  It is provided "as is"
   without express or implied warranty.















                       PPMMaakkee ---- 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

                           _R_e_v_i_s_i_o_n_s _b_y
                         _A_n_d_r_e_a_s _S_t_o_l_c_k_e
             International Computer Science Institute
                  1947 Center Street, Suite 600
                        Berkeley, CA 94704
                    stolcke@icsi.berkeley.edu



   11..  IInnttrroodduuccttiioonn

   PMake is a program for creating other programs, or  anything
   else  you  can think of for it to do.  The basic idea behind
   PMake 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).  PMake takes these dependencies, which you must spec-
   ify, and uses them to build whatever it is you  want  it  to
   build.

   PMake  is  almost fully-compatible with Make, with which you
   may already be familiar. PMake's most important  feature  is
   its  ability  to  run several different jobs at once, making
   the creation of systems considerably faster. It also  has  a
   great  deal  more  functionality  than  Make. Throughout the
   text, whenever something is mentioned that is  an  important
   difference between PMake and Make (i.e.  something that will
   cause a makefile to fail if you  don't  do  something  about
   it),  or is simply important, it will be flagged with a lit-
   tle sign in the left margin, like this:
   _N_o_t_e
   -----------
      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, Adam de Boor,  the  International  Com-
   puter  Science Institute, and Andreas Stolcke make
   no representations about the suitability  of  this
   software  for any purpose.  It is provided "as is"
   without express or implied warranty.









                                -2-


   This tutorial is divided into  three  main  sections  corre-
   sponding to basic, intermediate and advanced PMake usage. If
   you already know Make well, you will only need to skim chap-
   ter 2 (there are some aspects of PMake that I consider basic
   to its use that didn't exist in Make).  Things in chapter  3
   make life much easier, while those in chapter 4 are strictly
   for those who know what they are doing.  Chapter 5  contains
   possible  solutions to the problems presented throughout the
   tutorial, and chapter 6 has definitions  for  the  jargon  I
   use.
   _O_L_D _T_h_e _t_u_t_o_r_i_a_l _y_o_u _a_r_e _r_e_a_d_i_n_g _h_a_s _b_e_e_n _u_p_d_a_t_e_d _t_o _r_e_f_l_e_c_t
   _t_h_e _c_h_a_n_g_e_s _m_a_d_e _t_o _P_M_a_k_e _a_t _I_C_S_I _o_v_e_r  _r_e_c_e_n_t  _y_e_a_r_s_.   _T_h_e
   _c_h_a_n_g_e_s  _f_a_l_l _i_n_t_o _f_o_u_r _b_r_o_a_d _c_a_t_e_g_o_r_i_e_s_: _1_) _r_e_m_o_v_a_l _o_f _g_r_a_-
   _t_u_i_t_o_u_s  _i_n_c_o_m_p_a_t_i_b_i_l_i_t_i_e_s  _w_i_t_h  _o_t_h_e_r  _M_a_k_e  _v_e_r_s_i_o_n_s_,  _2_)
   _e_n_h_a_n_c_e_m_e_n_t_s  _(_o_f_t_e_n _b_o_r_r_o_w_e_d _f_r_o_m _o_t_h_e_r _M_a_k_e_s_)_, _3_) _f_e_a_t_u_r_e_s
   _p_r_e_v_i_o_u_s_l_y _p_r_e_s_e_n_t _b_u_t _u_n_d_o_c_u_m_e_n_t_e_d_,  _a_n_d  _4_)  _o_u_t_r_i_g_h_t  _b_u_g
   _f_i_x_e_s_.   _T_o _h_e_l_p _h_i_g_h_l_i_g_h_t _t_h_e _d_i_f_f_e_r_e_n_c_e_s _o_b_s_o_l_e_t_e _t_e_x_t _h_a_s
   _b_e_e_n _r_e_t_a_i_n_e_d _a_n_d _f_l_a_g_g_e_d _w_i_t_h _t_h_e _"_O_L_D_" _s_i_g_n _y_o_u _s_e_e  _h_e_r_e_.
   _N_E_W  _N_e_w  _a_n_d  _u_p_d_a_t_e_d  _f_e_a_t_u_r_e_s _a_r_e _s_i_m_i_l_a_r_l_y _m_a_r_k_e_d _w_i_t_h _a
   _"_N_E_W_" _s_i_g_n_, _a_n_d _i_m_p_o_r_t_a_n_t _c_h_a_n_g_e_s _a_r_e  _a_d_d_i_t_i_o_n_a_l_l_y  _t_y_p_e_s_e_t
   _i_n _i_t_a_l_i_c _f_o_n_t_.  _-_-_A_._S_.


   22..  TThhee BBaassiiccss ooff PPMMaakkee

   PMake 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, PMake will look for MMaakkeeffiillee and mmaakkeeffiillee
   (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.  ``ppmmaakkee --ff pprrooggrraamm..mmkk'').

   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 PMake.

   22..11..  DDeeppeennddeennccyy LLiinneess











                                -3-


   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
   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:










                                -4-


        +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  (aa..cc,  bb..cc  and
   cc..cc)  each of which includes the file ddeeffss..hh.  The dependen-
   cies between the files could then be expressed as follows:

        pprrooggrraamm         :: aa..oo bb..oo cc..oo
        aa..oo bb..oo cc..oo     :: ddeeffss..hh
        aa..oo             :: aa..cc
        bb..oo             :: bb..cc
        cc..oo             :: cc..cc


   You may be wondering at this point, where aa..oo, bb..oo  and  cc..oo
   came in and why _t_h_e_y depend on ddeeffss..hh and the C files don't.
   The reason is quite simple: pprrooggrraamm cannot be made by  link-
   ing  together  .c  files  --  it must be made from .o files.
   Likewise, if you change ddeeffss..hh, 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.  aa..oo depends on both ddeeffss..hh and aa..cc.
   _N_o_t_e

   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  pprrooggrraamm comes first in the example
   makefile, above.
   _N_E_W CCoommppaattiibbiilliittyy:: _H_o_w_e_v_e_r_, _t_a_r_g_e_t_s _s_t_a_r_t_i_n_g _w_i_t_h  _a  _p_e_r_i_o_d
   _a_n_d  _n_o_t  _c_o_n_t_a_i_n_i_n_g _`_`_/_'_' _a_r_e _n_o_t _u_s_e_d _a_s _d_e_f_a_u_l_t _m_a_i_n _t_a_r_-
   _g_e_t_s_.  _T_h_i_s _s_e_e_m_s _t_o _b_e _w_h_a_t _o_t_h_e_r _M_a_k_e_s _d_o_, _a_n_d  _a_l_s_o  _p_r_e_-
   _v_e_n_t_s  _m_i_s_c_e_l_l_a_n_e_o_u_s  _p_s_e_u_d_o_-_t_a_r_g_e_t_s  _(_s_p_e_c_i_f_i_c  _t_o _P_M_a_k_e _o_r
   _o_t_h_e_r _M_a_k_e_s_) _f_r_o_m _b_e_i_n_g _m_i_s_t_a_k_e_n _a_s _n_o_r_m_a_l _t_a_r_g_e_t_s_.

   Both targets and sources may contain  the  standard  C-Shell
   wildcard  characters  ({{,  }},  **, ??, [[, and ]]), but the non-









                                -5-


   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, ssrrcc//{{wwhhiiffffllee,,bbeeeepp,,ffiisshh}}..cc expands to
        the  three   words   ssrrcc//wwhhiiffffllee..cc,   ssrrcc//bbeeeepp..cc,   and
        ssrrcc//ffiisshh..cc.  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  PMake  is
        started.

   **    This  matches  zero  or  more  characters  of any sort.
        ssrrcc//**..cc will expand to the same three words as above as
        long  as  ssrrcc  contains those three files (and no other
        files that end in ..cc).

   ??    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 (aa--zz, for example means all characters between a
        and  z),  or both. It matches any single character con-
        tained in the list. E.g.  [[AA--ZZaa--zz]] will match all  let-
        ters, while [[00112233445566778899]] 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  Bourne  shell
   (better  known  as  ``/bin/sh'')  to  be  executed  and  are
   expected to do what's necessary to update  the  target  file
   (PMake  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 shell script made up of
   one or more of these shell commands. The creation script for
   a  target  should immediately follow the dependency line for
   that target. 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.
   _N_o_t_e










                                -6-


   If  the  double-colon was used, each dependency line for the
   target may be followed by a shell script. That  script  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 earlier.  I'll give you
   a good example of this later on.

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

        pprrooggrraamm         :: aa..oo bb..oo cc..oo
                cccc aa..oo bb..oo cc..oo --oo pprrooggrraamm
        aa..oo bb..oo cc..oo     :: ddeeffss..hh
        aa..oo             :: aa..cc
                cccc --cc aa..cc
        bb..oo             :: bb..cc
                cccc --cc bb..cc
        cc..oo             :: cc..cc
                cccc --cc cc..cc


   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 ``cccc --cc aa..cc'' will be executed  if  aa..oo  is
   out-of-date.  Because  of  the `:' operator, this means that
   should aa..cc _o_r ddeeffss..hh have been modified more  recently  than
   aa..oo,  the  command  will be executed (aa..oo 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 PMake to do dif-
   ferent things.
   _N_E_W CCoommppaattiibbiilliittyy:: _T_h_e _`_@_' _a_n_d _`_-_'  _c_h_a_r_a_t_e_r_s  _m_a_y  _a_c_t_u_a_l_l_y
   _o_c_c_u_r  _i_n_t_e_r_s_p_e_r_s_e_d _w_i_t_h _w_h_i_t_e _s_p_a_c_e_, _a_n_d _i_n_i_t_i_a_l _w_h_i_t_e_s_p_a_c_e
   _i_s _r_e_m_o_v_e_d _f_r_o_m _t_h_e _s_h_e_l_l _c_o_m_m_a_n_d _l_i_n_e_.

   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 eecchhoo command, say ``eecchhoo LLiinnkkiinngg
   iinnddeexx,'' it would be rather silly to see

        eecchhoo LLiinnkkiinngg iinnddeexx
        LLiinnkkiinngg iinnddeexx


   so PMake allows you to  place  an  `@'  before  the  command
   (``@@eecchhoo  LLiinnkkiinngg iinnddeexx'') to prevent the command from being
   printed.









                                -7-


   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, PMake will consider an error to
   have occurred if one of the shells it invokes returns a non-
   zero  status. When it detects an error, PMake'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.
   PMake will exit after the last job finishes).  This behavior
   can be altered, however, by placing a `-' at the front of  a
   command  (``--mmvv  iinnddeexx  iinnddeexx..oolldd''),  certain  command-line
   arguments, or doing other things, to be detailed  later.  In
   such a case, the non-zero status is simply ignored and PMake
   keeps chugging along.
   _N_o_t_e

   Because all the commands are given to a single shell to exe-
   cute,  such  things  as  setting  shell  variables, changing
   directories, etc., last beyond the command in which they are
   found.  This  also  allows shell compound commands (like ffoorr
   loops) to be entered in a natural manner.  Since this  could
   cause  problems  for some makefiles that depend on each com-
   mand being executed by a single shell, PMake has a  --BB  flag
   (it  stands  for backwards-compatible) that forces each com-
   mand to be given to a separate shell. It also  does  several
   other  things,  all of which I discourage since they are now
   old-fashioned....
   _N_o_t_e

   A target's shell script is fed to  the  shell  on  its  (the
   shell's)  input  stream.  This means that any commands, such
   as ccii 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:

        ccii $$((SSRRCCSS)) << //ddeevv//ttttyy

   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  PMake in its fullest compatibility
   mode. See CCoommppaattiibbiilliittyy in chapter 4.
   _N_E_W NNoottee:: _W_h_e_n _e_x_p_o_r_t_i_n_g  _c_o_m_m_a_n_d_s  _t_o  _o_t_h_e_r  _m_a_c_h_i_n_e_s  _t_h_e
   //ddeevv//ttttyy  _t_r_i_c_k  _w_o_n_'_t  _w_o_r_k_, _o_f _c_o_u_r_s_e_.  _S_u_c_h _t_a_r_g_e_t _s_h_o_u_l_d
   _t_h_e_r_e_f_o_r_e _a_l_s_o _b_e _m_a_r_k_e_d _w_i_t_h ..NNOOEEXXPPOORRTT_.


   22..33..  VVaarriiaabblleess

   PMake, like Make before it, has the ability to save text  in
   variables   to   be  recalled  later  at  your  convenience.









                                -8-


   Variables in PMake 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

        VVAARRIIAABBLLEE == vvaalluuee

   appended-to by

        VVAARRIIAABBLLEE ++== vvaalluuee

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

        VVAARRIIAABBLLEE ??== vvaalluuee

   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

        VVAARRIIAABBLLEE ::== vvaalluuee


   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.

   The final way a variable may be assigned to is using

        VVAARRIIAABBLLEE !!== sshheellll--ccoommmmaanndd

   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:

        CCWWDD             !!== ppwwdd

   _O_L_D

   NNoottee:: this is intended to be used to execute  commands  that
   produce  small  amounts of output (e.g. ``pwd''). The imple-
   mentation is less than intelligent and will likely freeze if
   you  execute  something  that produces thousands of bytes of
   output (8 Kb is the limit on many UNIX systems).
   _N_E_W FFiixxeedd:: _A_n_y _a_m_o_u_n_t _o_f _o_u_t_p_u_t _c_a_n _n_o_w _b_e _r_e_a_d  _b_y  _t_h_e  !!==
   _a_s_s_i_g_n_m_e_n_t  _o_p_e_r_a_t_o_r_,  _s_u_b_j_e_c_t  _o_n_l_y  _t_o _m_e_m_o_r_y _l_i_m_i_t_a_t_i_o_n_s_.
   _A_l_s_o_, _n_o_t_e _t_h_a_t _t_h_e _c_o_m_m_a_n_d _f_o_l_l_o_w_i_n_g !!== _i_s _e_x_e_c_u_t_e_d _b_y  _t_h_e
   _p_r_e_v_a_i_l_i_n_g _s_h_e_l_l_, _a_s _s_p_e_c_i_f_i_e_d _b_y _t_h_e ..SSHHEELLLL _t_a_r_g_e_t_.









                                -9-


   The  value  of  a variable may be retrieved by enclosing the
   variable name in parentheses or curly braces and  preceeding
   the whole thing with a dollar sign.

   For  example,  to  set  the  variable  CFLAGS  to the string
   ``--II//sspprriittee//ssrrcc//lliibb//lliibbcc --OO,'' you would place a line

        CCFFLLAAGGSS == --II//sspprriittee//ssrrcc//lliibb//lliibbcc --OO

   in the makefile and use  the  word  $$((CCFFLLAAGGSS))  wherever  you
   would  like  the string --II//sspprriittee//ssrrcc//lliibb//lliibbcc --OO to appear.
   This is called variable expansion.
   _O_L_D

   Unlike Make, PMake will not  expand  a  variable  unless  it
   knows  the  variable  exists.  E.g.  if you have a $${{ii}} in a
   shell command and you have not assigned a value to the vari-
   able ii (the empty string is considered a value, by the way),
   where Make would have substituted the  empty  string,  PMake
   will  leave the $${{ii}} alone.  To keep PMake from substituting
   for a variable  it  knows,  precede  the  dollar  sign  with
   another  dollar  sign.   (e.g. to pass $${{HHOOMMEE}} to the shell,
   use $$$${{HHOOMMEE}}).  This causes PMake, in effect, to expand  the
   $$  macro,  which  expands to a single $$.  For compatibility,
   Make's style of variable  expansion  will  be  used  if  you
   invoke  PMake with any of the compatibility flags (--VV, --BB or
   --MM.  The --VV flag alters just the variable expansion).
   _N_E_W

   CCoommppaattiibbiilliittyy::   _S_i_n_c_e   _n_o_n_-_s_t_a_n_d_a_r_d   _v_a_r_i_a_b_l_e   _e_x_p_a_n_s_i_o_n
   _a_c_c_o_u_n_t_e_d  _f_o_r _t_h_e _v_a_s_t _m_a_j_o_r_i_t_y _o_f _p_r_o_b_l_e_m_s _w_i_t_h _p_r_e_-_e_x_i_s_t_-
   _i_n_g _m_a_k_e_f_i_l_e_s_, _i_t _i_s _n_o_w _d_i_s_a_b_l_e_d _b_y _d_e_f_a_u_l_t_, _a_s  _i_f  _s_p_e_c_i_-
   _f_i_e_d  _b_y --VV_.  _T_o _r_e_-_e_n_a_b_l_e _t_h_e _o_r_i_g_i_n_a_l _P_M_a_k_e _s_t_y_l_e _o_f _v_a_r_i_-
   _a_b_l_e _e_x_p_a_n_s_i_o_n_, _u_s_e _t_h_e --CC _f_l_a_g_.

   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, PMake 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):











                               -10-


        +o Local variables.

        +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.
   _N_E_W  As  in  other  Makes, the --ee flag switches the order in
   which global variables and environment are searched.   (This
   is a compatibility feature that was not documented.)

   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  script  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.  Four local variables
   are defined for all targets. They are:

        .TARGET
             The name of the target.

        .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.

        .ALLSRC
             The  list  of  all  sources for this target in the
             order in which they were given.

        .PREFIX
             _O_L_D The target without its suffix and without  any
             leading  path.  E.g. for the target ....//....//lliibb//ccoomm--
             ppaatt//ffssRReeaadd..cc, this variable would contain  ffssRReeaadd.
             _N_E_W CCoommppaattiibbiilliittyy:: _S_t_r_i_p_p_i_n_g _o_f _d_i_r_e_c_t_o_r_y _p_a_t_h_s _i_n
             _._P_R_E_F_I_X _i_s _a_n_n_o_y_i_n_g_l_y _d_i_f_f_e_r_e_n_t _f_r_o_m _o_t_h_e_r  _m_o_d_e_r_n
             _M_a_k_e_s_.   _D_i_r_e_c_t_o_r_y  _p_a_t_h_s  _a_r_e _t_h_e_r_e_f_o_r_e _r_e_t_a_i_n_e_d_,
             _i_n_c_l_u_d_i_n_g  _t_h_o_s_e  _a_d_d_e_d  _a_s  _t_h_e  _r_e_s_u_l_t  _o_f  _p_a_t_h
             _s_e_a_r_c_h_e_s_.   _T_h_e  _f_i_l_e_n_a_m_e  _p_o_r_t_i_o_n  _o_f _a_n _e_x_p_a_n_d_e_d
             _._P_R_E_F_I_X _c_a_n _e_a_s_i_l_y _b_e  _o_b_t_a_i_n_e_d  _b_y  _a_p_p_l_y_i_n_g  _t_h_e
             _`_:_T_' _m_o_d_i_f_i_e_r_.

   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.









                               -11-


   Four of these variables may be used in sources as well as in
   shell   scripts.    These   are   ``.TARGET'',  ``.PREFIX'',
   ``.ARCHIVE'' and ``.MEMBER''. The variables in  the  sources
   are  expanded  once  for each target on the dependency line,
   providing what is known as a  ``dynamic  source,''  allowing
   you  to  specify several dependency lines at once. For exam-
   ple,

        $$((OOBBJJSS))         :: $$((..PPRREEFFIIXX))..cc

   will create a dependency between each object  file  and  its
   corresponding C source file.
   _N_E_W  _D_u_e  _t_o  _t_h_e _a_b_o_v_e _c_h_a_n_g_e_, _t_h_i_s _d_e_p_e_n_d_e_n_c_y _w_o_u_l_d _l_o_c_a_t_e
   _t_h_e _s_o_u_r_c_e _f_i_l_e_s _i_n _t_h_e _s_a_m_e _d_i_r_e_c_t_o_r_y _a_s _t_h_e _o_b_j_e_c_t  _f_i_l_e_s_.
   _I_f  $$((OOBBJJSS)) _i_n_c_l_u_d_e_s _d_i_r_e_c_t_o_r_i_e_s _p_a_t_h _a_n_d _t_h_e _s_o_u_r_c_e_s _a_r_e _t_o
   _b_e _l_o_c_a_t_e_d _e_l_s_e_w_h_e_r_e_, _u_s_e

        $$((OOBBJJSS))         :: $$((..PPRREEFFIIXX::TT))..cc

   instead.
   _N_E_W

   The seven dynamic variables are also known under their  tra-
   ditional names found in other Makes.  If makefiles are meant
   be compatible the  following  one-character  variable  names
   should be used instead:

        @@    ..TTAARRGGEETT
        ??    ..OOOODDAATTEE
        >>    ..AALLLLSSRRCC
        **    ..PPRREEFFIIXX
        <<    ..IIMMPPSSRRCC
        !!    ..AARRCCHHIIVVEE
        %%    ..MMEEMMBBEERR

   These were always present by undocumented.

   22..33..22..  CCoommmmaanndd--lliinnee VVaarriiaabblleess

   Command-line  variables  are set when PMake is first invoked
   by giving a variable assignment as one of the arguments. For
   example,

        ppmmaakkee ""CCFFLLAAGGSS == --II//sspprriittee//ssrrcc//lliibb//lliibbcc --OO""

   would  make CCFFLLAAGGSS be a command-line variable with the given
   value. Any assignments to CCFFLLAAGGSS 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  as  you would expect them to, mostly because assign-
   ments to command-line variables  are  performed  before  the
   makefile  is  read,  thus the values set in the makefile are









                               -12-


   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 (for reasons  of  effi-
   ciency  that I won't get into here).  ::== 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  PMake 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
   PMake sets (almost) always begin with a  period  and  always
   contain  upper-case letters, only. The variables are as fol-
   lows:

        .PMAKE
             The name by which PMake was invoked is  stored  in
             this variable. For compatibility, the name is also
             stored in the MAKE variable.

        .MAKEFLAGS
             All  the  relevant  flags  with  which  PMake  was
             invoked.  This  does not include such things as --ff
             or variable assignments. Again for  compatibility,
             this  value  is  stored  in the MFLAGS variable as
             well.

   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:

        ##uunnddeeff _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
   PMake  and are given by PMake to each shell it invokes. They
   are expanded like any other variable,  but  they  cannot  be
   altered in any way.

   One  special  environment  variable,  PPMMAAKKEE,  is examined by
   PMake for command-line flags, variable assignments, etc., it
   should  always  use.  This  variable  is examined before the
   actual arguments to PMake are. In addition, all flags  given
   to  PMake,  either  through  the  PPMMAAKKEE  variable  or on the









                               -13-


   command line, are placed in this  environment  variable  and
   exported  to each shell PMake executes. Thus recursive invo-
   cations of PMake automatically receive the same flags as the
   top-most one.

   Using all these variables, you can compress the sample make-
   file even more:

        OOBBJJSS            == aa..oo bb..oo cc..oo
        pprrooggrraamm         :: $$((OOBBJJSS))
                cccc $$((..AALLLLSSRRCC)) --oo $$((..TTAARRGGEETT))
        $$((OOBBJJSS))         :: ddeeffss..hh
        aa..oo             :: aa..cc
                cccc --cc aa..cc
        bb..oo             :: bb..cc
                cccc --cc bb..cc
        cc..oo             :: cc..cc
                cccc --cc cc..cc


   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.  PMake will compress the two into a
   single `#' (Note: this isn't true if PMake is  operating  in
   full-compatibility mode).

   22..55..  PPaarraalllleelliissmm
   _N_o_t_e

   PMake was specifically designed to re-create several targets
   at once, when possible. You do not have to do anything  spe-
   cial to cause this to happen (unless PMake was configured to
   not act in parallel, in which case you will have to make use
   of  the  --LL and --JJ flags (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  xxssttrr,  for example, always modifies the
   files ssttrriinnggss and xx..cc.  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 $$$$).  Since only one shell  is









                               -14-


   used for all the commands, you'll get the same file name for
   each command in the script.

   The other problem comes from improperly-specified  dependen-
   cies  that  worked in Make because of its sequential, depth-
   first way of examining them. While I don't want to  go  into
   depth on how PMake works (look in chapter 4 if you're inter-
   ested), I will warn you that files in two  different  ``lev-
   els''  of the dependency tree may be examined in a different
   order in PMake than they were in Make.  For  example,  given
   the makefile

        aa               :: bb cc
        bb               :: dd

   PMake  will examine the targets in the order cc, dd, bb, aa.  If
   the makefile's author expected PMake to abort before  making
   cc  if  an  error  occurred while making bb, or if bb needed to
   exist before cc was made, s/he will be  sorely  disappointed.
   The  dependencies are incomplete, since in both these cases,
   cc would depend on bb.  So watch out.

   Another problem you may face is that, while PMake 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 //ddeevv//ttttyy 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  PMake  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,  eexxpprr,  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: mmaaiinn..cc,
   ppaarrssee..cc, and oouuttppuutt..cc.  Harking  back  to  my  pithy  advice
   about  dependency  lines,  you  write  the first line of the
   file:

        eexxpprr            :: mmaaiinn..oo ppaarrssee..oo oouuttppuutt..oo

   because you remember eexxpprr is made  from  ..oo  files,  not  ..cc









                               -15-


   files. Similarly for the ..oo files you produce the lines:

        mmaaiinn..oo          :: mmaaiinn..cc
        ppaarrssee..oo         :: ppaarrssee..cc
        oouuttppuutt..oo        :: oouuttppuutt..cc
        mmaaiinn..oo ppaarrssee..oo oouuttppuutt..oo :: ddeeffss..hh


   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

        eexxpprr            :: mmaaiinn..oo ppaarrssee..oo oouuttppuutt..oo
                cccc --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))

   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
   ddeeffss..hh, thus if you said

             cccc --cc $$((..AALLLLSSRRCC))

   you'd get (for mmaaiinn..oo):

             cccc --cc mmaaiinn..cc ddeeffss..hh

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

        mmaaiinn..oo          :: mmaaiinn..cc
                cccc --cc mmaaiinn..cc
        ppaarrssee..oo         :: ppaarrssee..cc
                cccc --cc ppaarrssee..cc
        oouuttppuutt..oo        :: oouuttppuutt..cc
                cccc --cc oouuttppuutt..cc


   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  ``mmaaiinn..oo  ppaarrssee..oo  oouuttppuutt..oo''  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:











                               -16-


        _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:

        OOBBJJSS            == mmaaiinn..oo ppaarrssee..oo oouuttppuutt..oo
        eexxpprr            :: $$((OOBBJJSS))
                $$((CCCC)) $$((CCFFLLAAGGSS)) --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
        mmaaiinn..oo          :: mmaaiinn..cc
                $$((CCCC)) $$((CCFFLLAAGGSS)) --cc mmaaiinn..cc
        ppaarrssee..oo         :: ppaarrssee..cc
                $$((CCCC)) $$((CCFFLLAAGGSS)) --cc ppaarrssee..cc
        oouuttppuutt..oo        :: oouuttppuutt..cc
                $$((CCCC)) $$((CCFFLLAAGGSS)) --cc oouuttppuutt..cc
        $$((OOBBJJSS))         :: ddeeffss..hh

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

        OOBBJJSS            == mmaaiinn..oo ppaarrssee..oo oouuttppuutt..oo
        eexxpprr            :: $$((OOBBJJSS))
                $$((CCCC)) $$((CCFFLLAAGGSS)) --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
        $$((OOBBJJSS))         :: $$((..PPRREEFFIIXX))..cc ddeeffss..hh
                $$((CCCC)) $$((CCFFLLAAGGSS)) --cc $$((..PPRREEFFIIXX))..cc

   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
   PMake  will  choke  on the makefile. The second thing the --nn
   flag lets you do is see what PMake would do without it actu-
   ally  doing  it,  thus  you can make sure the right commands
   would be executed were you to give PMake its head.











                               -17-


   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, two flags will
   serve  you  well:  ``--dd  mm'' and ``--pp 22.''  The first causes
   PMake 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.  The ``--pp 22'' flag makes PMake
   print out its internal state when it is done,  allowing  you
   to  see  that  you forgot to make that one chapter depend on
   that file of macros you just got a new version of. The  out-
   put  from  ``--pp  22''  is intended to resemble closely a real
   makefile, but with additional information provided and  with
   variables  expanded in those commands PMake actually printed
   or executed.

   Something to be especially careful about is circular  depen-
   dencies.  E.g.

        aa         :: bb
        bb         :: cc dd
        dd         :: aa

   In  this  case,  because  of  how PMake works, cc is the only
   thing PMake will examine, because dd and aa  will  effectively
   fall  off  the edge of the universe, making it impossible to
   examine bb (or them, for that matter).  PMake 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, PMake 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 PPMMaakkee

   PMake 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.  The  flags  are
   as follows:

   --dd _w_h_a_t
        This  causes  PMake  to  spew out debugging information
        that may prove useful to you. If you can't  figure  out
        why PMake is doing what it's doing, you might try using
        this flag. The _w_h_a_t parameter is  a  string  of  single
        characters  that tell PMake what aspects you are inter-
        ested in. 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:











                               -18-


        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.

        q    Job queue maintenance.

        r    Remote execution.

        s    The application  of  suffix-transformation  rules.
             (See chapter 3)

        t    The maintenance of the list of targets.

        v    Variable assignment.

        *    All  of the above, resulting in massive amounts of
             output (`*' may need to be quoted in the shell).

        Of these all, the mm and ss letters will be  most  useful
        to you.

   --ee   Causes global variable values defined in the UNIX envi-
        ronment to take precedence over any  assignments  found
        in the makefile.
        _N_E_W  _T_h_i_s _f_l_a_g _w_a_s _p_r_e_v_i_o_u_s_l_y _p_r_e_s_e_n_t _b_u_t _u_n_d_o_c_u_m_e_n_t_e_d_.

   --ff _m_a_k_e_f_i_l_e
        Specify a makefile to read different from the  standard
        makefiles  (MMaakkeeffiillee  or  mmaakkeeffiillee).   If  _m_a_k_e_f_i_l_e  is
        ``-'', PMake uses the standard input.  This  is  useful
        for making quick and dirty makefiles...

   --hh   Prints  out  a  summary  of  the  various  flags  PMake
        accepts. It can also be used to find out what level  of
        concurrency  was compiled into the version of PMake you
        are using (look at --JJ and --LL) and various other  infor-
        mation on how PMake was configured.

   --ii   If  you give this flag, PMake will ignore non-zero sta-
        tus returned by any of its shells. It's like placing  a
        `-' before all the commands in the makefile.










                               -19-


   --kk   This  is  similar to --ii in that it allows PMake to con-
        tinue when it sees an error, but unlike --ii, where PMake
        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''...

   --ll   PMake has the ability to lock a directory against other
        people  executing it in the same directory (by means of
        a file called ``LOCK.make'' that it creates and  checks
        for in the directory). This is a Good Thing because two
        people doing the same thing in the same  place  can  be
        disastrous  for  the  final product (too many cooks and
        all that).  Whether this locking is the default  is  up
        to your system administrator. If locking is on, --ll will
        turn it off, and vice versa.  Note  that  this  locking
        will  not  prevent _y_o_u from invoking PMake twice in the
        same place -- if you own the lock file, PMake will warn
        you about it but continue to execute.
        _N_E_W  _W_h_e_n  _r_u_n_n_i_n_g  _a_s  _s_u_p_e_r_-_u_s_e_r_, _P_M_a_k_e _w_i_l_l _c_o_n_t_i_n_u_e
        _i_n_s_p_i_t_e _o_f _a _f_a_i_l_u_r_e _t_o _c_r_e_a_t_e _t_h_e _l_o_c_k _f_i_l_e_, _i_s_s_u_i_n_g _a
        _w_a_r_n_i_n_g  _t_o _t_h_a_t _e_f_f_e_c_t_.  _F_r_e_q_u_e_n_t_l_y _t_h_e _r_o_o_t _u_s_e_r _c_a_n_-
        _n_o_t _c_r_e_a_t_e _f_i_l_e_s _i_n _s_o_u_r_c_e  _t_r_e_e_s  _m_o_u_n_t_e_d  _f_r_o_m  _o_t_h_e_r
        _s_y_s_t_e_m_s_,  _e_v_e_n  _i_f  _a_l_l _t_h_a_t_'_s _w_a_n_t_e_d _i_s _a _s_i_m_p_l_e _`_m_a_k_e
        _i_n_s_t_a_l_l_._'

   --nn   This flag tells  PMake  not  to  execute  the  commands
        needed  to  update the out-of-date targets in the make-
        file. Rather, PMake will simply print the  commands  it
        would have executed and exit. This is particularly use-
        ful for checking the  correctness  of  a  makefile.  If
        PMake  doesn't  do  what  you expect it to, it's a good
        chance the makefile is wrong.

   --pp _n_u_m_b_e_r
        This causes PMake to print its input  in  a  reasonable
        form,  though not necessarily one that would make imme-
        diate sense to anyone but me. The _n_u_m_b_e_r is a  bitwise-
        or  of  1 and 2 where 1 means it should print the input
        before doing any processing and 2 says it should  print
        it  after  everything  has  been  re-created. Thus --pp 33
        would print it twice--once before processing  and  once
        after  (you  might  find the difference between the two
        interesting). This is mostly useful to me, but you  may
        find it informative in some bizarre circumstances.

   --qq   If you give PMake 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  PMake starts up, it reads a default makefile that
        tells it what sort of system it's on and gives it  some









                               -20-


        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,
        PMake won't read the default makefile.

   --ss   This  causes PMake 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, PMake will sim-
        ply ``touch'' it so as to make it appear up-to-date. If
        the target didn't exist before, it will when PMake fin-
        ishes, but if the target did exist, it will  appear  to
        have been updated.

   --vv   This  is  a  mixed-compatibility flag intended to mimic
        the System V version of Make. It is the same as  giving
        --BB,  and  --VV  as well as turning off directory locking.
        Targets can still be created in parallel, however. This
        is the mode PMake will enter if it is invoked either as
        ``ssmmaakkee'' or ``vvmmaakkee''.

   --xx   This tells PMake  it's  ok  to  export  jobs  to  other
        machines, if they're available. It is used when running
        in Make mode, as exporting in this mode tends  to  make
        things  run  slower than if the commands were just exe-
        cuted locally.

   --BB   Forces PMake to be as backwards-compatible with Make as
        possible while still being itself.  This includes:

        +o Executing one shell per shell command

        +o Expanding  anything  that  looks  even vaguely like a
          variable, with the empty string replacing  any  vari-
          able PMake doesn't know.

        +o Refusing  to  allow  you to escape a `#' with a back-
          slash.

        +o Permitting undefined variables  on  dependency  lines
          and  conditionals  (see  below). Normally this causes
          PMake to abort.

   --CC   This nullifies any and all compatibility mode flags you
        may  have  given  or  implied  up to the time the --CC is
        encountered. It is useful mostly in a makefile that you
        wrote  for  PMake  to  avoid  bad things happening when
        someone runs PMake as ``mmaakkee'' or has things set in the
        environment  that  tell it to be compatible.  --CC is _n_o_t
        placed in the PPMMAAKKEE environment variable or the  ..MMAAKKEE--
        FFLLAAGGSS or MMFFLLAAGGSS global variables.

   --DD _v_a_r_i_a_b_l_e
        Allows  you  to  define a variable to have ``11'' as its









                               -21-


        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  PMake another place to search for included make-
        files. Yet another thing to be explained in  chapter  3
        (section 3.2, to be precise).

   --JJ _n_u_m_b_e_r
        Gives  the absolute maximum number of targets to create
        at once on both local and remote machines.

   --LL _n_u_m_b_e_r
        This specifies the maximum number of targets to  create
        on the local machine at once. This may be 0, though you
        should be wary of doing this, as PMake may hang until a
        remote  machine becomes available, if one is not avail-
        able when it is started.
        _N_E_W _A _n_e_g_a_t_i_v_e _a_r_g_u_m_e_n_t _t_o _t_h_i_s _f_l_a_g _l_i_m_i_t_s _t_h_e  _n_u_m_b_e_r
        _o_f  _l_o_c_a_l _j_o_b_s _(_t_o _t_h_e _a_b_s_o_l_u_t_e _v_a_l_u_e_, _o_f _c_o_u_r_s_e_) _w_h_i_l_e
        _a_l_s_o _f_o_r_c_i_n_g _a_l_l _l_o_c_a_l _j_o_b_s _s_l_o_t_s _t_o _b_e  _f_i_l_l_e_d  _b_e_f_o_r_e
        _e_x_p_o_r_t_a_t_i_o_n  _c_a_n _b_e_g_i_n_.  _T_h_i_s _i_s _u_s_e_f_u_l _t_o _f_o_r_c_e _u_s_e _o_f
        _o_n_e_'_s _o_w_n _m_a_c_h_i_n_e _i_n _c_a_s_e_s _w_h_e_r_e  _i_t  _i_s  _k_n_o_w_n  _t_o  _b_e
        _f_a_s_t_e_r _t_h_a_n _o_t_h_e_r_s_.

   --MM   This is the flag that provides absolute, complete, full
        compatibility with Make. It still allows you to use all
        but  a few of the features of PMake, but it is non-par-
        allel. This is the mode PMake enters  if  you  call  it
        ``mmaakkee.''

   --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 PMake in some way  in  order  to  prevent  the
        screen from being filled with garbage even more indeci-
        pherable than you usually see. PMake 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 happpening.
        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 PMake.
        _N_E_W _W_h_e_n _e_x_p_o_r_t_i_n_g _c_o_m_m_a_n_d_s  _u_s_i_n_g  _p_i_p_e_d  _o_u_t_p_u_t_,  _t_h_e









                               -22-


        _i_d_e_n_t_i_f_i_c_a_t_i_o_n  _l_i_n_e_s  _a_l_s_o  _c_o_n_t_a_i_n  _t_h_e  _n_a_m_e  _o_f _t_h_e
        _r_e_m_o_t_e _h_o_s_t _e_x_e_c_u_t_i_n_g _t_h_e _c_o_m_m_a_n_d_.
        _N_E_W

   --RR _w_h_e_n
        Standard Make rechecks the date on a target  after  its
        recreation  the see whether it actually changed, avoid-
        ing recreating dependent targets if not.  PMake  cannot
        use  use  this strategy for targets created by exported
        commands since network filesystems are  typically  slow
        in  propagating  modification  times.   The  value _w_h_e_n
        determines how  to  handle  this  problem:  ``a''  will
        always  recheck file dates, ``l'' only for locally cre-
        ated  targets  (the  default),  and  ``n(ever)''  never
        rechecks, always assuming an update occurred.

   --SS   This  overrides  a previous --kk flag and instructs PMake
        to abort as soon  as  a  target  creation  command  has
        failed.
        _N_E_W  _T_h_i_s _f_l_a_g _w_a_s _p_r_e_v_i_o_u_s_l_y _p_r_e_s_e_n_t _b_u_t _u_n_d_o_c_u_m_e_n_t_e_d_.

   --VV   As mentioned before, the --VV flag  tells  PMake  to  use
        Make's  style  of expanding variables, substituting the
        empty string for any variable it doesn't know.
        _N_E_W _A_s _l_i_k_e_w_i_s_e _m_e_n_t_i_o_n_e_d  _b_e_f_o_r_e_,  _t_h_i_s  _f_l_a_g  _i_s  _n_o_w
        _t_u_r_n_e_d _o_n _b_y _d_e_f_a_u_l_t _i_n _t_h_e _s_t_a_n_d_a_r_d _c_o_n_f_i_g_u_r_a_t_i_o_n_.  _T_o
        _r_e_v_e_r_t _t_o _P_M_a_k_e_-_s_t_y_l_e _v_a_r_i_a_b_l_e _e_x_p_a_n_s_i_o_n _u_s_e --CC_.

   --WW   There are several times when PMake will print a message
        at  you that is only a warning, i.e. it can continue to
        work in spite of your having done something silly (such
        as  forgotten a leading tab for a shell command). Some-
        times you are well aware of silly things you have  done
        and  would  like PMake to stop bothering you. This flag
        tells it to shut up about anything non-fatal.

   --XX   This flag causes PMake to not  attempt  to  export  any
        jobs to another machine.
        _N_E_W

   --ZZ _c Changes the character used to introduce Makefile direc-
        tives to _c, from the default ``#''.  This can  be  used
        process  to  BSD makefiles, which use ``.'', or to dis-
        able directives, by choosing some unlikely character.

   Several flags may follow a  single  `-'.  Those  flags  that
   require arguments take them from successive parameters. E.g.

        ppmmaakkee --ffDDnnII sseerrvveerr..mmkk DDEEBBUUGG //cchhiipp22//XX//sseerrvveerr//iinncclluuddee

   will cause PMake to read sseerrvveerr..mmkk as  the  input  makefile,
   define  the variable DDEEBBUUGG as a global variable and look for
   included makefiles in the directory //cchhiipp22//XX//sseerrvveerr//iinncclluuddee.
   _N_E_W









                               -23-


   FFiixxeedd::  _T_h_e _o_r_i_g_i_n_a_l _v_e_r_s_i_o_n _o_f _P_M_a_k_e _i_n _f_a_c_t _d_i_d _n_o_t _h_a_n_d_l_e
   _f_l_a_g_s _i_n_t_e_r_s_p_e_r_s_e_d _w_i_t_h _v_a_r_i_a_b_l_e  _a_s_s_i_g_n_m_e_n_t_s  _a_n_d  _t_a_r_g_e_t_s_,
   _w_h_i_c_h  _w_a_s _a _m_a_j_o_r _a_n_n_o_y_a_n_c_e _a_n_d _i_n_c_o_m_p_a_t_i_b_i_l_i_t_y_, _e_s_p_e_c_i_a_l_l_y
   _i_n _r_e_c_u_r_s_i_v_e _m_a_k_e _c_o_m_m_a_n_d_s_.

   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  `--',
   PMake will cause the command to not be printed (if the char-
   acter is `@@') or errors from it to be ignored (if  `--').   A
   blank  line,  dependency  line or variable assignment termi-
   nates 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,  prceeded  by  a  dollar sign.  A dollar sign may be
   escaped with another dollar sign.   (In  native  PMake  mode
   (--CC)  variables are not expanded if PMake doesn't know about
   them.)  There are seven local variables:  ..TTAARRGGEETT,  ..AALLLLSSRRCC,
   ..OOOODDAATTEE,  ..PPRREEFFIIXX,  ..IIMMPPSSRRCC, ..AARRCCHHIIVVEE, and ..MMEEMMBBEERR.  Four of
   them (..TTAARRGGEETT, ..PPRREEFFIIXX, ..AARRCCHHIIVVEE, and ..MMEEMMBBEERR) 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, --dd
   mm, and --pp 22 flags.












                               -24-


   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 PMake 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
   easier  and  the  makefiles themselves shorter and easier to
   modify (and, occasionally,  simpler).  In  this  chapter,  I
   assume  you are somewhat more familiar with Sprite (or UNIX,
   if that's what you're using) than I did in chapter  2,  just
   so you're on your toes.  So without further 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 ..cc is assumed to contain C source code; one with a
   ..oo suffix is assumed to be a  compiled,  relocatable  object
   file  that may be linked into any program; a file with a ..mmss
   suffix is usually a text file to be processed by Troff  with
   the  -ms  macro package, and so on.  One of the best aspects
   of both Make and PMake comes from their understanding of how
   the  suffix  of  a  file  pertains to its contents and their
   ability to do things with a file based soley on its  suffix.
   This  ability comes from something 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 PMake by placing them
   as sources on a dependency line whose target is the  special
   target ..SSUUFFFFIIXXEESS.  E.g.

        ..SSUUFFFFIIXXEESS       :: ..oo ..cc
        ..cc..oo            ::
                $$((CCCC)) $$((CCFFLLAAGGSS)) --cc $$((..IIMMPPSSRRCC))

   The creation script attached to the target is used to trans-
   form a file with the first suffix (in this case, ..cc) into  a
   file  with  the  second suffix (here, ..oo).  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  cccc  with  the  --cc flag.  This rule is taken









                               -25-


   straight from the system makefile. Many transformation rules
   (and  suffixes) are defined there, and I refer you to it for
   more examples (type ``ppmmaakkee --hh'' to find out where it is).

   There are several things to note  about  the  transformation
   rule given above:

        1)   The ..IIMMPPSSRRCC 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 CCFFLLAAGGSS 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
             you want all your C files to be compiled with  the
             --gg flag, to provide information for ddbbxx, you would
             set the CCFFLLAAGGSS variable to contain --gg (``CCFFLLAAGGSS  ==
             --gg'') and PMake would take care of the rest.

   To  give you a quick example, the makefile in 2.3.4 could be
   changed to this:

        OOBBJJSS            == aa..oo bb..oo cc..oo
        pprrooggrraamm         :: $$((OOBBJJSS))
                $$((CCCC)) --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
        $$((OOBBJJSS))         :: ddeeffss..hh

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

        aa..oo             :: aa..cc
                cccc --cc aa..cc
        bb..oo             :: bb..cc
                cccc --cc bb..cc
        cc..oo             :: cc..cc
                cccc --cc cc..cc


   Now you may be wondering about the dependency between the ..oo
   and ..cc 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.
   _N_E_W  _A_s  _b_e_f_o_r_e_,  _t_r_a_n_s_f_o_r_m_a_t_i_o_n _r_u_l_e_s _a_r_e _n_o_t _a_p_p_l_i_e_d _i_f _a_t
   _l_e_a_s_t _o_n_e _e_x_p_l_i_c_i_t _d_e_p_e_n_d_e_n_c_y _w_i_t_h _c_r_e_a_t_i_o_n _c_o_m_m_a_n_d _i_s _s_p_e_c_-
   _i_f_i_e_d  _f_o_r  _a  _t_a_r_g_e_t_.  _H_o_w_e_v_e_r_, _P_M_a_k_e _u_s_e_d _t_o _n_o_t _o_b_e_y _t_h_i_s
   _s_t_i_p_u_l_a_t_i_o_n _i_n _t_h_e _c_a_s_e _o_f _t_h_e _e_x_p_l_i_c_i_t _n_u_l_l _c_o_m_m_a_n_d_,  _w_h_i_c_h
   _i_s _o_f_t_e_n _u_s_e_d _t_o _i_n_h_i_b_i_t _i_m_p_l_i_c_i_t _d_e_p_e_n_d_e_n_c_i_e_s_:
   -----------
     1 This  is  also somewhat cleaner, I think, than
   the dynamic source solution presented in 2.6









                               -26-


        dd..oo:: ;;


   For  a  more  detailed example. Say you have a makefile like
   this:

        aa..oouutt           :: aa..oo bb..oo
                $$((CCCC)) $$((..AALLLLSSRRCC))

   and a directory set up like this:

        ttoottaall 44
        --rrww--rrww--rr----  11 ddeebboooorr         3344 SSeepp  77 0000::4433 MMaakkeeffiillee
        --rrww--rrww--rr----  11 ddeebboooorr        111199 OOcctt  33 1199::3399 aa..cc
        --rrww--rrww--rr----  11 ddeebboooorr        220011 SSeepp  77 0000::4433 aa..oo
        --rrww--rrww--rr----  11 ddeebboooorr         6699 SSeepp  77 0000::4433 bb..cc

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

        SSuuffff__FFiinnddDDeeppss ((aa..oouutt))
             uussiinngg eexxiissttiinngg ssoouurrccee aa..oo
             aappppllyyiinngg ..oo -->> ..oouutt ttoo ""aa..oo""
        SSuuffff__FFiinnddDDeeppss ((aa..oo))
             ttrryyiinngg aa..cc......ggoott iitt
             aappppllyyiinngg ..cc -->> ..oo ttoo ""aa..cc""
        SSuuffff__FFiinnddDDeeppss ((bb..oo))
             ttrryyiinngg bb..cc......ggoott iitt
             aappppllyyiinngg ..cc -->> ..oo ttoo ""bb..cc""
        SSuuffff__FFiinnddDDeeppss ((aa..cc))
             ttrryyiinngg aa..yy......nnoott tthheerree
             ttrryyiinngg aa..ll......nnoott tthheerree
             ttrryyiinngg aa..cc,,vv......nnoott tthheerree
             ttrryyiinngg aa..yy,,vv......nnoott tthheerree
             ttrryyiinngg aa..ll,,vv......nnoott tthheerree
        SSuuffff__FFiinnddDDeeppss ((bb..cc))
             ttrryyiinngg bb..yy......nnoott tthheerree
             ttrryyiinngg bb..ll......nnoott tthheerree
             ttrryyiinngg bb..cc,,vv......nnoott tthheerree
             ttrryyiinngg bb..yy,,vv......nnoott tthheerree
             ttrryyiinngg bb..ll,,vv......nnoott tthheerree
        ------ aa..oo ------
        cccc  --cc aa..cc
        ------ bb..oo ------
        cccc  --cc bb..cc
        ------ aa..oouutt ------
        cccc aa..oo bb..oo


   SSuuffff__FFiinnddDDeeppss  is  the  name  of a function in PMake that is
   called to check for  implied  sources  for  a  target  using
   transformation  rules.   The  transformations  it tries are,









                               -27-


   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 ..SSUUFFFFIIXXEESS 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 ..SSUUFFFFIIXXEESS 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, aa..oouutt depends on aa..oo
   and bb..oo.  Since a transformation exists  from  ..oo  to  ..oouutt,
   PMake uses that, as indicated by the ``uussiinngg eexxiissttiinngg ssoouurrccee
   aa..oo'' 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,
   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:

        ..SSUUFFFFIIXXEESS       :: ..oouutt ..oo ..cc ..yy ..ll
        ..ll..cc            ::
                lleexx $$((..IIMMPPSSRRCC))
                mmvv lleexx..yyyy..cc $$((..TTAARRGGEETT))
        ..yy..cc            ::
                yyaacccc $$((..IIMMPPSSRRCC))
                mmvv yy..ttaabb..cc $$((..TTAARRGGEETT))
        ..cc..oo            ::
                cccc --cc $$((..IIMMPPSSRRCC))
        ..oo..oouutt          ::
                cccc --oo $$((..TTAARRGGEETT)) $$((..IIMMPPSSRRCC))

   and the single file jjiivvee..ll.  If you were to type ``ppmmaakkee --rrdd
   mmss jjiivvee..oouutt,''  you  would  get  the  following  output  for
   jjiivvee..oouutt:

















                               -28-


        SSuuffff__FFiinnddDDeeppss ((jjiivvee..oouutt))
             ttrryyiinngg jjiivvee..oo......nnoott tthheerree
             ttrryyiinngg jjiivvee..cc......nnoott tthheerree
             ttrryyiinngg jjiivvee..yy......nnoott tthheerree
             ttrryyiinngg jjiivvee..ll......ggoott iitt
             aappppllyyiinngg ..ll -->> ..cc ttoo ""jjiivvee..ll""
             aappppllyyiinngg ..cc -->> ..oo ttoo ""jjiivvee..cc""
             aappppllyyiinngg ..oo -->> ..oouutt ttoo ""jjiivvee..oo""

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

        SSuuffff__FFiinnddDDeeppss ((jjiivvee..oo))
             uussiinngg eexxiissttiinngg ssoouurrccee jjiivvee..cc
             aappppllyyiinngg ..cc -->> ..oo ttoo ""jjiivvee..cc""
        SSuuffff__FFiinnddDDeeppss ((jjiivvee..cc))
             uussiinngg eexxiissttiinngg ssoouurrccee jjiivvee..ll
             aappppllyyiinngg ..ll -->> ..cc ttoo ""jjiivvee..ll""
        SSuuffff__FFiinnddDDeeppss ((jjiivvee..ll))
        EExxaammiinniinngg jjiivvee..ll......mmooddiiffiieedd 1177::1166::0011 OOcctt 44,, 11998877......uupp--ttoo--ddaattee
        EExxaammiinniinngg jjiivvee..cc......nnoonn--eexxiisstteenntt......oouutt--ooff--ddaattee
        ------ jjiivvee..cc ------
        lleexx jjiivvee..ll
        ...... mmeeaanniinngglleessss lleexx oouuttppuutt ddeelleetteedd ......
        mmvv lleexx..yyyy..cc jjiivvee..cc
        EExxaammiinniinngg jjiivvee..oo......nnoonn--eexxiisstteenntt......oouutt--ooff--ddaattee
        ------ jjiivvee..oo ------
        cccc --cc jjiivvee..cc
        EExxaammiinniinngg jjiivvee..oouutt......nnoonn--eexxiisstteenntt......oouutt--ooff--ddaattee
        ------ jjiivvee..oouutt ------
        cccc --oo jjiivvee..oouutt jjiivvee..oo


   One final question remains: what does PMake do with  targets
   that have no known suffix? PMake simply pretends it actually
   has a known suffix and searches for transformations  accord-
   ingly.   The  suffix  it chooses is the source for the ..NNUULLLL
   target mentioned later. In the system makefile, ..oouutt is cho-
   sen  as the ``null suffix'' because most people use PMake to
   create programs. You  are,  however,  free  and  welcome  to









                               -29-


   change it to a suffix of your own choosing.  The null suffix
   is ignored, however, when PMake  is  in  compatibility  mode
   (see chapter 4).

   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

        ##iinncclluuddee ""ddeeffss..hh""

   to include the contents of ddeeffss..hh in the source file.  PMake
   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:

        ##iinncclluuddee <<ffiillee>>

   or this

        ##iinncclluuddee ""ffiillee""

   The  difference  between the two is where PMake searches for
   the file: the first way, PMake will look for the  file  only
   in  the  system  makefile  directory  (to find out what that
   directory is, give PMake the --hh flag).  For files in double-
   quotes, the search is more complex:

        1)   The directory of the makefile that's including the
             file.

        2)   The  current  directory  (the  one  in  which  you
             invoked PMake).

        3)   The  directories  given  by you using --II flags, in
             the order in which you gave them.

        4)   Directories given by ..PPAATTHH dependency  lines  (see
             chapter 4).

        5)   The system makefile directory.

   in that order.
   _N_E_W FFiixxeedd:: _P_M_a_k_e _w_i_l_l _n_o_w _r_e_f_u_s_e _t_o _i_n_c_l_u_d_e _d_i_r_e_c_t_o_r_y _f_i_l_e_s_.
   _A_l_s_o_, --ddpp _a_l_l_o_w_s _t_r_a_c_i_n_g _o_f _f_i_l_e _i_n_c_l_u_s_i_o_n_s_.

   You are free to use PMake variables in  the  filename--PMake
   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










                               -30-


        SSYYSSTTEEMM    == <<ccoommmmaanndd..mmkk>>

        ##iinncclluuddee $$((SSYYSSTTEEMM))

   won't work.
   _N_E_W

   CCoommppaattiibbiilliittyy:: _T_w_o _i_n_c_l_u_d_e _d_i_r_e_c_t_i_v_e_s  _f_r_e_q_u_e_n_t_l_y  _f_o_u_n_d  _i_n
   _o_t_h_e_r  _M_a_k_e_s  _a_r_e _a_l_s_o _s_u_p_p_o_r_t_e_d _(_n_o_t_e _t_h_e _m_i_s_s_i_n_g _`_`_#_'_' _a_n_d
   _q_u_o_t_e_s_)_:

        iinncclluuddee ffiillee

   for simple inclusions, and

        --iinncclluuddee ffiillee

   for inclusions that are ignored if the ffiillee  is  not  found.
   The  effect  of --iinncclluuddee is better achieved using the native
   PMake construction

        ##iiff eexxiissttss((ffiillee))
        ##iinncclluuddee ""ffiillee""
        ##eennddiiff


   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, rraannlliibb 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  ____..SSYYMMDDEEFF  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. PMake 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 rraannlliibb case above, you might do this:


















                               -31-


        lliibb11..aa          :: $$((LLIIBB11OOBBJJSS))
                rrmm --ff $$((..TTAARRGGEETT))
                aarr ccrr $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
                ......
                rraannlliibb $$((..TTAARRGGEETT))

        lliibb22..aa          :: $$((LLIIBB22OOBBJJSS))
                rrmm --ff $$((..TTAARRGGEETT))
                aarr ccrr $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
                ......
                rraannlliibb $$((..TTAARRGGEETT))

   This would save both

        rraannlliibb $$((..TTAARRGGEETT))

   commands until the end, when they would run  one  after  the
   other  (using the correct value for the ..TTAARRGGEETT variable, of
   course).
   _N_E_W NNoottee:: _A_n _i_m_p_o_r_t_a_n_t _f_e_a_t_u_r_e _(_s_o_m_e  _m_i_g_h_t  _s_a_y  _b_u_g_)  _w_i_t_h
   _t_h_i_s _m_e_c_h_a_n_i_s_m _i_s _t_h_a_t _t_h_e _d_e_f_e_r_r_e_d _c_o_m_m_a_n_d_s _a_r_e _r_u_n _a_t _v_e_r_y
   _e_n_d _o_f _t_h_e _e_n_t_i_r_e _P_M_a_k_e _p_r_o_c_e_s_s_, _n_o_t _n_e_c_e_s_s_a_r_i_l_y _i_m_m_e_d_i_a_t_e_l_y
   _a_f_t_e_r  _t_h_e _l_a_s_t _c_r_e_a_t_i_o_n _c_o_m_m_a_n_d _f_o_r _t_h_e _t_a_r_g_e_t _i_n _q_u_e_s_t_i_o_n_.
   _T_h_i_s _m_a_k_e_s _i_t _n_e_c_e_s_s_a_r_y _t_o _e_m_b_e_d _s_u_c_h _d_e_f_e_r_r_e_d  _c_o_m_m_a_n_d_s  _i_n
   _r_e_c_u_r_s_i_v_e  _m_a_k_e _r_u_n_s _i_f _o_t_h_e_r _t_a_r_g_e_t_s _d_e_p_e_n_d _o_n _t_h_e _e_n_d _p_r_o_-
   _c_e_s_s_i_n_g _h_a_v_i_n_g _t_a_k_e_n _p_l_a_c_e _(_o_r _u_s_e _a_n  _a_l_t_e_r_n_a_t_i_v_e  _f_o_r_m_u_l_a_-
   _t_i_o_n_, _e_._g_._, _u_s_i_n_g _t_h_e ..JJOOIINN _a_t_t_r_i_b_u_t_e_)_.

   Commands  saved  in  this  manner are only executed if PMake
   manages to re-create everything without an error.

   33..44..  TTaarrggeett AAttttrriibbuutteess

   PMake allows you to give attributes to targets by  means  of
   special  sources.  Like  everything  else  PMake 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
                PMake 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 PMake just
                can't  find  it.  This may prove wrong, but the
                error will be noted later on,  not  when  PMake









                               -32-


                tries  to  create  the  target  so marked. This
                attribute also prevents PMake  from  attempting
                to touch the target if it is given the --tt flag.
                _N_E_W

   .OPTIONAL    CCoommppaattiibbiilliittyy:: This is a synonym for  ..DDOONNTTCCAARREE
                found in 4.4BSD make.

   .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 CCOOMMPPIILLEE rule, all will soon  become  clear,
                grasshopper):

                     ssyysstteemm          :: iinniitt aa..ffaassll bb..ffaassll cc..ffaassll
                             ffoorr ii iinn $$((..AALLLLSSRRCC));;
                             ddoo
                                     eecchhoo --nn ''((llooaadd ""'' >>>> iinnppuutt
                                     eecchhoo --nn $${{ii}} >>>> iinnppuutt
                                     eecchhoo ''""))'' >>>> iinnppuutt
                             ddoonnee
                             eecchhoo ''((dduummpp ""$$((..TTAARRGGEETT))""))'' >>>> iinnppuutt
                             lliisspp << iinnppuutt

                     aa..ffaassll          :: aa..ll iinniitt CCOOMMPPIILLEE
                     bb..ffaassll          :: bb..ll iinniitt CCOOMMPPIILLEE
                     cc..ffaassll          :: cc..ll iinniitt CCOOMMPPIILLEE
                     CCOOMMPPIILLEE         :: ..UUSSEE
                             eecchhoo ''((ccoommppiillee ""$$((..AALLLLSSRRCC))""))'' >>>> iinnppuutt
                     iinniitt            :: ..EEXXEECC
                             eecchhoo ''((llooaadd--ssyysstteemm))'' >> iinnppuutt


                ..EEXXEECC  sources  don't appear in the local vari-
                ables of targets that depend on them  (nor  are
                they  touched  if  PMake is given the --tt flag).
                Note that all the rules, not just that for ssyyss--
                tteemm,  include iinniitt as a source. This is because
                none of the other targets  can  be  made  until
                iinniitt has been made, thus they depend on it.











                               -33-


   .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  nnrrooffff) can be performed
                the same on any architecture (CPU and operating
                system  type),  while  others (e.g. compiling a
                program with cccc) must be performed on a machine
                with the same architecture. Not all export sys-
                tems will support this attribute.
                _N_E_W

   .EXPORT=_a_t_t_r_i_b_u_t_e
                Each such dependency specifies that the  target
                creation  commands  should  only be exported to
                machines having _a_t_t_r_i_b_u_t_e among  their  charac-
                teristics.   Available  attributes are entirely
                site-dependent, and their syntax depends on the
                underlying   exportation   system.    (see  the
                description for eexxppoorrtt((11))).   This  feature  is
                typically  used  to  specify  minimum  resource
                requirements for memory, disk  space,  software
                licenses,  etc.   If  the  target  marked  with
                ..EEXXPPOORRTT== attributes cannot be exported, and the
                local host does not match the attributes speci-
                fied, PMake will try to defer  the  exportation
                indefinitely   until  a  suitable  remote  host
                becomes  available  (or   until   interrupted).
                Whether  deferment  is possible also depends on
                the exportation system.

   .IGNORE      Giving a target the  ..IIGGNNOORREE  attribute  causes
                PMake 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

                     pprroogg11           :: $$((PPRROOGG11OOBBJJSS)) pprroogg22 MMAAKKEEIINNSSTTAALLLL
                     pprroogg22           :: $$((PPRROOGG22OOBBJJSS)) ..IINNVVIISSIIBBLLEE MMAAKKEEIINNSSTTAALLLL

                where MMAAKKEEIINNSSTTAALLLL is  some  complex  .USE  rule
                (see   below)   that  depends  on  the  ..AALLLLSSRRCC









                               -34-


                variable containing the right  things.  Without
                the ..IINNVVIISSIIBBLLEE attribute for pprroogg22, the MMAAKKEEIINN--
                SSTTAALLLL 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  ..TTAARRGGEETT  variable  and all the local vari-
                ables of any target  that  depends  on  it,  is
                replaced  by the value of its ..AALLLLSSRRCC 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
                rraannlliibb that I mentioned earlier, 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:

                     pprrooggrraamm         :: $$((OOBBJJSS)) lliibbrraarriieess
                             cccc --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))

                     lliibbrraarriieess       :: lliibb11..aa lliibb22..aa lliibb33..aa lliibb44..aa ..JJOOIINN
                             rraannlliibb $$((..OOOODDAATTEE))

                In this case, PMake will re-create the  $$((OOBBJJSS))
                as necessary, along with lliibb11..aa, lliibb22..aa, lliibb33..aa
                and lliibb44..aa.  It will then execute rraannlliibb on any
                library  that  was  changed  and  set pprrooggrraamm's
                ..AALLLLSSRRCC variable to contain what's  in  $$((OOBBJJSS))
                followed  by  ``lliibb11..aa  lliibb22..aa lliibb33..aa lliibb44..aa.''
                In case you're  wondering,  it's  called  ..JJOOIINN
                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
                created if the --tt flag was given.

   .MAKE        The ..MMAAKKEE attribute marks its target as being a
                recursive invocation  of  PMake.   This  forces
                PMake 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

                     ppmmaakkee --nn










                               -35-


                and have it descend the directory tree (if your
                makefiles  are set up correctly), printing what
                it would have executed if you  hadn't  included
                the --nn flag.
                _N_E_W _T_h_i_s _a_t_t_r_i_b_u_t_e _a_l_s_o _a_f_f_e_c_t_s _q_u_e_r_y _m_o_d_e _(--qq_)
                _p_r_o_c_e_s_s_i_n_g_.  _T_o _v_e_r_i_f_y _t_h_e _u_p_t_o_d_a_t_e_-_n_e_s_s  _o_f  _a
                _r_e_c_u_r_s_i_v_e  _m_a_k_e  _t_a_r_g_e_t_, _P_M_a_k_e _i_n_v_o_k_e_s _t_h_e _c_o_m_-
                _m_a_n_d _a_n_d _c_h_e_c_k_s _f_o_r _a _z_e_r_o _r_e_t_u_r_n _s_t_a_t_u_s_.
                CCoommppaattiibbiilliittyy:: _I_n _c_o_m_p_a_t_i_b_i_l_i_t_y _(--MM_) _m_o_d_e_,  _a_l_l
                _c_o_m_m_a_n_d_s  _t_h_a_t  _e_x_p_a_n_d  _t_h_e  MMAAKKEE  _v_a_r_i_a_b_l_e _a_r_e
                _a_u_t_o_m_a_t_i_c_a_l_l_y _c_o_n_s_i_d_e_r_e_d _r_e_c_u_r_s_i_v_e _m_a_k_e _i_n_v_o_c_a_-
                _t_i_o_n_s_.

   .NOEXPORT    If  possible,  PMake will attempt to export the
                creation of  all  targets  to  another  machine
                (this  depends  on  how  PMake was configured).
                Sometimes, the creation is  so  simple,  it  is
                pointless to send it to another machine. If you
                give the target  the  ..NNOOEEXXPPOORRTT  attribute,  it
                will be run locally, even if you've given PMake
                the --LL 00 flag.

   .NOTMAIN     Normally, if you do not  specify  a  target  to
                make  in  any  other  way,  PMake will take the
                first target 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  PMake  that the target is definitely _n_o_t
                the Main Target.  This allows you to place tar-
                gets  in  an  included  makefile and have PMake
                create something else by default.
                _N_E_W CCoommppaattiibbiilliittyy:: _T_a_r_g_e_t_s  _w_h_o_s_e  _n_a_m_e_s  _s_t_a_r_t
                _w_i_t_h _`_`_._'_' _a_r_e _a_l_s_o _n_o_t _c_o_n_s_i_d_e_r_e_d _m_a_i_n _t_a_r_g_e_t_s
                _(_u_n_l_e_s_s _t_h_e_y _l_o_o_k _l_i_k_e _a _p_a_t_h _n_a_m_e_, _i_._e_._,  _c_o_n_-
                _t_a_i_n _a _`_`_/_'_'_)_.

   .PRECIOUS    When  PMake  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 ..PPRREECCIIOOUUSS  attribute,  how-
                ever,  PMake will leave it alone. An additional
                side effect of the `::' operator is to mark the
                targets as ..PPRREECCIIOOUUSS.
                _N_E_W  FFiixxeedd::  _A_c_t_u_a_l_l_y_,  _o_n_l_y  _f_i_l_e_s  _t_h_a_t  _h_a_v_e
                _c_h_a_n_g_e_d _s_i_n_c_e _P_M_a_k_e _s_t_a_r_t_e_d _a_r_e _r_e_m_o_v_e_d_, _o_t_h_e_r_-
                _w_i_s_e _w_e _m_i_g_h_t _d_e_s_t_r_o_y _p_r_e_-_e_x_i_s_t_i_n_g _w_o_r_k_.  _A_l_s_o_,
                _d_i_r_e_c_t_o_r_i_e_s  _a_r_e  _s_p_a_r_e_d_,  _s_i_n_c_e  _u_n_l_i_n_k_i_n_g   _a
                _d_i_r_e_c_t_o_r_y  _c_a_n  _l_e_a_d  _t_o _i_n_c_o_n_s_i_s_t_e_n_t _f_i_l_e _s_y_s_-
                _t_e_m_s_.
                _N_E_W











                               -36-


   .RESTART     Tells PMake to restart commands that exit  with
                a  non-zero  status, instead of aborting.  With
                this feature, PMake can be used to manage long-
                running  computations  performed by restartable
                commands.  Between  restarts  the  job  may  be
                assigned  to  different  hosts for exportation.
                Note that the effect in the  case  of  multiple
                creation  commands depends on the compatibility
                mode in effect.  In PMake  (single-shell)  mode
                the  entire  command list is restarted from the
                top; in backwards (--BB)  mode  only  the  failed
                command  is  restarted;  in  Make (--MM) mode the
                attribute has no effect.
                If an exportation system is  available  it  may
                have its own criteria for restarting jobs inde-
                pendent of ..RREESSTTAARRTT, e.g., in the event that  a
                remote jobs is evicted from its importing host.

   .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 PMake's equivalent of a  macro.  When  the
                target  is used as a source for another target,
                the other target acquires the commands, sources
                and attributes (except ..UUSSEE) of the source.  If
                the target already has commands, the ..UUSSEE  tar-
                get's  commands  are  added to the end. If more
                than one .USE-marked source is given to a  tar-
                get, 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 ..AALLLLSSRRCC variable for
                the target) as its ``arguments,'' if you  will.
                For example, you probably noticed that the com-
                mands for creating lliibb11..aa  and  lliibb22..aa  in  the
                example  in  section 3.3 were exactly the same.
                You can use the ..UUSSEE attribute to eliminate the
                repetition, like so:

                     lliibb11..aa          :: $$((LLIIBB11OOBBJJSS)) MMAAKKEELLIIBB
                     lliibb22..aa          :: $$((LLIIBB22OOBBJJSS)) MMAAKKEELLIIBB

                     MMAAKKEELLIIBB         :: ..UUSSEE
                             rrmm --ff $$((..TTAARRGGEETT))
                             aarr ccrr $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))
                             ......
                             rraannlliibb $$((..TTAARRGGEETT))












                               -37-


                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
                (MMAAKKEELLIIBB) does not appear in any  of  the  tar-
                gets's  local  variables.  There is no limit to
                the number of times I  could  use  the  MMAAKKEELLIIBB
                rule.  If  there  were  more libraries, I could
                continue with ``lliibb33..aa :: $$((LLIIBB33OOBBJJSS))  MMAAKKEELLIIBB''
                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 PMake. 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 exe-
               cuted 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 PMake can't fig-
               ure  out  any  other  way  to  create. It's only
               ``sort of'' a .USE rule because only  the  shell
               script  attached to the ..DDEEFFAAUULLTT target is used.
               The ..IIMMPPSSRRCC variable of a target  that  inherits
               ..DDEEFFAAUULLTT's  commands  is set to the target's own
               name.

   .END        This serves a function  similar  to  ..BBEEGGIINN,  in
               that  commands  attached to it are executed once
               everything has been re-created (so  long  as  no
               errors occurred). It also serves the extra func-
               tion of being a place on which  PMake  can  hang
               commands you put off to the end. Thus the script
               for this target 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  PMake.  Some
               systems  will  use  these  sources  to configure
               themselves. You should ask your system  adminis-
               trator about this.
               _N_E_W  _S_p_e_c_i_a_l  _s_o_u_r_c_e  _k_e_y_w_o_r_d_s _i_n_c_l_u_d_e_: SSAAMMEE_, _t_o
               _m_a_k_e ..EEXXPPOORRTTSSAAMMEE _t_h_e _d_e_f_a_u_l_t_; _a_n_d  UUSSEELLOOCCAALL_,  _t_o
               _a_l_w_a_y_s  _c_h_e_c_k  _t_h_e  _l_o_c_a_l  _h_o_s_t _f_o_r _a_v_a_i_l_a_b_i_l_i_t_y









                               -38-


               _b_e_f_o_r_e _e_x_p_o_r_t_i_n_g _a _c_o_m_m_a_n_d _(_s_i_m_i_l_a_r _t_o  _a  _n_e_g_a_-
               _t_i_v_e --LL _o_p_t_i_o_n _v_a_l_u_e_)_.
               _O_n  _s_y_s_t_e_m_s  _t_h_a_t  _a_l_l_o_w _t_h_e _s_e_l_e_c_t_i_o_n _o_f _r_e_m_o_t_e
               _h_o_s_t_s _b_y _a_t_t_r_i_b_u_t_e_s_, _o_t_h_e_r _s_o_u_r_c_e_s  _f_o_r  ..EEXXPPOORRTT
               _a_r_e  _i_n_t_e_r_p_r_e_t_e_d  _a_s  _a_t_t_r_i_b_u_t_e_s  _t_h_a_t _s_h_o_u_l_d _b_e
               _u_s_e_d _g_l_o_b_a_l_l_y _(_s_e_e _t_h_e _d_e_s_c_r_i_p_t_i_o_n _f_o_r  ..EEXXPPOORRTT==
               _s_o_u_r_c_e_s _a_b_o_v_e_)_.
               _S_p_e_c_i_f_y_i_n_g  ..EEXXPPOORRTT  _w_i_t_h_o_u_t  _s_o_u_r_c_e_s _w_i_l_l _r_e_s_e_t
               _t_h_e _e_x_p_o_r_t_a_t_i_o_n  _s_y_s_t_e_m_s_'_s  _c_o_n_f_i_g_u_r_a_t_i_o_n  _t_o  _a
               _d_e_f_a_u_l_t  _s_t_a_t_e  _a_n_d  _d_i_s_c_a_r_d  _a_n_y  _g_l_o_b_a_l _e_x_p_o_r_t
               _a_t_t_r_i_b_u_t_e_s_.  _T_h_i_s _i_s _u_s_e_f_u_l _t_o _u_n_d_o  _t_h_e  _e_f_f_e_c_t
               _o_f _d_i_r_e_c_t_i_v_e_s _p_r_e_s_e_n_t _i_n _t_h_e _s_y_s_t_e_m _m_a_k_e_f_i_l_e_.

   .IGNORE     This  target  marks each of its sources with the
               ..IIGGNNOORREE attribute. If  you  don't  give  it  any
               sources, then it is like giving the --ii flag when
               you invoke PMake -- errors are ignored  for  all
               commands.
               _N_E_W  FFiixxeedd::  ..IIGGNNOORREE _a_n_d --ii _n_o_w _a_l_s_o _i_g_n_o_r_e _c_o_m_-
               _m_a_n_d_s _t_h_a_t _f_a_i_l _a_s _a _r_e_s_u_l_t _o_f _b_e_i_n_g _k_i_l_l_e_d _b_y _a
               _s_i_g_n_a_l_.   _T_h_i_s  _u_s_e_d _t_o _b_e _t_r_u_e _o_n_l_y _i_n _c_o_m_p_a_t_i_-
               _b_i_l_i_t_y _m_o_d_e_.

   .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  ..SSUUFFFFIIXXEESS  (see
               below).  Any suffix  so  marked  will  have  the
               directories  on  its  search  path  (see  ..PPAATTHH,
               below) placed in the  ..IINNCCLLUUDDEESS  variable,  each
               preceded by a --II flag. This variable can then be
               used as an argument for the compiler in the nor-
               mal  fashion. The ..hh suffix is already marked in
               this way in the system makefile.   E.g.  if  you
               have

                    ..SSUUFFFFIIXXEESS       :: ..bbiittmmaapp
                    ..PPAATTHH..bbiittmmaapp    :: //uussrr//llooccaall//XX//lliibb//bbiittmmaappss
                    ..IINNCCLLUUDDEESS       :: ..bbiittmmaapp

               PMake  will place ``--II//uussrr//llooccaall//XX//lliibb//bbiittmmaappss''
               in the ..IINNCCLLUUDDEESS variable and you can then say

                    cccc $$((..IINNCCLLUUDDEESS)) --cc xxpprrooggrraamm..cc

               (Note: the ..IINNCCLLUUDDEESS variable  is  not  actually
               filled  in  until  the  entire makefile has been
               read.)

   .INTERRUPT  When PMake is interrupted, it will  execute  the
               commands  in  the  script for this target, if it
               exists.











                               -39-


   .LIBS       This does for libraries what ..IINNCCLLUUDDEESS 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 ..LLIIBBSS.  Be forewarned that PMake may not have
               been  compiled  to do this if the linker on your
               system doesn't accept the --LL  flag,  though  the
               ..LLIIBBSS  variable  will always be defined once the
               makefile has been read.

   .MAIN       If you didn't give a target (or targets) to cre-
               ate  when  you  invoked  PMake, it will take the
               sources of this target as the targets to create.

   .MAKEFLAGS  This  target  provides  a  way for you to always
               specify flags for PMake  when  the  makefile  is
               used.  The flags are just as they would be typed
               to the shell (except you can't use  shell  vari-
               ables unless they're in the environment), though
               the --ff and --rr flags have no effect.

   .NULL       This allows you to  specify  what  suffix  PMake
               should pretend a file has if, in fact, it has no
               known suffix. Only one suffix may be  so  desig-
               nated. The last source on the dependency line is
               the suffix that is used  (you  should,  however,
               only give one suffix...).
               _N_E_W  _I_f  _n_o  _s_o_u_r_c_e_s _a_r_e _g_i_v_e_n_, _P_M_a_k_e _r_e_v_e_r_t_s _t_o
               _t_h_e _d_e_f_a_u_l_t _w_h_i_c_h _i_s _t_o _h_a_v_e  _n_o  _i_m_p_l_i_c_i_t  _n_u_l_l
               _s_u_f_f_i_x  _a_t _a_l_l_.  _T_h_i_s _i_s _u_s_e_f_u_l _t_o _u_n_d_o _w_h_a_t_e_v_e_r
               _i_s _d_e_f_i_n_e_d _i_n _t_h_e _s_y_s_t_e_m _m_a_k_e_f_i_l_e_.

   .ORDER      This target constrains  PMake  to  run  creation
               commands  for  certain targets sequentially in a
               specified order.  The  sources  listed  will  be
               created  in the order they are given.  Note that
               this affects only the targets listed, not  their
               child  dependencies, which may be created in any
               order unless mentioned in ..OORRDDEERR  targets  them-
               selves.  PMake will silently fail to create tar-
               gets that cannot be created  under  any  allowed
               ordering,  such  as  when  a  cyclic ordering is
               specified.
               _N_E_W _T_h_i_s _s_p_e_c_i_a_l _t_a_r_g_e_t _w_a_s  _p_r_e_v_i_o_u_s_l_y  _p_r_e_s_e_n_t
               _b_u_t _u_n_d_o_c_u_m_e_n_t_e_d_.

   .PATH       If  you give sources for this target, PMake 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
               directories  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 explanation.









                               -40-


   .PATH_s_u_f_f_i_x This does a similar thing to ..PPAATTHH, 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 ..IIGGNNOORREE,  this  gives  the  ..PPRREECCIIOOUUSS
               attribute to each source on the dependency line,
               unless there are no sources, in which  case  the
               ..PPRREECCIIOOUUSS  attribute is given to every target in
               the file.

   .RECURSIVE  This target applies the ..MMAAKKEE attribute  to  all
               its  sources.  It does nothing if you don't give
               it any sources.

   .SHELL      PMake is  not  constrained  to  only  using  the
               Bourne  shell to execute the commands you put in
               the makefile. 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 informa-
               tion.
               _N_E_W  ..SSHHEELLLL  _w_i_t_h _n_o _d_e_p_e_n_d_e_n_c_i_e_s _r_e_v_e_r_t_s _t_o _t_h_e
               _c_o_n_f_i_g_u_r_e_d _d_e_f_a_u_l_t _(_u_s_u_a_l_l_y _t_h_e _B_o_u_r_n_e_) _s_h_e_l_l_.

   .SILENT     When you use ..SSIILLEENNTT as a target, it applies the
               ..SSIILLEENNTT  attribute  to  each  of its sources. If
               there are no sources  on  the  dependency  line,
               then  it is as if you gave PMake the --ss flag and
               no commands will be echoed.

   .SUFFIXES   This is used to give new file suffixes for PMake
               to  handle. Each source is a suffix PMake should
               recognize. If you give  a  ..SSUUFFFFIIXXEESS  dependency
               line  with  no  sources, PMake will forget about
               all the suffixes 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.  PMake
   defines several modifiers that may be  applied  to  a  vari-
   able'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:

        $${{_V_A_R_I_A_B_L_E::_m_o_d_i_f_i_e_r}}










                               -41-


   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; [[aabbccdd]] matches any single
             character  that  is  either  `a',  `b', `c' or `d'
             (there may be any number of characters between the
             brackets); [[00--99]] 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 <<mmaakkeeddeeppeenndd..mmkk>> uses
             ``$$((CCFFLLAAGGSS::MM--[[IIDD]]**))'' to extract all the --II and --DD
             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 ::MM 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 gg flag is given at the  end,  in  which
             case  all  occurences  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 preceeds 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-









                               -42-


             expression/wildcard characters  have  any  special
             meaning  save ^^ and $$.  In the replacement string,
             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  preceeding it with a back-
             slash.

        T    Replaces each word in the  variable  expansion  by
             its  last  component  (its ``tail''). For example,
             given

                  OOBBJJSS == ....//lliibb//aa..oo bb //uussrr//lliibb//lliibbmm..aa
                  TTAAIILLSS == $$((OOBBJJSS::TT))

             the  variable  TTAAIILLSS  would  expand  to  ``aa..oo   bb
             lliibbmm..aa.''

        H    This  is  similar to ::TT, except that every word is
             replaced  by  everything   but   the   tail   (the
             ``head'').  Using the same definition of OOBBJJSS, the
             string  ``$$((OOBBJJSS::HH))''  would  expand  to  ``....//lliibb
             //uussrr//lliibb.''   Note  that  the  final  slash on the
             heads is removed and anything without  a  head  is
             replaced by the empty string.

        E    ::EE  replaces  each  word  by  its suffix (``exten-
             sion''). So  ``$$((OOBBJJSS::EE))''  would  give  you  ``..oo
             ..aa.''

        R    This replaces each word by everything but the suf-
             fix (the ``root''  of  the  word).   ``$$((OOBBJJSS::RR))''
             expands to `` ....//lliibb//aa bb //uussrr//lliibb//lliibbmm.''

   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
        ..aassmm).  Each program can be  assembled  into  two  ver-
        sions,  one  with  error-checking code assembled in and









                               -43-


        one without. You could assemble them  into  files  with
        different  suffixes (..eeoobbjj and ..oobbjj, for instance), but
        your linker only understands files that  end  in  ..oobbjj.
        To  top it all off, the final executables _m_u_s_t have the
        suffix ..eexxee.  How  can  you  still  use  transformation
        rules to make your life easier (Hint: assume the error-
        checking versions have eecc tacked onto their prefix)?

   (3.2)
        Assume, for a moment or two, you want to perform a sort
        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,
        PMake doesn't allow constructs like

             $$(($$((FFOOOO))))

        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.
        _N_E_W  CCoommppaattiibbiilliittyy:: _A_s _a _m_a_t_t_e_r _o_f _f_a_c_t_, _P_M_a_k_e _n_o_w _d_o_e_s
        _a_l_l_o_w _`_c_o_m_p_u_t_e_d_' _v_a_r_i_a_b_l_e _n_a_m_e_s _b_o_t_h _i_n _e_x_p_a_n_s_i_o_n_s  _a_n_d
        _a_s_s_i_g_n_m_e_n_t_s_, _j_u_s_t _a_s _o_t_h_e_r _m_o_d_e_r_n _M_a_k_e_s_.

   44..  PPMMaakkee ffoorr GGooddss

   This  chapter  is  devoted to those facilities in PMake 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

   PMake supports the dispersal of files into multiple directo-
   ries by allowing you to specify places to look  for  sources
   with ..PPAATTHH targets in the makefile. The directories you give
   as sources for these targets make up a ``search path.''
   _O_L_D Only those files used exclusively as sources  are  actu-
   ally sought on a search path, the assumption being that any-
   thing listed as a target in the makefile can be  created  by
   the makefile and thus should be in the current directory.
   _N_E_W  CCoommppaattiibbiilliittyy::  _T_h_i_s  _i_s  _n_o _l_o_n_g_e_r _t_r_u_e_.  _B_o_t_h _t_o _m_a_k_e
   _P_M_a_k_e _m_o_r_e _c_o_m_p_a_t_i_b_l_e _a_n_d _l_i_f_e _e_a_s_i_e_r  _w_i_t_h  _t_h_e  _M_A_K_E_O_B_J_D_I_R
   _f_e_a_t_u_r_e  _d_e_s_c_r_i_b_e_d  _b_e_l_o_w_,  _P_M_a_k_e _w_i_l_l _t_r_y _t_o _l_o_c_a_t_e _t_a_r_g_e_t_s
   _u_s_i_n_g _s_e_a_r_c_h _p_a_t_h_s _a_s _w_e_l_l_.

   There are two types of search paths in PMake:  one  is  used
   for all types of files (including included makefiles) and is









                               -44-


   specified with a plain ..PPAATTHH target (e.g.  ``..PPAATTHH :: RRCCSS''),
   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 ..PPAATTHH with the suffix
   of the file. For instance

        ..PPAATTHH..hh         :: //sspprriittee//lliibb//iinncclluuddee //sspprriittee//aatttt//lliibb//iinncclluuddee

   would   tell   PMake   to   look    in    the    directories
   //sspprriittee//lliibb//iinncclluuddee   and  //sspprriittee//aatttt//lliibb//iinncclluuddee  for  any
   files whose suffix is ..hh.

   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
   target's name (..AALLLLSSRRCC, and ..IIMMPPSSRRCC)  will  instead  contain
   the path to the file, as found by PMake.  Thus if you have a
   file ....//lliibb//mmuummbbllee..cc and a makefile

        ..PPAATTHH..cc         :: ....//lliibb
        mmuummbbllee          :: mmuummbbllee..cc
                $$((CCCC)) --oo $$((..TTAARRGGEETT)) $$((..AALLLLSSRRCC))

   the command executed to create mmuummbbllee would be ``cccc --oo  mmuumm--
   bbllee  ....//lliibb//mmuummbbllee..cc.''   (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 ..oouutt
   is the null suffix by default and  a  transformation  exists
   from ..cc to ..oouutt.  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
   PMake  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.
   _N_E_W

   To be compatible with other Makes, PMake also interprets the
   value of the VPATH variable as  a  list  of  colon-separated
   directories  to  be  added to the general search path.  This
   feature was previously present but undocumented.

   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









                               -45-


   any  changes  to the directories while PMake is running will
   not be noted when searching for implicit sources,  nor  will
   they  be found when PMake attempts to discover when the file
   was last modified, unless the file was created in  the  cur-
   rent  directory.  While  people  have  suggested  that PMake
   should read the directories each time,  my  experience  sug-
   gests  that the caching seldom causes problems. In addition,
   not caching the directories  slows  things  down  enormously
   because  of  PMake's  attempts to apply transformation rules
   through non-existent files -- the number of extra  file-sys-
   tem  searches  is truly staggering, especially if many files
   without suffixes are used and the null suffix isn't  changed
   from ..oouutt.
   _N_E_W

   PMake  was  retrofitted  with a useful feature from 4.4BSD's
   make (which is a descendant of PMake).  This feature  allows
   platform-specific  files, in particular, object files, to be
   created in separate directories.  On startup, if  the  envi-
   ronment  variable  MMAAKKEEOOBBJJDDIIRR  is set and refers to a direc-
   tory, or if the subdirectory ``obj'' exists, the PMake  pro-
   cess  changes  its working directory to that directory.  The
   PMake variable ..CCUURRDDIIRR is set to the original working direc-
   tory (``.'' if no MAKEOBJDIR was found).

   Unless  specified  otherwise,  all commands will thus create
   their targets in the object directory.  Since the  value  of
   ..CCUURRDDIIRR  is also added to the default file search path, most
   target creation commands written in terms of variables ..AALLLL--
   SSRRCC,   ..IIMMPPSSRRCC,   ..OOOODDAATTEE,  etc.,  will  continue  to  work.
   Explicit path references may have to be modified to be rela-
   tive  to  $$((..CCUURRDDIIRR)).   For  example,  the first line in the
   example above would have to read thusly:

        ..PPAATTHH..cc         :: $$((..CCUURRDDIIRR))//....//lliibb


   44..22..  AArrcchhiivveess aanndd LLiibbrraarriieess

   UNIX and Sprite allow you to merge  files  into  an  archive
   using  the aarr command. Further, if the files are relocatable
   object files, you can run rraannlliibb  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 --llmm
   rather than //uussrr//lliibb//lliibbmm..aa and the  linker  thinks  they're
   out-of-date if you so much as look at them.

   PMake  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,  PMake









                               -46-


   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.
     _N_E_W  _A_c_t_u_a_l_l_y_, _t_h_i_s _i_s _t_r_u_e _o_n_l_y _i_f _t_h_e _l_i_b_r_a_r_y _d_e_p_e_n_d_s _o_n
     _o_n_e _o_f _i_t_s _m_e_m_b_e_r_s_.  _O_t_h_e_r_w_i_s_e _w_e _c_o_u_l_d _n_e_v_e_r _c_r_e_a_t_e  _n_o_n_-
     _o_b_j_e_c_t _f_i_l_e _a_r_c_h_i_v_e_s _t_h_a_t _a_r_e _u_p_-_t_o_-_d_a_t_e_.

   A  library  is any target that looks like ``--llname'' or that
   ends in a suffix that was marked  as  a  library  using  the
   ..LLIIBBSS target.  ..aa 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 ``'lliibbddiixx..aa((wwiinnddooww..oo))''  specifies  the
   file  wwiinnddooww..oo  in  the  archive lliibbddiixx..aa.  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.
   _N_E_W

   CCoommppaattiibbiilliittyy:: Additionally, archive member may  be  identi-
   fied  by  a symbol they define.  The symbols are enclosed in
   parentheses and take the place  of  a  regular  member  file
   name,  i.e., ``_a_r_c_h_i_v_e(... (_s_y_m_b_o_l) ...)''.  The correspond-
   ing actual file names found in the library are  expanded  in
   any local variables, of course.  The member in question must
   already be part of the library, or an  error  will  be  sig-
   naled.   Consider  this clever way to extract an object file
   from a library.

        LLIIBBCC == //lliibb//lliibbcc..aa

        mmyyggeettoopptt..oo      :: $$((LLIIBBCC))((((ggeettoopptt))))
                aarr xx $$((LLIIBBCC)) $$((..OOOODDAATTEE))
                mmvv $$((..OOOODDAATTEE)) $$((..TTAARRGGEETT))

   Member lookup by symbol may not be supported on some  system
   with weird symbol table formats.

   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.










                               -47-


   +o The _a_r_c_h_i_v_e(_m_e_m_b_e_r)'s ..TTAARRGGEETT 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 ..AARRCCHHIIVVEE 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  ..MMEEMMBBEERR  variable  is set to the actual string inside
     the parentheses. In most cases, this will be the  same  as
     the ..TTAARRGGEETT 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
     ..TTAARRGGEETT variable.

   Thus,  a program library could be created with the following
   makefile:

        ..oo..aa            ::
                ......
                rrmm --ff $$((..TTAARRGGEETT::TT))
        OOBBJJSS            == oobbjj11..oo oobbjj22..oo oobbjj33..oo
        lliibbpprroogg..aa       :: lliibbpprroogg..aa(($$((OOBBJJSS))))
                aarr ccrruu $$((..TTAARRGGEETT)) $$((..OOOODDAATTEE))
                rraannlliibb $$((..TTAARRGGEETT))

   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 lliibbpprroogg..aa, a table
   of contents placed in the  archive  and  the  newly-archived
   object files to be removed.

   All this is used in the mmaakkeelliibb..mmkk system makefile to create
   a single library with ease. This makefile looks like this:




























                               -48-


        ##
        ## RRuulleess ffoorr mmaakkiinngg lliibbrraarriieess.. TThhee oobbjjeecctt ffiilleess tthhaatt mmaakkee uupp tthhee lliibbrraarryy aarree
        ## rreemmoovveedd oonnccee tthheeyy aarree aarrcchhiivveedd..
        ##
        ## TToo mmaakkee sseevveerraall lliibbaarraarriieess iinn ppaarraalllleell,, yyoouu sshhoouulldd ddeeffiinnee tthhee vvaarriiaabbllee
        ## ""mmaannyy__lliibbrraarriieess"".. TThhiiss wwiillll sseerriiaalliizzee tthhee iinnvvooccaattiioonnss ooff rraannlliibb..
        ##
        ## TToo uussee,, ddoo ssoommeetthhiinngg lliikkee tthhiiss::
        ##
        ## OOBBJJEECCTTSS == <<ffiilleess iinn tthhee lliibbrraarryy>>
        ##
        ## ffiisshh..aa:: ffiisshh..aa(($$((OOBBJJEECCTTSS)))) MMAAKKEELLIIBB
        ##
        ##

        ##iiffnnddeeff __MMAAKKEELLIIBB__MMKK
        __MMAAKKEELLIIBB__MMKK    ==

        ##iinncclluuddee  <<ppoo..mmkk>>

        ..ppoo..aa ..oo..aa     ::
             ......
             rrmm --ff $$((..MMEEMMBBEERR))

        AARRFFLLAAGGSS        ??== ccrrll

        ##
        ## RRee--aarrcchhiivvee tthhee oouutt--ooff--ddaattee mmeemmbbeerrss aanndd rreeccrreeaattee tthhee lliibbrraarryy''ss ttaabbllee ooff
        ## ccoonntteennttss uussiinngg rraannlliibb.. IIff mmaannyy__lliibbrraarriieess iiss ddeeffiinneedd,, ppuutt tthhee rraannlliibb ooffff
        ## ttiill tthhee eenndd ssoo mmaannyy lliibbrraarriieess ccaann bbee mmaaddee aatt oonnccee..
        ##
        MMAAKKEELLIIBB        :: ..UUSSEE ..PPRREECCIIOOUUSS
             aarr $$((AARRFFLLAAGGSS)) $$((..TTAARRGGEETT)) $$((..OOOODDAATTEE))
        ##iiffnnddeeff nnoo__rraannlliibb
        ## iiffddeeff mmaannyy__lliibbrraarriieess
             ......
        ## eennddiiff mmaannyy__lliibbrraarriieess
             rraannlliibb $$((..TTAARRGGEETT))
        ##eennddiiff nnoo__rraannlliibb

        ##eennddiiff __MMAAKKEELLIIBB__MMKK

   _N_E_W

   _P_M_a_k_e _k_n_o_w_s _h_o_w _t_o _r_e_a_d _a _f_a_i_r _n_u_m_b_e_r _o_f  _d_i_f_f_e_r_e_n_t  _a_r_c_h_i_v_e
   _f_o_r_m_a_t_s  _(_i_n_c_l_u_d_i_n_g  _t_h_e  _r_a_n_l_i_b  _f_o_r_m_a_t_s _u_s_e_d _b_y _4_._4_B_S_D _a_n_d
   _M_I_P_S_, _a_s _w_e_l_l _a_s _S_y_s_t_e_m _V _c_o_m_m_o_n _a_r_c_h_i_v_e_s_)_.  _H_o_w_e_v_e_r_, _d_u_e _t_o
   _d_i_f_f_e_r_e_n_t  _w_o_r_d  _s_i_z_e_s  _a_n_d  _b_y_t_e _o_r_d_e_r_i_n_g_s _i_t _i_s _n_o_t _a_l_w_a_y_s
   _p_o_s_s_i_b_l_e _t_o _i_n_t_e_r_p_r_e_t _o_b_j_e_c_t _f_i_l_e  _a_r_c_h_i_v_e_s  _c_r_e_a_t_e_d  _f_o_r  _a
   _t_a_r_g_e_t _p_l_a_t_f_o_r_m _d_i_f_f_e_r_e_n_t _f_r_o_m _t_h_e _h_o_s_t _s_y_s_t_e_m_.

   44..33..  OOnn tthhee CCoonnddiittiioonn......











                               -49-


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

        ##iiff _b_o_o_l_e_a_n _e_x_p_r_e_s_s_i_o_n
        _l_i_n_e_s
        ##eelliiff _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
        ##eellssee
        _s_t_i_l_l _m_o_r_e _l_i_n_e_s
        ##eennddiiff

   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 mmaakkee((_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 ..MMAAIINN target (note that the sources for ..MMAAIINN
            are only used if no targets were given on the  com-
            mand line).

   defined  The  syntax  is  ddeeffiinneedd((_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 PMake is being run.

   exists   The syntax is eexxiissttss((_f_i_l_e)) and is true if the  file
            can  be found on the global search path (i.e.  that
            defined by ..PPAATTHH targets, not by  ..PPAATTHH_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









                               -50-


            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
            ``!!ddeeffiinneedd((_v_a_r)) |||| eemmppttyy((_v_a_r))'' as  the  definition
            of |||| will prevent the eemmppttyy(()) 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:

                 ##iiff !!eemmppttyy((_v_a_r::MM_w_o_r_d))


   target   The syntax is ttaarrggeett((_n_a_m_e)) and is true if _n_a_m_e is a
            target mentioned in the makefile(s) read up to that
            point, i.e., appears somewhere to  the  left  of  a
            dependency  operator.   This  is  useful  following
            makefile inclusions or  wildcard  expansions  which
            may or may not result in the definition of targets.
            _N_E_W _T_h_i_s _f_u_n_c_t_i_o_n _w_a_s _p_r_e_v_i_o_u_s_l_y _p_r_e_s_e_n_t _b_u_t _u_n_d_o_c_-
            _u_m_e_n_t_e_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.

        ##iiff $$((OOSS)) ==== 44..33

        ##iiff $$((MMAACCHHIINNEE)) ==== ""ssuunn33""

        ##iiff $$((LLOOAADD__AADDDDRR)) << 00xxcc000000

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

        ##iiff $$((LLOOAADD))

   would see if LLOOAADD contains a non-zero value and

        ##iiff !!$$((LLOOAADD))

   would test if LLOOAADD contains a zero value.

   In  addition to the bare ``##iiff,'' there are other forms that
   apply one of the first two functions to each term. They  are
   as follows:

             iiffddeeff     defined
             iiffnnddeeff    !defined
             iiffmmaakkee    make
             iiffnnmmaakkee   !make










                               -51-


   There  are also the ``else if'' forms: eelliiff, eelliiffddeeff, eelliiffnn--
   ddeeff, eelliiffmmaakkee, and eelliiffnnmmaakkee.

   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 --gg flag for the compilation, while the  other
   uses  the  --OO  flag,  or you can use another target (call it
   ddeebbuugg) to create the debug version. The construct below will
   take  care  of this for you. I have also made it so defining
   the variable DDEEBBUUGG (say with ppmmaakkee --DD DDEEBBUUGG) will also cause
   the debug version to be made.

        ##iiff ddeeffiinneedd((DDEEBBUUGG)) |||| mmaakkee((ddeebbuugg))
        CCFFLLAAGGSS         ++== --gg
        ##eellssee
        CCFFLLAAGGSS         ++== --OO
        ##eennddiiff

   There  are, of course, problems with this approach. The most
   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

        ##iiff mmaakkee((pprriinntt))
        FFOORRMMAATTTTEERR == ddiittrrooffff --PPllaasseerr__pprriinntteerr
        ##eennddiiff
        ##iiff mmaakkee((ddrraafftt))
        FFOORRMMAATTTTEERR == nnrrooffff --PPddoott__mmaattrriixx__pprriinntteerr
        ##eennddiiff

   would wreak havok if you tried ``ppmmaakkee ddrraafftt  pprriinntt''  since
   you would use the same formatter for each target. As I said,
   this all gets somewhat complicated.
   _N_E_W

   _E_v_e_n _m_o_r_e _s_o _a_s _s_o_m_e _o_f _t_h_e _o_r_g_i_n_a_l _v_a_r_i_a_n_t_s  _o_f  ##iiff  _w_h_e_r_e
   _h_i_g_h_l_y  _u_n_i_n_t_u_i_t_i_v_e_,  _a_s _v_a_r_i_a_b_l_e _e_x_p_a_n_s_i_o_n_s _i_n _c_o_n_d_i_t_i_o_n_a_l_s
   _w_e_r_e _a_l_w_a_y_s _i_n_t_e_r_p_r_e_t_e_d _a_s _n_u_m_e_r_i_c _c_o_m_p_a_r_i_s_o_n_s_, _e_v_e_n _i_n  _t_h_e
   _a_b_s_e_n_c_e _o_f _a_n _o_p_e_r_a_t_o_r_.  _T_h_e _f_o_l_l_o_w_i_n_g _n_o_w _w_o_r_k _a_s _e_x_p_e_c_t_e_d_:

        ##iiffmmaakkee $$((VVAARR))
        ##iiffddeeff $$((VVAARR))

   expand the variable and apply the mmaakkee(()) or  ddeeffiinneedd(())  test
   to the result, respectively.

        ##iiff aarrgg










                               -52-


   checks  non-zero-ness  if aarrgg is a number (either literal or
   the result of a variable expansion), and  otherwise  assumes
   aarrgg to be a variable name and checks for its existence.
   _N_E_W

   The fact that PMake conditionals appear as comments to other
   Makes can be very convenient.  It allows one to write  make-
   files that work for both, while doing something special when
   run with PMake.  A standard idiom for this purpose is

        ##iiffddeeff ..PPMMAAKKEE
        FFOOOO == ssoommeetthhiinngg__vveerryy__ssppeecciiaall
        ##eellssee
        FFOOOO == ssttaannddaarrdd__vvaalluuee
        ##eennddiiff

   PMake will always choose the  first  branch  of  the  condi-
   tional,  whereas others will effectively only use the second
   assignment as it always overrides the first.

   44..44..  AA SShheellll iiss aa SShheellll iiss aa SShheellll

   In normal operation,  the  Bourne  Shell  (better  known  as
   ``sshh'')  is  used  to execute the commands to re-create tar-
   gets. PMake also allows you to specify a different shell for
   it  to  use when executing these commands. There are several
   things PMake must know about the  shell  you  wish  to  use.
   These  things  are  specified  as the sources for the ..SSHHEELLLL
   target by keyword, as follows:

   ppaatthh==_p_a_t_h
        PMake needs to know where the shell  actually  resides,
        so  it  can execute it. If you specify this and nothing
        else, PMake 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, PMake 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  PMake
        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 (``..SSHHEELLLL:: nnaammee==ccsshh''
        will do it).

   qquuiieett==_e_c_h_o_-_o_f_f _c_o_m_m_a_n_d
        As mentioned before, PMake  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









                               -53-


        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 PMake should give to turn echoing  back  on
        again.

   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  PMake  in  what  format  the
        shell  actually  prints  the echo-off command. Wherever
        PMake 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 ..SSIILLEENNTT, PMake 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 ..IIGGNNOORREE, PMake
        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 PMake 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 PMake if the shell can
        do  error-control,  but you'd be wrong. If hhaassEErrrrCCttll is
        yyeess, PMake uses the check  and  ignore  commands  in  a
        straight-forward manner.  If this is nnoo, however, their









                               -54-


        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) (``\\nn'') 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)
   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 <<sshhxx..mmkk>>  system  make-
   file, and causes PMake 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:

        ##
        ## TThhiiss iiss aa sshheellll ssppeecciiffiiccaattiioonn ttoo hhaavvee tthhee bboouurrnnee sshheellll eecchhoo
        ## tthhee ccoommmmaannddss jjuusstt bbeeffoorree eexxeeccuuttiinngg tthheemm,, rraatthheerr tthhaann wwhheenn iitt rreeaaddss
        ## tthheemm.. UUsseeffuull iiff yyoouu wwaanntt ttoo sseeee hhooww vvaarriiaabblleess aarree bbeeiinngg eexxppaannddeedd,, eettcc..
        ##
        ..SSHHEELLLL    :: ppaatthh==//bbiinn//sshh \\
             qquuiieett==""sseett --"" \\
             eecchhoo==""sseett --xx"" \\
             ffiilltteerr==""++ sseett -- "" \\
             eecchhooFFllaagg==xx \\
             eerrrrFFllaagg==ee \\
             hhaassEErrrrCCttll==yyeess \\
             cchheecckk==""sseett --ee"" \\
             iiggnnoorree==""sseett ++ee""


   It tells PMake the following:

   +o The shell is located in the file  //bbiinn//sshh.   It  need  not
     tell  PMake  that the name of the shell is sshh as PMake can
     figure that out for itself (it's the last component of the
     path).

   +o The command to stop echoing is sseett --.

   +o The command to start echoing is sseett --xx.











                               -55-


   +o When  the  echo  off  command  is executed, the shell will
     print ++ sseett -- (The  `+'  comes  from  using  the  --xx  flag
     (rather  than the --vv flag PMake usually uses)). PMake will
     remove all occurences 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 --xx 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 --ee.

   +o The shell can turn error-checking on and off, and the com-
     mands to do so are sseett ++ee and sseett --ee, respectively.

   I  should  note that this specification is for Bourne Shells
   that are not part of Berkeley UNIX, as shells from  Berkeley
   don't  do  error control. You can get a similar effect, how-
   ever, by changing the last three lines to be:

             hhaassEErrrrCCttll==nnoo \\
             cchheecckk==""eecchhoo \\""++ %%ss\\""\\nn"" \\
             iiggnnoorree==""sshh --cc ''%%ss |||| eexxiitt 00\\nn""


   This will cause PMake to execute the two commands

        eecchhoo ""++ _c_m_d""
        sshh --cc ''_c_m_d |||| ttrruuee''

   for each command for which errors are  to  be  ignored.  (In
   case you are wondering, the thing for iiggnnoorree tells the shell
   to execute another  shell  without  error  checking  on  and
   always exit 0, since the |||| causes the eexxiitt 00 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).
   _N_E_W

   CCoommppaattiibbiilliittyy:: _P_M_a_k_e _n_o_w _a_l_s_o _r_e_c_o_g_n_i_z_e_s _t_h_e SSHHEELLLL  _v_a_r_i_a_b_l_e
   _f_o_u_n_d  _i_n  _m_o_s_t _o_t_h_e_r _M_a_k_e_s_.  _A_s _a_n _e_x_c_e_p_t_i_o_n _t_o _t_h_e _g_e_n_e_r_a_l
   _r_u_l_e_, _t_h_i_s _v_a_r_i_a_b_l_e _i_s _n_o_t _r_e_l_a_t_e_d _t_o _t_h_e _e_n_v_i_r_o_n_m_e_n_t  _v_a_r_i_-
   _a_b_l_e  _h_o_l_d_i_n_g  _t_h_e  _u_s_e_r_'_s _i_n_t_e_r_a_c_t_i_v_e _s_h_e_l_l_.  _I_n_s_t_e_a_d _i_t _i_s
   _s_e_t _f_r_o_m_, _a_n_d _m_a_y _b_e _u_s_e_d _t_o _s_e_t_, _t_h_e _c_u_r_r_e_n_t ..SSHHEELLLL  _s_p_e_c_i_-
   _f_i_c_a_t_i_o_n_.  _T_h_u_s_,

        SSHHEELLLL == //bbiinn//ccsshh

   is equivalent to











                               -56-


        ..SSHHEELLLL:: ppaatthh==//bbiinn//ccsshh

   _A_l_s_o  ##uunnddeeff  SSHHEELLLL  _r_e_s_e_t_s  _t_h_e  _s_h_e_l_l  _t_o  _t_h_e  _c_o_n_f_i_g_u_r_e_d
   _d_e_f_a_u_l_t_.

   44..55..  CCoommppaattiibbiilliittyy

   There are three (well, 3 1/2) levels  of  backwards-compati-
   bility  built  into PMake.  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, PMake 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 (``$${{PPRRIINNTTEERR}}''),  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.
   _O_L_D

   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 ..MMAAKKEEFFLLAAGGSS target, if you want).
   _N_E_W

   _T_h_i_s  _i_s _t_h_e _d_e_f_a_u_l_t _c_o_n_f_i_g_u_r_a_t_i_o_n _n_o_w_.  _U_s_e --CC _t_o _c_h_a_n_g_e _i_t
   _b_a_c_k_.

   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 PMake 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:













                               -57-


        iinnssttaallll :::: ..MMAAKKEE
             ((ccdd ssrrcc;; $$((..PPMMAAKKEE)) iinnssttaallll))
             ((ccdd lliibb;; $$((..PPMMAAKKEE)) iinnssttaallll))
             ((ccdd mmaann;; $$((..PPMMAAKKEE)) iinnssttaallll))

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

   44..55..33..   DDEEFFCCOONN  11  ----  IImmiittaattiioonn iiss 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 PMake. If you are the conservative sort,  this  is  the
   compatibility  mode  for you. It is entered either by giving
   PMake the --MM flag (for  Make),  or  by  executing  PMake  as
   ``mmaakkee.''   In  either  case,  PMake performs things exactly
   like Make (while still supporting most of the nice new  fea-
   tures PMake provides). 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, PMake
     will break the command into words itself and  execute  the
     command  directly,  without  executing  a shell first. The
     characters that cause PMake 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 PMake deals with all by its lonesome).

   +o The use of the null suffix is turned off.

   44..66..  TThhee WWaayy TThhiinnggss WWoorrkk

   When PMake reads the makefile, it parses sources and targets
   into  nodes  in  a  graph. The graph is directed only in the
   sense that PMake 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,









                               -58-


   respectively),  but  also a count of the number of its chil-
   dren that have already been processed.

   The most important thing to know about how PMake  uses  this
   graph  is  that the traversal is breadth-first and occurs in
   two passes.

   After PMake has parsed the  makefile,  it  begins  with  the
   nodes  the  user  has told it to make (either on the command
   line, or via a ..MMAAIINN target, or  by  the  target  being  the
   first  in  the file not labeled with the ..NNOOTTMMAAIINN 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 SSuuffff__FFiinnddDDeeppss (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 ..UUSSEE 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
   decremented  (since  the  ..UUSSEE node has been processed). You
   will note that this allows a ..UUSSEE node to have children that
   are  ..UUSSEE  nodes  and the rules will be applied in sequence.
   If the node has no children, it is  placed  at  the  end  of
   another  queue  to be examined in the second pass. This pro-
   cess continues until the first queue is empty.

   At this point, all the leaves of the graph are in the exami-
   nation  queue.  PMake  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
   asynchronously. When the commands have  completed,  all  the
   node's  parents  have  their  unmade children counter decre-
   mented and, if the counter is then 0, they are placed on the
   examination queue. Likewise, if the node is up-to-date. Only
   those parents that were marked on the downward pass are pro-
   cessed  in  this way. Thus PMake 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 cre-
   ate a target, PMake is finished.

   Once all targets have been  processed,  PMake  executes  the
   commands  attached  to the ..EENNDD 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 (PMake keeps a running count of how many
   targets are left to be made), there is a cycle in the graph.
   PMake 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











                               -59-


   (3.1)
        This is something of a  trick  question,  for  which  I
        apologize.  The trick comes from the UNIX definition of
        a suffix, which PMake 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
        (..mmss,  ..cc, etc.). Now, PMake'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:

             ..SSUUFFFFIIXXEESS       :: eecc..eexxee ..eexxee eecc..oobbjj ..oobbjj ..aassmm
             eecc..oobbjjeecc..eexxee ..oobbjj..eexxee ::
                     lliinnkk --oo $$((..TTAARRGGEETT)) $$((..IIMMPPSSRRCC))
             ..aassmmeecc..oobbjj      ::
                     aassmm --oo $$((..TTAARRGGEETT)) --DDDDOO__EERRRROORR__CCHHEECCKKIINNGG $$((..IIMMPPSSRRCC))
             ..aassmm..oobbjj        ::
                     aassmm --oo $$((..TTAARRGGEETT)) $$((..IIMMPPSSRRCC))


   (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

             $$((PPTTRR::SS//^^//\\$$((//::SS//$$//))))

        which  places  ``$$(('' at the front of the variable name
        and ``))'' at the end, thus  transforming  ``VVAARR,''  for
        example,  into  ``$$((VVAARR)),'' which is just what we want.
        Unfortunately (as you know if you've tried it),  since,
        as it says in the hint, PMake 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:

             **PPTTRR            ::== $$((PPTTRR::SS//^^//\\$$((//::SS//$$//))//))

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

   66..  GGlloossssaarryy ooff JJaarrggoonn

   aattttrriibbuuttee:: A property given to a target that causes PMake 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.










                               -60-


   ccoommmmaanndd--lliinnee  vvaarriiaabbllee::  A  variable  defined in an argument
        when PMake is first executed.   Overrides  all  assign-
        ments 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 PMake.

   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 PMake 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.

   llooccaall vvaarriiaabbllee:: A variable defined by PMake visible only  in
        a  target's  shell script.  There are seven local vari-
        ables, not all of which are defined for  every  target:
        ..TTAARRGGEETT,  ..AALLLLSSRRCC, ..OOOODDAATTEE, ..PPRREEFFIIXX, ..IIMMPPSSRRCC, ..AARRCCHHIIVVEE,
        and ..MMEEMMBBEERR.  ..TTAARRGGEETT, ..PPRREEFFIIXX, ..AARRCCHHIIVVEE,  and  ..MMEEMMBBEERR
        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
        tutorial....

   mmooddiiffiieerr:: A letter, following a colon, used to alter  how  a
        variable is expanded.  It has no effect on the variable
        itself.











                               -61-


   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.  PMake'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 PMake to do special
        things when it's encountered.

   ssuuffffiixx:: The tail end of a file name. Usually begins  with  a
        period, ..cc or ..mmss, e.g.

   ttaarrggeett::  A  word to the left of the operator on a dependency
        line. More generally, any file that PMake might create.
        A file may be (and often is) both a target and a source
        (what it is depends on how PMake 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.





















                                -i-


                           TTaabbllee ooff CCoonntteennttss


        1. Introduction . . . . . . . . . . . . . . . . . .   1
        2. The Basics of PMake  . . . . . . . . . . . . . .   2
        2.1. Dependency Lines . . . . . . . . . . . . . . .   2
        2.2. Shell Commands . . . . . . . . . . . . . . . .   5
        2.3. Variables  . . . . . . . . . . . . . . . . . .   7
        2.3.1. Local Variables  . . . . . . . . . . . . . .  10
        2.3.2. Command-line Variables . . . . . . . . . . .  11
        2.3.3. Global Variables . . . . . . . . . . . . . .  12
        2.3.4. Environment Variables  . . . . . . . . . . .  12
        2.4. Comments . . . . . . . . . . . . . . . . . . .  13
        2.5. Parallelism  . . . . . . . . . . . . . . . . .  13
        2.6. Writing and Debugging a Makefile . . . . . . .  14
        2.7. Invoking PMake . . . . . . . . . . . . . . . .  17
        2.8. Summary  . . . . . . . . . . . . . . . . . . .  23
        2.9. Exercises  . . . . . . . . . . . . . . . . . .  24
        3. Short-cuts and Other Nice Things . . . . . . . .  24
        3.1. Transformation Rules . . . . . . . . . . . . .  24
        3.2. Including Other Makefiles  . . . . . . . . . .  29
        3.3. Saving Commands  . . . . . . . . . . . . . . .  30
        3.4. Target Attributes  . . . . . . . . . . . . . .  31
        3.5. Special Targets  . . . . . . . . . . . . . . .  37
        3.6. Modifying Variable Expansion . . . . . . . . .  40
        3.7. More on Debugging  . . . . . . . . . . . . . .  42
        3.8. More Exercises . . . . . . . . . . . . . . . .  42
        4. PMake for Gods . . . . . . . . . . . . . . . . .  43
        4.1. Search Paths . . . . . . . . . . . . . . . . .  43
        4.2. Archives and Libraries . . . . . . . . . . . .  45
        4.3. On the Condition...  . . . . . . . . . . . . .  48
        4.4. A Shell is a Shell is a Shell  . . . . . . . .  52
        4.5. Compatibility  . . . . . . . . . . . . . . . .  56
        4.5.1. DEFCON 3 -- Variable Expansion . . . . . . .  56
        4.5.2. DEFCON 2 -- The Number of the Beast  . . . .  56
        4.5.3. DEFCON 1 -- Imitation is Not the Highest Form
        of Flattery . . . . . . . . . . . . . . . . . . . .  57
        4.6. The Way Things Work  . . . . . . . . . . . . .  57
        5. Answers to Exercises . . . . . . . . . . . . . .  58
        6. Glossary of Jargon . . . . . . . . . . . . . . .  59
        Index . . . . . . . . . . . . . . . . . . . . . . .  62



















