








                       [1mPMake -- A Tutorial[0m

                           [4mAdam[24m [4mde[24m [4mBoor[0m
                        Berkeley Softworks
                   2150 Shattuck Ave, Penthouse
                        Berkeley, CA 94704
                         adam@bsw.uu.net
                        ...!uunet!bsw!adam

                           [4mRevisions[24m [4mby[0m
                         [4mAndreas[24m [4mStolcke[0m
             International Computer Science Institute
                  1947 Center Street, Suite 600
                        Berkeley, CA 94704
                    stolcke@icsi.berkeley.edu






   18 March 2007


















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















                       [1mPMake -- A Tutorial[0m

                           [4mAdam[24m [4mde[24m [4mBoor[0m
                        Berkeley Softworks
                   2150 Shattuck Ave, Penthouse
                        Berkeley, CA 94704
                         adam@bsw.uu.net
                        ...!uunet!bsw!adam

                           [4mRevisions[24m [4mby[0m
                         [4mAndreas[24m [4mStolcke[0m
             International Computer Science Institute
                  1947 Center Street, Suite 600
                        Berkeley, CA 94704
                    stolcke@icsi.berkeley.edu



   [1m1.  Introduction[0m

   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:
   [4mNote[0m
   -----------
      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.
   [4mOLD[24m [4mThe[24m [4mtutorial[24m [4myou[24m [4mare[24m [4mreading[24m [4mhas[24m [4mbeen[24m [4mupdated[24m [4mto[24m [4mreflect[0m
   [4mthe[24m [4mchanges[24m [4mmade[24m [4mto[24m [4mPMake[24m [4mat[24m [4mICSI[24m [4mover[24m  [4mrecent[24m  [4myears.[24m   [4mThe[0m
   [4mchanges[24m  [4mfall[24m [4minto[24m [4mfour[24m [4mbroad[24m [4mcategories:[24m [4m1)[24m [4mremoval[24m [4mof[24m [4mgra-[0m
   [4mtuitous[24m  [4mincompatibilities[24m  [4mwith[24m  [4mother[24m  [4mMake[24m  [4mversions,[24m  [4m2)[0m
   [4menhancements[24m  [4m(often[24m [4mborrowed[24m [4mfrom[24m [4mother[24m [4mMakes),[24m [4m3)[24m [4mfeatures[0m
   [4mpreviously[24m [4mpresent[24m [4mbut[24m [4mundocumented,[24m  [4mand[24m  [4m4)[24m  [4moutright[24m  [4mbug[0m
   [4mfixes.[24m   [4mTo[24m [4mhelp[24m [4mhighlight[24m [4mthe[24m [4mdifferences[24m [4mobsolete[24m [4mtext[24m [4mhas[0m
   [4mbeen[24m [4mretained[24m [4mand[24m [4mflagged[24m [4mwith[24m [4mthe[24m [4m"OLD"[24m [4msign[24m [4myou[24m [4msee[24m  [4mhere.[0m
   [4mNEW[24m  [4mNew[24m  [4mand[24m  [4mupdated[24m  [4mfeatures[24m [4mare[24m [4msimilarly[24m [4mmarked[24m [4mwith[24m [4ma[0m
   [4m"NEW"[24m [4msign,[24m [4mand[24m [4mimportant[24m [4mchanges[24m [4mare[24m  [4madditionally[24m  [4mtypeset[0m
   [4min[24m [4mitalic[24m [4mfont.[24m  [4m--A.S.[0m


   [1m2.  The Basics of PMake[0m

   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 [1mMakefile [22mand [1mmakefile[0m
   (in  that  order) in the current directory if you don't tell
   it otherwise.  To specify a different makefile, use  the  [1m-f[0m
   flag (e.g.  ``[1mpmake -f program.mk[22m'').

   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.

   [1m2.1.  Dependency Lines[0m











                                -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  ([1ma.c[22m,  [1mb.c  [22mand
   [1mc.c[22m)  each of which includes the file [1mdefs.h[22m.  The dependen-
   cies between the files could then be expressed as follows:

        [1mprogram         : a.o b.o c.o[0m
        [1ma.o b.o c.o     : defs.h[0m
        [1ma.o             : a.c[0m
        [1mb.o             : b.c[0m
        [1mc.o             : c.c[0m


   You may be wondering at this point, where [1ma.o[22m, [1mb.o  [22mand  [1mc.o[0m
   came in and why [4mthey[24m depend on [1mdefs.h [22mand the C files don't.
   The reason is quite simple: [1mprogram [22mcannot be made by  link-
   ing  together  .c  files  --  it must be made from .o files.
   Likewise, if you change [1mdefs.h[22m, 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.  [1ma.o [22mdepends on both [1mdefs.h [22mand [1ma.c[22m.
   [4mNote[0m

   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  [1mprogram [22mcomes first in the example
   makefile, above.
   [4mNEW[24m [1mCompatibility: [4m[22mHowever,[24m [4mtargets[24m [4mstarting[24m [4mwith[24m  [4ma[24m  [4mperiod[0m
   [4mand[24m  [4mnot[24m  [4mcontaining[24m [4m``/''[24m [4mare[24m [4mnot[24m [4mused[24m [4mas[24m [4mdefault[24m [4mmain[24m [4mtar-[0m
   [4mgets.[24m  [4mThis[24m [4mseems[24m [4mto[24m [4mbe[24m [4mwhat[24m [4mother[24m [4mMakes[24m [4mdo,[24m [4mand[24m  [4malso[24m  [4mpre-[0m
   [4mvents[24m  [4mmiscellaneous[24m  [4mpseudo-targets[24m  [4m(specific[24m  [4mto[24m [4mPMake[24m [4mor[0m
   [4mother[24m [4mMakes)[24m [4mfrom[24m [4mbeing[24m [4mmistaken[24m [4mas[24m [4mnormal[24m [4mtargets.[0m

   Both targets and sources may contain  the  standard  C-Shell
   wildcard  characters  ([1m{[22m,  [1m}[22m,  [1m*[22m, [1m?[22m, [1m[[22m, and [1m][22m), 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:

   [1m{}   [22mThese 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, [1msrc/{whiffle,beep,fish}.c [22mexpands to
        the  three   words   [1msrc/whiffle.c[22m,   [1msrc/beep.c[22m,   and
        [1msrc/fish.c[22m.  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.

   [1m*    [22mThis  matches  zero  or  more  characters  of any sort.
        [1msrc/*.c [22mwill expand to the same three words as above as
        long  as  [1msrc  [22mcontains those three files (and no other
        files that end in [1m.c[22m).

   [1m?    [22mMatches any single character.

   [1m[]   [22mThis is known as a character class and contains  either
        a  list  of single characters, or a series of character
        ranges ([1ma-z[22m, for example means all characters between a
        and  z),  or both. It matches any single character con-
        tained in the list. E.g.  [1m[A-Za-z] [22mwill match all  let-
        ters, while [1m[0123456789] [22mwill match all numbers.

   [1m2.2.  Shell Commands[0m

   ``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 [4mmust[24m 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.
   [4mNote[0m










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

        [1mprogram         : a.o b.o c.o[0m
                [1mcc a.o b.o c.o -o program[0m
        [1ma.o b.o c.o     : defs.h[0m
        [1ma.o             : a.c[0m
                [1mcc -c a.c[0m
        [1mb.o             : b.c[0m
                [1mcc -c b.c[0m
        [1mc.o             : c.c[0m
                [1mcc -c c.c[0m


   Something  you  should  remember when writing a makefile is,
   the commands will be executed if the [4mtarget[24m  on  the  depen-
   dency  line  is out-of-date, not the sources.  In this exam-
   ple, the command ``[1mcc -c a.c[22m'' will be executed  if  [1ma.o  [22mis
   out-of-date.  Because  of  the `:' operator, this means that
   should [1ma.c [4m[22mor[24m [1mdefs.h [22mhave been modified more  recently  than
   [1ma.o[22m,  the  command  will be executed ([1ma.o [22mwill 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.
   [4mNEW[24m [1mCompatibility: [4m[22mThe[24m [4m`@'[24m [4mand[24m [4m`-'[24m  [4mcharaters[24m  [4mmay[24m  [4mactually[0m
   [4moccur[24m  [4minterspersed[24m [4mwith[24m [4mwhite[24m [4mspace,[24m [4mand[24m [4minitial[24m [4mwhitespace[0m
   [4mis[24m [4mremoved[24m [4mfrom[24m [4mthe[24m [4mshell[24m [4mcommand[24m [4mline.[0m

   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 [1mecho [22mcommand, say ``[1mecho Linking[0m
   [1mindex[22m,'' it would be rather silly to see

        [1mecho Linking index[0m
        [1mLinking index[0m


   so PMake allows you to  place  an  `@'  before  the  command
   (``[1m@echo  Linking index[22m'') 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  (``[1m-mv  index  index.old[22m''),  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.
   [4mNote[0m

   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 [1mfor[0m
   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  [1m-B  [22mflag
   (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....
   [4mNote[0m

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

        [1mci $(SRCS) < /dev/tty[0m

   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 [1mCompatibility [22min chapter 4.
   [4mNEW[24m [1mNote: [4m[22mWhen[24m [4mexporting[24m  [4mcommands[24m  [4mto[24m  [4mother[24m  [4mmachines[24m  [4mthe[0m
   [1m/dev/tty  [4m[22mtrick[24m  [4mwon't[24m  [4mwork,[24m [4mof[24m [4mcourse.[24m  [4mSuch[24m [4mtarget[24m [4mshould[0m
   [4mtherefore[24m [4malso[24m [4mbe[24m [4mmarked[24m [4mwith[24m [1m.NOEXPORT[4m[22m.[0m


   [1m2.3.  Variables[0m

   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 [4mhave[24m to use all upper-case letters.  In  fact  there's
   nothing  to  stop  you from calling a variable [1m@^&$%$[22m.  Just
   tradition). Variables are assigned-to  using  lines  of  the
   form

        [1mVARIABLE = value[0m

   appended-to by

        [1mVARIABLE += value[0m

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

        [1mVARIABLE ?= value[0m

   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

        [1mVARIABLE := value[0m


   Any whitespace before [4mvalue[24m 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

        [1mVARIABLE != shell-command[0m

   In  this  case, [4mshell-command[24m 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:

        [1mCWD             != pwd[0m

   [4mOLD[0m

   [1mNote: [22mthis 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).
   [4mNEW[24m [1mFixed: [4m[22mAny[24m [4mamount[24m [4mof[24m [4moutput[24m [4mcan[24m [4mnow[24m [4mbe[24m [4mread[24m  [4mby[24m  [4mthe[24m  [1m!=[0m
   [4massignment[24m  [4moperator,[24m  [4msubject[24m  [4monly[24m  [4mto[24m [4mmemory[24m [4mlimitations.[0m
   [4mAlso,[24m [4mnote[24m [4mthat[24m [4mthe[24m [4mcommand[24m [4mfollowing[24m [1m!= [4m[22mis[24m [4mexecuted[24m [4mby[24m  [4mthe[0m
   [4mprevailing[24m [4mshell,[24m [4mas[24m [4mspecified[24m [4mby[24m [4mthe[24m [1m.SHELL [4m[22mtarget.[0m









                                -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
   ``[1m-I/sprite/src/lib/libc -O[22m,'' you would place a line

        [1mCFLAGS = -I/sprite/src/lib/libc -O[0m

   in the makefile and use  the  word  [1m$(CFLAGS)  [22mwherever  you
   would  like  the string [1m-I/sprite/src/lib/libc -O [22mto appear.
   This is called variable expansion.
   [4mOLD[0m

   Unlike Make, PMake will not  expand  a  variable  unless  it
   knows  the  variable  exists.  E.g.  if you have a [1m${i} [22min a
   shell command and you have not assigned a value to the vari-
   able [1mi [22m(the empty string is considered a value, by the way),
   where Make would have substituted the  empty  string,  PMake
   will  leave the [1m${i} [22malone.  To keep PMake from substituting
   for a variable  it  knows,  precede  the  dollar  sign  with
   another  dollar  sign.   (e.g. to pass [1m${HOME} [22mto the shell,
   use [1m$${HOME}[22m).  This causes PMake, in effect, to expand  the
   [1m$  [22mmacro,  which  expands to a single [1m$[22m.  For compatibility,
   Make's style of variable  expansion  will  be  used  if  you
   invoke  PMake with any of the compatibility flags ([1m-V[22m, [1m-B [22mor
   [1m-M[22m.  The [1m-V [22mflag alters just the variable expansion).
   [4mNEW[0m

   [1mCompatibility:   [4m[22mSince[24m   [4mnon-standard[24m   [4mvariable[24m   [4mexpansion[0m
   [4maccounted[24m  [4mfor[24m [4mthe[24m [4mvast[24m [4mmajority[24m [4mof[24m [4mproblems[24m [4mwith[24m [4mpre-exist-[0m
   [4ming[24m [4mmakefiles,[24m [4mit[24m [4mis[24m [4mnow[24m [4mdisabled[24m [4mby[24m [4mdefault,[24m [4mas[24m  [4mif[24m  [4mspeci-[0m
   [4mfied[24m  [4mby[24m [1m-V[4m[22m.[24m  [4mTo[24m [4mre-enable[24m [4mthe[24m [4moriginal[24m [4mPMake[24m [4mstyle[24m [4mof[24m [4mvari-[0m
   [4mable[24m [4mexpansion,[24m [4muse[24m [4mthe[24m [1m-C [4m[22mflag.[0m

   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.
   [4mNEW[24m  As  in  other  Makes, the [1m-e [22mflag switches the order in
   which global variables and environment are searched.   (This
   is a compatibility feature that was not documented.)

   [1m2.3.1.  Local Variables[0m

   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
             [4mOLD[24m The target without its suffix and without  any
             leading  path.  E.g. for the target [1m../../lib/com-[0m
             [1mpat/fsRead.c[22m, this variable would contain  [1mfsRead[22m.
             [4mNEW[24m [1mCompatibility: [4m[22mStripping[24m [4mof[24m [4mdirectory[24m [4mpaths[24m [4min[0m
             [4m.PREFIX[24m [4mis[24m [4mannoyingly[24m [4mdifferent[24m [4mfrom[24m [4mother[24m  [4mmodern[0m
             [4mMakes.[24m   [4mDirectory[24m  [4mpaths[24m  [4mare[24m [4mtherefore[24m [4mretained,[0m
             [4mincluding[24m  [4mthose[24m  [4madded[24m  [4mas[24m  [4mthe[24m  [4mresult[24m  [4mof[24m  [4mpath[0m
             [4msearches.[24m   [4mThe[24m  [4mfilename[24m  [4mportion[24m  [4mof[24m [4man[24m [4mexpanded[0m
             [4m.PREFIX[24m [4mcan[24m [4measily[24m [4mbe[24m  [4mobtained[24m  [4mby[24m  [4mapplying[24m  [4mthe[0m
             [4m`:T'[24m [4mmodifier.[0m

   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'', ``.AR-
   CHIVE'' and ``.MEMBER''. The variables in  the  sources  are
   expanded  once  for each target on the dependency line, pro-
   viding what is known as a ``dynamic source,''  allowing  you
   to specify several dependency lines at once. For example,

        [1m$(OBJS)         : $(.PREFIX).c[0m

   will  create  a  dependency between each object file and its
   corresponding C source file.
   [4mNEW[24m [4mDue[24m [4mto[24m [4mthe[24m [4mabove[24m [4mchange,[24m [4mthis[24m  [4mdependency[24m  [4mwould[24m  [4mlocate[0m
   [4mthe[24m  [4msource[24m [4mfiles[24m [4min[24m [4mthe[24m [4msame[24m [4mdirectory[24m [4mas[24m [4mthe[24m [4mobject[24m [4mfiles.[0m
   [4mIf[24m [1m$(OBJS) [4m[22mincludes[24m [4mdirectories[24m [4mpath[24m [4mand[24m [4mthe[24m [4msources[24m [4mare[24m  [4mto[0m
   [4mbe[24m [4mlocated[24m [4melsewhere,[24m [4muse[0m

        [1m$(OBJS)         : $(.PREFIX:T).c[0m

   instead.
   [4mNEW[0m

   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:

        [1m@    .TARGET[0m
        [1m?    .OODATE[0m
        [1m>    .ALLSRC[0m
        [1m*    .PREFIX[0m
        [1m<    .IMPSRC[0m
        [1m!    .ARCHIVE[0m
        [1m%    .MEMBER[0m

   These were always present by undocumented.

   [1m2.3.2.  Command-line Variables[0m

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

        [1mpmake "CFLAGS = -I/sprite/src/lib/libc -O"[0m

   would make [1mCFLAGS [22mbe a command-line variable with the  given
   value.  Any  assignments to [1mCFLAGS [22min 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 [1m= [22mand [1m?=[0m
   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
   unavailable  at  the time.  [1m+= [22mis the same as [1m=[22m, because the









                               -12-


   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).  [1m:= [22mand [1m?= [22mwill work  if
   the  only variables used are in the environment.  [1m!= [22mis 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).

   [1m2.3.3.  Global Variables[0m

   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  [1m-f[0m
             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:

        [1m#undef [4m[22mvariable[0m

   The `[1m#[22m' must be the first character on the line.  Note  that
   this may only be done on global variables.

   [1m2.3.4.  Environment Variables[0m

   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,  [1mPMAKE[22m,  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 [1mPMAKE [22mvariable or on  the  com-
   mand  line,  are  placed  in  this  environment variable and









                               -13-


   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:

        [1mOBJS            = a.o b.o c.o[0m
        [1mprogram         : $(OBJS)[0m
                [1mcc $(.ALLSRC) -o $(.TARGET)[0m
        [1m$(OBJS)         : defs.h[0m
        [1ma.o             : a.c[0m
                [1mcc -c a.c[0m
        [1mb.o             : b.c[0m
                [1mcc -c b.c[0m
        [1mc.o             : c.c[0m
                [1mcc -c c.c[0m


   [1m2.4.  Comments[0m

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

   [1m2.5.  Parallelism[0m
   [4mNote[0m

   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 [1m-L [22mand [1m-J [22mflags (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 [1mxstr[22m, for  example,  always  modifies  the
   files  [1mstrings  [22mand [1mx.c[22m.  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 [1m$$$$  [22mto  the
   end  of the file name to tack on the process ID of the shell
   executing the command (each [1m$$ [22mexpands to a single  [1m$[22m,  thus
   giving  you the shell variable [1m$$[22m).  Since only one shell is
   used for all the commands, you'll get the same file name for









                               -14-


   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

        [1ma               : b c[0m
        [1mb               : d[0m

   PMake will examine the targets in the order [1mc[22m, [1md[22m, [1mb[22m, [1ma[22m.   If
   the  makefile's author expected PMake to abort before making
   [1mc [22mif an error occurred while making [1mb[22m, or  if  [1mb  [22mneeded  to
   exist  before  [1mc [22mwas made, s/he will be sorely disappointed.
   The dependencies are incomplete, since in both these  cases,
   [1mc [22mwould depend on [1mb[22m.  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  [1m/dev/tty  [22mI mentioned earlier, you must be careful not
   to run two of the jobs at once.

   [1m2.6.  Writing and Debugging a Makefile[0m

   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 [4mare[24m 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, [1mexpr[22m, 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:  [1mmain.c[22m,
   [1mparse.c[22m,  and  [1moutput.c[22m.   Harking  back  to my pithy advice
   about dependency lines, you write  the  first  line  of  the
   file:

        [1mexpr            : main.o parse.o output.o[0m

   because  you  remember  [1mexpr  [22mis  made from [1m.o [22mfiles, not [1m.c[0m
   files. Similarly for the [1m.o [22mfiles you produce the lines:









                               -15-


        [1mmain.o          : main.c[0m
        [1mparse.o         : parse.c[0m
        [1moutput.o        : output.c[0m
        [1mmain.o parse.o output.o : defs.h[0m


   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

        [1mexpr            : main.o parse.o output.o[0m
                [1mcc -o $(.TARGET) $(.ALLSRC)[0m

   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 [4mand[0m
   [1mdefs.h[22m, thus if you said

             [1mcc -c $(.ALLSRC)[0m

   you'd get (for [1mmain.o[22m):

             [1mcc -c main.c defs.h[0m

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

        [1mmain.o          : main.c[0m
                [1mcc -c main.c[0m
        [1mparse.o         : parse.c[0m
                [1mcc -c parse.c[0m
        [1moutput.o        : output.c[0m
                [1mcc -c output.c[0m


   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  ``[1mmain.o  parse.o  output.o[22m''  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:

        [4mAnything[24m  [4mthat[24m  [4mneeds[24m [4mto[24m [4mbe[24m [4mwritten[24m [4mmore[24m [4mthan[24m [4monce[0m
        [4mshould[24m [4mbe[24m [4mplaced[24m [4min[24m [4ma[24m [4mvariable.[0m










                               -16-


        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:

        [4mAny[24m  [4mflags[24m  [4mor[24m  [4mprograms[24m  [4mused[24m  [4minside[24m  [4ma[24m [4mmakefile[0m
        [4mshould[24m [4mbe[24m [4mplaced[24m [4min[24m [4ma[24m  [4mvariable[24m  [4mso[24m  [4mthey[24m  [4mmay[24m  [4mbe[0m
        [4mchanged,[24m  [4mtemporarily[24m  [4mor[24m  [4mpermanently,[24m  [4mwith[24m  [4mthe[0m
        [4mgreatest[24m [4mease.[0m

   The makefile should more properly read:

        [1mOBJS            = main.o parse.o output.o[0m
        [1mexpr            : $(OBJS)[0m
                [1m$(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)[0m
        [1mmain.o          : main.c[0m
                [1m$(CC) $(CFLAGS) -c main.c[0m
        [1mparse.o         : parse.c[0m
                [1m$(CC) $(CFLAGS) -c parse.c[0m
        [1moutput.o        : output.c[0m
                [1m$(CC) $(CFLAGS) -c output.c[0m
        [1m$(OBJS)         : defs.h[0m

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

        [1mOBJS            = main.o parse.o output.o[0m
        [1mexpr            : $(OBJS)[0m
                [1m$(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)[0m
        [1m$(OBJS)         : $(.PREFIX).c defs.h[0m
                [1m$(CC) $(CFLAGS) -c $(.PREFIX).c[0m

   These  two rules and examples lead to de Boor's First Corol-
   lary:

        [4mVariables[24m [4mare[24m [4myour[24m [4mfriends.[0m

   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  [1m-n [22mflag, which allows you to see if
   PMake will choke on the makefile. The second  thing  the  [1m-n[0m
   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.

   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: ``[1m-d m[22m'' and ``[1m-p  2[22m.''   The  first  causes









                               -17-


   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  ``[1m-p  2[22m''  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 ``[1m-p 2[22m'' 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.

        [1ma         : b[0m
        [1mb         : c d[0m
        [1md         : a[0m

   In this case, because of how PMake  works,  [1mc  [22mis  the  only
   thing  PMake  will examine, because [1md [22mand [1ma [22mwill effectively
   fall off the edge of the universe, making it  impossible  to
   examine  [1mb  [22m(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.

   [1m2.7.  Invoking PMake[0m

   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:

   [1m-d [4m[22mwhat[0m
        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  [4mwhat[24m  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:

        a    Archive searching and caching.

        c    Conditional evaluation.











                               -18-


        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 [1mm [22mand [1ms [22mletters will be most useful
        to you.

   [1m-e   [22mCauses global variable values defined in the UNIX envi-
        ronment  to  take precedence over any assignments found
        in the makefile.
        [4mNEW[24m [4mThis[24m [4mflag[24m [4mwas[24m [4mpreviously[24m [4mpresent[24m [4mbut[24m  [4mundocumented.[0m

   [1m-f [4m[22mmakefile[0m
        Specify  a makefile to read different from the standard
        makefiles  ([1mMakefile  [22mor  [1mmakefile[22m).   If  [4mmakefile[24m  is
        ``-'',  PMake  uses  the standard input. This is useful
        for making quick and dirty makefiles...

   [1m-h   [22mPrints  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 [1m-J [22mand [1m-L[22m) and various other infor-
        mation on how PMake was configured.

   [1m-i   [22mIf 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.

   [1m-k   [22mThis is similar to [1m-i [22min that it allows PMake  to  con-
        tinue when it sees an error, but unlike [1m-i[22m, where PMake
        continues blithely as if nothing went wrong, [1m-k  [22mcauses
        it  to  recognize  the  error and only continue work on









                               -19-


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

   [1m-l   [22mPMake 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, [1m-l [22mwill
        turn  it  off,  and  vice versa. Note that this locking
        will not prevent [4myou[24m from invoking PMake twice  in  the
        same place -- if you own the lock file, PMake will warn
        you about it but continue to execute.
        [4mNEW[24m [4mWhen[24m [4mrunning[24m [4mas[24m  [4msuper-user,[24m  [4mPMake[24m  [4mwill[24m  [4mcontinue[0m
        [4minspite[24m [4mof[24m [4ma[24m [4mfailure[24m [4mto[24m [4mcreate[24m [4mthe[24m [4mlock[24m [4mfile,[24m [4missuing[24m [4ma[0m
        [4mwarning[24m [4mto[24m [4mthat[24m [4meffect.[24m  [4mFrequently[24m [4mthe[24m [4mroot[24m [4muser[24m  [4mcan-[0m
        [4mnot[24m  [4mcreate[24m  [4mfiles[24m  [4min[24m  [4msource[24m [4mtrees[24m [4mmounted[24m [4mfrom[24m [4mother[0m
        [4msystems,[24m [4meven[24m [4mif[24m [4mall[24m [4mthat's[24m [4mwanted[24m [4mis[24m  [4ma[24m  [4msimple[24m  [4m`make[0m
        [4minstall.'[0m

   [1m-n   [22mThis  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.

   [1m-p [4m[22mnumber[0m
        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 [4mnumber[24m 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  [1m-p  3[0m
        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.

   [1m-q   [22mIf 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.

   [1m-r   [22mWhen PMake starts up, it reads a default makefile  that
        tells  it what sort of system it's on and gives it some
        idea of what to do if you don't tell it anything.  I'll
        tell  you about it in chapter 3. If you give this flag,
        PMake won't read the default makefile.










                               -20-


   [1m-s   [22mThis causes PMake to not print commands before  they're
        executed. It is the equivalent of putting an `@' before
        every command in the makefile.

   [1m-t   [22mRather 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.

   [1m-v   [22mThis is a mixed-compatibility flag  intended  to  mimic
        the  System V version of Make. It is the same as giving
        [1m-B[22m, and [1m-V [22mas 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
        ``[1msmake[22m'' or ``[1mvmake[22m''.

   [1m-x   [22mThis  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.

   [1m-B   [22mForces 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.

   [1m-C   [22mThis nullifies any and all compatibility mode flags you
        may have given or implied up to  the  time  the  [1m-C  [22mis
        encountered. It is useful mostly in a makefile that you
        wrote for PMake to  avoid  bad  things  happening  when
        someone runs PMake as ``[1mmake[22m'' or has things set in the
        environment that tell it to be compatible.  [1m-C  [22mis  [4mnot[0m
        placed  in the [1mPMAKE [22menvironment variable or the [1m.MAKE-[0m
        [1mFLAGS [22mor [1mMFLAGS [22mglobal variables.

   [1m-D [4m[22mvariable[0m
        Allows you to define a variable to have  ``[1m1[22m''  as  its
        value.   The  variable is a global variable, not a com-
        mand-line variable. This is useful  mostly  for  people
        who  are  used  to  the  C compiler arguments and those
        using conditionals, which I'll get into in section 4.3









                               -21-


   [1m-I [4m[22mdirectory[0m
        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).

   [1m-J [4m[22mnumber[0m
        Gives the absolute maximum number of targets to  create
        at once on both local and remote machines.

   [1m-L [4m[22mnumber[0m
        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.
        [4mNEW[24m  [4mA[24m [4mnegative[24m [4margument[24m [4mto[24m [4mthis[24m [4mflag[24m [4mlimits[24m [4mthe[24m [4mnumber[0m
        [4mof[24m [4mlocal[24m [4mjobs[24m [4m(to[24m [4mthe[24m [4mabsolute[24m [4mvalue,[24m [4mof[24m [4mcourse)[24m  [4mwhile[0m
        [4malso[24m  [4mforcing[24m  [4mall[24m [4mlocal[24m [4mjobs[24m [4mslots[24m [4mto[24m [4mbe[24m [4mfilled[24m [4mbefore[0m
        [4mexportation[24m [4mcan[24m [4mbegin.[24m  [4mThis[24m [4mis[24m [4museful[24m [4mto[24m [4mforce[24m [4muse[24m  [4mof[0m
        [4mone's[24m  [4mown[24m  [4mmachine[24m  [4min[24m  [4mcases[24m  [4mwhere[24m [4mit[24m [4mis[24m [4mknown[24m [4mto[24m [4mbe[0m
        [4mfaster[24m [4mthan[24m [4mothers.[0m

   [1m-M   [22mThis 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
        ``[1mmake[22m.''

   [1m-P   [22mWhen 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 [1m-P [22mflag to PMake.
        [4mNEW[24m  [4mWhen[24m  [4mexporting[24m  [4mcommands[24m  [4musing[24m [4mpiped[24m [4moutput,[24m [4mthe[0m
        [4midentification[24m [4mlines[24m  [4malso[24m  [4mcontain[24m  [4mthe[24m  [4mname[24m  [4mof[24m  [4mthe[0m
        [4mremote[24m [4mhost[24m [4mexecuting[24m [4mthe[24m [4mcommand.[0m
        [4mNEW[0m











                               -22-


   [1m-R [4m[22mwhen[0m
        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  [4mwhen[0m
        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.

   [1m-S   [22mThis overrides a previous [1m-k [22mflag and  instructs  PMake
        to  abort  as  soon  as  a  target creation command has
        failed.
        [4mNEW[24m [4mThis[24m [4mflag[24m [4mwas[24m [4mpreviously[24m [4mpresent[24m [4mbut[24m  [4mundocumented.[0m

   [1m-V   [22mAs  mentioned  before,  the  [1m-V [22mflag tells PMake to use
        Make's style of expanding variables,  substituting  the
        empty string for any variable it doesn't know.
        [4mNEW[24m  [4mAs[24m  [4mlikewise[24m  [4mmentioned[24m  [4mbefore,[24m  [4mthis[24m [4mflag[24m [4mis[24m [4mnow[0m
        [4mturned[24m [4mon[24m [4mby[24m [4mdefault[24m [4min[24m [4mthe[24m [4mstandard[24m [4mconfiguration.[24m  [4mTo[0m
        [4mrevert[24m [4mto[24m [4mPMake-style[24m [4mvariable[24m [4mexpansion[24m [4muse[24m [1m-C[4m[22m.[0m

   [1m-W   [22mThere 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.

   [1m-X   [22mThis  flag  causes  PMake  to not attempt to export any
        jobs to another machine.
        [4mNEW[0m

   [1m-Z [4m[22mc[24m Changes the character used to introduce Makefile direc-
        tives  to  [4mc[24m, 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.

        [1mpmake -fDnI server.mk DEBUG /chip2/X/server/include[0m

   will  cause  PMake  to read [1mserver.mk [22mas the input makefile,
   define the variable [1mDEBUG [22mas a global variable and look  for
   included makefiles in the directory [1m/chip2/X/server/include[22m.
   [4mNEW[0m

   [1mFixed: [4m[22mThe[24m [4moriginal[24m [4mversion[24m [4mof[24m [4mPMake[24m [4min[24m [4mfact[24m [4mdid[24m [4mnot[24m  [4mhandle[0m
   [4mflags[24m  [4minterspersed[24m  [4mwith[24m  [4mvariable[24m [4massignments[24m [4mand[24m [4mtargets,[0m
   [4mwhich[24m [4mwas[24m [4ma[24m [4mmajor[24m [4mannoyance[24m [4mand[24m [4mincompatibility,[24m  [4mespecially[0m









                               -23-


   [4min[24m [4mrecursive[24m [4mmake[24m [4mcommands.[0m

   [1m2.8.  Summary[0m

   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  (`[1m:[22m',  `[1m::[22m',  or  `[1m![22m'),  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 `[1m@[22m' or `[1m-[22m',
   PMake will cause the command to not be printed (if the char-
   acter  is  `[1m@[22m') or errors from it to be ignored (if `[1m-[22m').  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 `[1m:[22m' or `[1m![22m'  operator.

   Variables are places to store text. They may be uncondition-
   ally  assigned-to  using the `[1m=[22m' operator, appended-to using
   the `[1m+=[22m' operator, conditionally (if the variable  is  unde-
   fined)  assigned-to  with the `[1m?=[22m' operator, and assigned-to
   with variable expansion with the `[1m:=[22m' operator.  The  output
   of  a  shell command may be assigned to a variable using the
   `[1m!=[22m' 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
   ([1m-C[22m) variables are not expanded if PMake doesn't know  about
   them.)   There  are seven local variables: [1m.TARGET[22m, [1m.ALLSRC[22m,
   [1m.OODATE[22m, [1m.PREFIX[22m, [1m.IMPSRC[22m, [1m.ARCHIVE[22m, and [1m.MEMBER[22m.   Four  of
   them  ([1m.TARGET[22m,  [1m.PREFIX[22m, [1m.ARCHIVE[22m, and [1m.MEMBER[22m) may be used
   to specify ``dynamic sources.''  Variables  are  good.  Know
   them. Love them. Live them.

   Debugging of makefiles is best accomplished using the [1m-n[22m, [1m-d[0m
   [1mm[22m, and [1m-p 2 [22mflags.

   [1m2.9.  Exercises[0m
                               [1mTBA[0m












                               -24-


   [1m3.  Short-cuts and Other Nice Things[0m

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

   [1m3.1.  Transformation Rules[0m

   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 [1m.c [22mis assumed to contain C source code; one with a
   [1m.o  [22msuffix  is  assumed to be a compiled, relocatable object
   file that may be linked into any program; a file with a  [1m.ms[0m
   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 [1m.SUFFIXES[22m.  E.g.

        [1m.SUFFIXES       : .o .c[0m
        [1m.c.o            :[0m
                [1m$(CC) $(CFLAGS) -c $(.IMPSRC)[0m

   The creation script attached to the target is used to trans-
   form  a file with the first suffix (in this case, [1m.c[22m) into a
   file with the second suffix (here, [1m.o[22m).   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 [1mcc [22mwith the  [1m-c  [22mflag.   This  rule  is  taken
   straight from the system makefile. Many transformation rules
   (and suffixes) are defined there, and I refer you to it  for
   more examples (type ``[1mpmake -h[22m'' to find out where it is).









                               -25-


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

        1)   The [1m.IMPSRC [22mvariable.  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 [1mCFLAGS [22mvariable. 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
             [1m-g [22mflag, to provide information for [1mdbx[22m, you would
             set  the [1mCFLAGS [22mvariable to contain [1m-g [22m(``[1mCFLAGS =[0m
             [1m-g[22m'') 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:

        [1mOBJS            = a.o b.o c.o[0m
        [1mprogram         : $(OBJS)[0m
                [1m$(CC) -o $(.TARGET) $(.ALLSRC)[0m
        [1m$(OBJS)         : defs.h[0m

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

        [1ma.o             : a.c[0m
                [1mcc -c a.c[0m
        [1mb.o             : b.c[0m
                [1mcc -c b.c[0m
        [1mc.o             : c.c[0m
                [1mcc -c c.c[0m


   Now you may be wondering about the dependency between the [1m.o[0m
   and [1m.c [22mfiles -- 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
   [4msource[24m.
   [4mNEW[24m [4mAs[24m [4mbefore,[24m [4mtransformation[24m [4mrules[24m [4mare[24m [4mnot[24m  [4mapplied[24m  [4mif[24m  [4mat[0m
   [4mleast[24m [4mone[24m [4mexplicit[24m [4mdependency[24m [4mwith[24m [4mcreation[24m [4mcommand[24m [4mis[24m [4mspec-[0m
   [4mified[24m [4mfor[24m [4ma[24m [4mtarget.[24m  [4mHowever,[24m [4mPMake[24m [4mused[24m [4mto[24m  [4mnot[24m  [4mobey[24m  [4mthis[0m
   [4mstipulation[24m  [4min[24m [4mthe[24m [4mcase[24m [4mof[24m [4mthe[24m [4mexplicit[24m [4mnull[24m [4mcommand,[24m [4mwhich[0m
   [4mis[24m [4moften[24m [4mused[24m [4mto[24m [4minhibit[24m [4mimplicit[24m [4mdependencies:[0m

        [1md.o: ;[0m


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









                               -26-


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

        [1ma.out           : a.o b.o[0m
                [1m$(CC) $(.ALLSRC)[0m

   and a directory set up like this:

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

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

        [1mSuff_FindDeps (a.out)[0m
             [1musing existing source a.o[0m
             [1mapplying .o -> .out to "a.o"[0m
        [1mSuff_FindDeps (a.o)[0m
             [1mtrying a.c...got it[0m
             [1mapplying .c -> .o to "a.c"[0m
        [1mSuff_FindDeps (b.o)[0m
             [1mtrying b.c...got it[0m
             [1mapplying .c -> .o to "b.c"[0m
        [1mSuff_FindDeps (a.c)[0m
             [1mtrying a.y...not there[0m
             [1mtrying a.l...not there[0m
             [1mtrying a.c,v...not there[0m
             [1mtrying a.y,v...not there[0m
             [1mtrying a.l,v...not there[0m
        [1mSuff_FindDeps (b.c)[0m
             [1mtrying b.y...not there[0m
             [1mtrying b.l...not there[0m
             [1mtrying b.c,v...not there[0m
             [1mtrying b.y,v...not there[0m
             [1mtrying b.l,v...not there[0m
        [1m--- a.o ---[0m
        [1mcc  -c a.c[0m
        [1m--- b.o ---[0m
        [1mcc  -c b.c[0m
        [1m--- a.out ---[0m
        [1mcc a.o b.o[0m


   [1mSuff_FindDeps [22mis 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,
   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,









                               -27-


   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 [1m.SUFFIXES [22mline -- 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 [1m.SUFFIXES [22mdependency 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, [1ma.out [22mdepends  on  [1ma.o[0m
   and  [1mb.o[22m.   Since  a  transformation exists from [1m.o [22mto [1m.out[22m,
   PMake uses that, as indicated by the ``[1musing existing source[0m
   [1ma.o[22m'' 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:

        [1m.SUFFIXES       : .out .o .c .y .l[0m
        [1m.l.c            :[0m
                [1mlex $(.IMPSRC)[0m
                [1mmv lex.yy.c $(.TARGET)[0m
        [1m.y.c            :[0m
                [1myacc $(.IMPSRC)[0m
                [1mmv y.tab.c $(.TARGET)[0m
        [1m.c.o            :[0m
                [1mcc -c $(.IMPSRC)[0m
        [1m.o.out          :[0m
                [1mcc -o $(.TARGET) $(.IMPSRC)[0m

   and the single file [1mjive.l[22m.  If you were to type ``[1mpmake -rd[0m
   [1mms  jive.out[22m,''  you  would  get  the  following  output for
   [1mjive.out[22m:

        [1mSuff_FindDeps (jive.out)[0m
             [1mtrying jive.o...not there[0m
             [1mtrying jive.c...not there[0m
             [1mtrying jive.y...not there[0m
             [1mtrying jive.l...got it[0m
             [1mapplying .l -> .c to "jive.l"[0m
             [1mapplying .c -> .o to "jive.c"[0m
             [1mapplying .o -> .out to "jive.o"[0m

   and this is why: PMake  starts  with  the  target  [1mjive.out[22m,









                               -28-


   figures  out  its  suffix ([1m.out[22m) and looks for things it can
   transform to a [1m.out [22mfile. In this case, it only finds [1m.o[22m, so
   it  looks  for  the file [1mjive.o[22m.  It fails to find it, so it
   looks for transformations into a [1m.o [22mfile. Again it has  only
   one  choice:  [1m.c[22m.   So it looks for [1mjive.c [22mand, as you know,
   fails to find it. At this point it has two choices:  it  can
   create the [1m.c [22mfile from either a [1m.y [22mfile or a [1m.l [22mfile. Since
   [1m.y [22mcame first on the [1m.SUFFIXES [22mline, it  checks  for  [1mjive.y[0m
   first, but can't find it, so it looks for [1mjive.l [22mand, lo and
   behold, there it is.  At this point, it has defined a trans-
   formation  path  as  follows:  [1m.l  [22m->  [1m.c  [22m-> [1m.o [22m-> [1m.out [22mand
   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:

        [1mSuff_FindDeps (jive.o)[0m
             [1musing existing source jive.c[0m
             [1mapplying .c -> .o to "jive.c"[0m
        [1mSuff_FindDeps (jive.c)[0m
             [1musing existing source jive.l[0m
             [1mapplying .l -> .c to "jive.l"[0m
        [1mSuff_FindDeps (jive.l)[0m
        [1mExamining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date[0m
        [1mExamining jive.c...non-existent...out-of-date[0m
        [1m--- jive.c ---[0m
        [1mlex jive.l[0m
        [1m... meaningless lex output deleted ...[0m
        [1mmv lex.yy.c jive.c[0m
        [1mExamining jive.o...non-existent...out-of-date[0m
        [1m--- jive.o ---[0m
        [1mcc -c jive.c[0m
        [1mExamining jive.out...non-existent...out-of-date[0m
        [1m--- jive.out ---[0m
        [1mcc -o jive.out jive.o[0m


   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  [1m.NULL[0m
   target mentioned later. In the system makefile, [1m.out [22mis cho-
   sen as the ``null suffix'' because most people use PMake  to
   create  programs.  You  are,  however,  free  and welcome to
   change it to a suffix of your own choosing.  The null suffix
   is  ignored,  however,  when  PMake is in compatibility mode
   (see chapter 4).

   [1m3.2.  Including Other Makefiles[0m

   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









                               -29-


        [1m#include "defs.h"[0m

   to  include the contents of [1mdefs.h [22min 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:

        [1m#include <file>[0m

   or this

        [1m#include "file"[0m

   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 [1m-h [22mflag).  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  [1m-I  [22mflags,  in
             the order in which you gave them.

        4)   Directories  given  by [1m.PATH [22mdependency lines (see
             chapter 4).

        5)   The system makefile directory.

   in that order.
   [4mNEW[24m [1mFixed: [4m[22mPMake[24m [4mwill[24m [4mnow[24m [4mrefuse[24m [4mto[24m [4minclude[24m [4mdirectory[24m [4mfiles.[0m
   [4mAlso,[24m [1m-dp [4m[22mallows[24m [4mtracing[24m [4mof[24m [4mfile[24m [4minclusions.[0m

   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 [4moutside[24m of a variable expansion. I.e. the fol-
   lowing

        [1mSYSTEM    = <command.mk>[0m

        [1m#include $(SYSTEM)[0m

   won't work.
   [4mNEW[0m

   [1mCompatibility:  [4m[22mTwo[24m  [4minclude[24m  [4mdirectives[24m [4mfrequently[24m [4mfound[24m [4min[0m
   [4mother[24m [4mMakes[24m [4mare[24m [4malso[24m [4msupported[24m [4m(note[24m [4mthe[24m [4mmissing[24m  [4m``#''[24m  [4mand[0m
   [4mquotes):[0m










                               -30-


        [1minclude file[0m

   for simple inclusions, and

        [1m-include file[0m

   for  inclusions  that  are ignored if the [1mfile [22mis not found.
   The effect of [1m-include [22mis better achieved using  the  native
   PMake construction

        [1m#if exists(file)[0m
        [1m#include "file"[0m
        [1m#endif[0m


   [1m3.3.  Saving Commands[0m

   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,  [1mranlib  [22mis  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 [1m__.SYMDEF [22minto 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 [1mranlib [22mcase above, you might do this:

        [1mlib1.a          : $(LIB1OBJS)[0m
                [1mrm -f $(.TARGET)[0m
                [1mar cr $(.TARGET) $(.ALLSRC)[0m
                [1m...[0m
                [1mranlib $(.TARGET)[0m

        [1mlib2.a          : $(LIB2OBJS)[0m
                [1mrm -f $(.TARGET)[0m
                [1mar cr $(.TARGET) $(.ALLSRC)[0m
                [1m...[0m
                [1mranlib $(.TARGET)[0m

   This would save both

        [1mranlib $(.TARGET)[0m

   commands  until  the  end, when they would run one after the
   other (using the correct value for the [1m.TARGET [22mvariable,  of
   course).









                               -31-


   [4mNEW[24m  [1mNote:  [4m[22mAn[24m  [4mimportant[24m  [4mfeature[24m [4m(some[24m [4mmight[24m [4msay[24m [4mbug)[24m [4mwith[0m
   [4mthis[24m [4mmechanism[24m [4mis[24m [4mthat[24m [4mthe[24m [4mdeferred[24m [4mcommands[24m [4mare[24m [4mrun[24m [4mat[24m [4mvery[0m
   [4mend[24m [4mof[24m [4mthe[24m [4mentire[24m [4mPMake[24m [4mprocess,[24m [4mnot[24m [4mnecessarily[24m [4mimmediately[0m
   [4mafter[24m [4mthe[24m [4mlast[24m [4mcreation[24m [4mcommand[24m [4mfor[24m [4mthe[24m [4mtarget[24m [4min[24m  [4mquestion.[0m
   [4mThis[24m  [4mmakes[24m  [4mit[24m [4mnecessary[24m [4mto[24m [4membed[24m [4msuch[24m [4mdeferred[24m [4mcommands[24m [4min[0m
   [4mrecursive[24m [4mmake[24m [4mruns[24m [4mif[24m [4mother[24m [4mtargets[24m [4mdepend[24m [4mon[24m [4mthe[24m [4mend[24m  [4mpro-[0m
   [4mcessing[24m  [4mhaving[24m  [4mtaken[24m [4mplace[24m [4m(or[24m [4muse[24m [4man[24m [4malternative[24m [4mformula-[0m
   [4mtion,[24m [4me.g.,[24m [4musing[24m [4mthe[24m [1m.JOIN [4m[22mattribute).[0m

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

   [1m3.4.  Target Attributes[0m

   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
                tries to create  the  target  so  marked.  This
                attribute  also  prevents PMake from attempting
                to touch the target if it is given the [1m-t [22mflag.
                [4mNEW[0m

   .OPTIONAL    [1mCompatibility:  [22mThis is a synonym for [1m.DONTCARE[0m
                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









                               -32-


                through  the  loading and dumping unless one of
                [4myour[24m files has  changed.  Your  makefile  might
                look  a little bit like this (remember, this is
                an educational example, and don't  worry  about
                the  [1mCOMPILE  [22mrule, all will soon become clear,
                grasshopper):

                     [1msystem          : init a.fasl b.fasl c.fasl[0m
                             [1mfor i in $(.ALLSRC);[0m
                             [1mdo[0m
                                     [1mecho -n '(load "' >> input[0m
                                     [1mecho -n ${i} >> input[0m
                                     [1mecho '")' >> input[0m
                             [1mdone[0m
                             [1mecho '(dump "$(.TARGET)")' >> input[0m
                             [1mlisp < input[0m

                     [1ma.fasl          : a.l init COMPILE[0m
                     [1mb.fasl          : b.l init COMPILE[0m
                     [1mc.fasl          : c.l init COMPILE[0m
                     [1mCOMPILE         : .USE[0m
                             [1mecho '(compile "$(.ALLSRC)")' >> input[0m
                     [1minit            : .EXEC[0m
                             [1mecho '(load-system)' > input[0m


                [1m.EXEC [22msources don't appear in the  local  vari-
                ables  of  targets that depend on them (nor are
                they touched if PMake is given  the  [1m-t  [22mflag).
                Note that all the rules, not just that for [1msys-[0m
                [1mtem[22m, include [1minit [22mas a source. This is  because
                none  of  the  other  targets can be made until
                [1minit [22mhas been made, thus they depend on it.

   .EXPORT      This is used to mark those targets  whose  cre-
                ation  should  be sent to another machine if at
                all possible. This may be used by some exporta-
                tion  schemes  if the exportation is expensive.
                You should ask your system administrator if  it
                is necessary.

   .EXPORTSAME  Tells  the export system that the job should be
                exported to a machine of the same  architecture
                as  the  current  one. Certain operations (e.g.
                running text through [1mnroff[22m)  can  be  performed
                the same on any architecture (CPU and operating
                system type), while others  (e.g.  compiling  a
                program with [1mcc[22m) must be performed on a machine
                with the same architecture. Not all export sys-
                tems will support this attribute.
                [4mNEW[0m

   .EXPORT=[4mattribute[0m
                Each  such dependency specifies that the target









                               -33-


                creation commands should only  be  exported  to
                machines  having  [4mattribute[24m among their charac-
                teristics.  Available attributes  are  entirely
                site-dependent, and their syntax depends on the
                underlying  exportation   system.    (see   the
                description  for  [1mexport(1)[22m).   This feature is
                typically  used  to  specify  minimum  resource
                requirements  for  memory, disk space, software
                licenses,  etc.   If  the  target  marked  with
                [1m.EXPORT= [22mattributes 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 [1m.IGNORE [22mattribute 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

                     [1mprog1           : $(PROG1OBJS) prog2 MAKEINSTALL[0m
                     [1mprog2           : $(PROG2OBJS) .INVISIBLE MAKEINSTALL[0m

                where  [1mMAKEINSTALL  [22mis  some  complex .USE rule
                (see below) that depends on the  [1m.ALLSRC  [22mvari-
                able  containing  the right things. Without the
                [1m.INVISIBLE [22mattribute for [1mprog2[22m, the [1mMAKEINSTALL[0m
                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 [1m.TARGET [22mvariable and all  the  local  vari-
                ables  of  any  target  that  depends on it, is
                replaced by the value of its [1m.ALLSRC  [22mvariable.
                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









                               -34-


                [1mranlib [22mthat 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:

                     [1mprogram         : $(OBJS) libraries[0m
                             [1mcc -o $(.TARGET) $(.ALLSRC)[0m

                     [1mlibraries       : lib1.a lib2.a lib3.a lib4.a .JOIN[0m
                             [1mranlib $(.OODATE)[0m

                In  this case, PMake will re-create the [1m$(OBJS)[0m
                as necessary, along with [1mlib1.a[22m, [1mlib2.a[22m, [1mlib3.a[0m
                and [1mlib4.a[22m.  It will then execute [1mranlib [22mon any
                library that  was  changed  and  set  [1mprogram[22m's
                [1m.ALLSRC  [22mvariable  to contain what's in [1m$(OBJS)[0m
                followed by ``[1mlib1.a  lib2.a  lib3.a  lib4.a[22m.''
                In  case  you're  wondering,  it's called [1m.JOIN[0m
                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 [1m-t [22mflag was given.

   .MAKE        The [1m.MAKE [22mattribute 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 [1m-n [22mor [1m-t [22mflag. By doing this, you can start
                at the top of a system and type

                     [1mpmake -n[0m

                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 [1m-n [22mflag.
                [4mNEW[24m [4mThis[24m [4mattribute[24m [4malso[24m [4maffects[24m [4mquery[24m [4mmode[24m [4m([24m[1m-q[4m[22m)[0m
                [4mprocessing.[24m   [4mTo[24m  [4mverify[24m [4mthe[24m [4muptodate-ness[24m [4mof[24m [4ma[0m
                [4mrecursive[24m [4mmake[24m [4mtarget,[24m [4mPMake[24m [4minvokes[24m  [4mthe[24m  [4mcom-[0m
                [4mmand[24m [4mand[24m [4mchecks[24m [4mfor[24m [4ma[24m [4mzero[24m [4mreturn[24m [4mstatus.[0m
                [1mCompatibility:  [4m[22mIn[24m [4mcompatibility[24m [4m([24m[1m-M[4m[22m)[24m [4mmode,[24m [4mall[0m
                [4mcommands[24m [4mthat[24m  [4mexpand[24m  [4mthe[24m  [1mMAKE  [4m[22mvariable[24m  [4mare[0m
                [4mautomatically[24m [4mconsidered[24m [4mrecursive[24m [4mmake[24m [4minvoca-[0m
                [4mtions.[0m

   .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  [1m.NOEXPORT [22mattribute, it
                will be run locally, even if you've given PMake









                               -35-


                the [1m-L 0 [22mflag.

   .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  [1m-p  [22mflag.   Giving a target this attribute
                tells PMake that the target is  definitely  [4mnot[0m
                the Main Target.  This allows you to place tar-
                gets in an included  makefile  and  have  PMake
                create something else by default.
                [4mNEW[24m  [1mCompatibility:  [4m[22mTargets[24m  [4mwhose[24m [4mnames[24m [4mstart[0m
                [4mwith[24m [4m``.''[24m [4mare[24m [4malso[24m [4mnot[24m [4mconsidered[24m [4mmain[24m [4mtargets[0m
                [4m(unless[24m  [4mthey[24m [4mlook[24m [4mlike[24m [4ma[24m [4mpath[24m [4mname,[24m [4mi.e.,[24m [4mcon-[0m
                [4mtain[24m [4ma[24m [4m``/'').[0m

   .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 [1m.PRECIOUS [22mattribute, how-
                ever, PMake will leave it alone. An  additional
                side effect of the `::' operator is to mark the
                targets as [1m.PRECIOUS[22m.
                [4mNEW[24m  [1mFixed:  [4m[22mActually,[24m  [4monly[24m  [4mfiles[24m  [4mthat[24m  [4mhave[0m
                [4mchanged[24m [4msince[24m [4mPMake[24m [4mstarted[24m [4mare[24m [4mremoved,[24m [4mother-[0m
                [4mwise[24m [4mwe[24m [4mmight[24m [4mdestroy[24m [4mpre-existing[24m [4mwork.[24m  [4mAlso,[0m
                [4mdirectories[24m   [4mare[24m  [4mspared,[24m  [4msince[24m  [4munlinking[24m  [4ma[0m
                [4mdirectory[24m [4mcan[24m [4mlead[24m [4mto[24m  [4minconsistent[24m  [4mfile[24m  [4msys-[0m
                [4mtems.[0m
                [4mNEW[0m

   .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  ([1m-B[22m)  mode only the failed
                command is restarted; in  Make  ([1m-M[22m)  mode  the
                attribute has no effect.
                If  an  exportation  system is available it may
                have its own criteria for restarting jobs inde-
                pendent  of [1m.RESTART[22m, 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









                               -36-


                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 [1m.USE[22m) of the source.  If
                the  target already has commands, the [1m.USE [22mtar-
                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 [1m.ALLSRC [22mvariable  for
                the  target) as its ``arguments,'' if you will.
                For example, you probably noticed that the com-
                mands  for  creating  [1mlib1.a  [22mand [1mlib2.a [22min the
                example in section 3.3 were exactly  the  same.
                You can use the [1m.USE [22mattribute to eliminate the
                repetition, like so:

                     [1mlib1.a          : $(LIB1OBJS) MAKELIB[0m
                     [1mlib2.a          : $(LIB2OBJS) MAKELIB[0m

                     [1mMAKELIB         : .USE[0m
                             [1mrm -f $(.TARGET)[0m
                             [1mar cr $(.TARGET) $(.ALLSRC)[0m
                             [1m...[0m
                             [1mranlib $(.TARGET)[0m


                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
                ([1mMAKELIB[22m)  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 [1mMAKELIB[0m
                rule. If there were  more  libraries,  I  could
                continue  with ``[1mlib3.a : $(LIB3OBJS) MAKELIB[22m''
                and so on and so forth.

   [1m3.5.  Special Targets[0m

   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









                               -37-


   chapter 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 [1m.DEFAULT [22mtarget is  used.
               The  [1m.IMPSRC  [22mvariable of a target that inherits
               [1m.DEFAULT[22m's commands is set to the  target's  own
               name.

   .END        This  serves  a  function  similar to [1m.BEGIN[22m, 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.
               [4mNEW[24m [4mSpecial[24m [4msource[24m [4mkeywords[24m  [4minclude:[24m  [1mSAME[4m[22m,[24m  [4mto[0m
               [4mmake[24m  [1m.EXPORTSAME  [4m[22mthe[24m [4mdefault;[24m [4mand[24m [1mUSELOCAL[4m[22m,[24m [4mto[0m
               [4malways[24m [4mcheck[24m [4mthe[24m  [4mlocal[24m  [4mhost[24m  [4mfor[24m  [4mavailability[0m
               [4mbefore[24m  [4mexporting[24m  [4ma[24m [4mcommand[24m [4m(similar[24m [4mto[24m [4ma[24m [4mnega-[0m
               [4mtive[24m [1m-L [4m[22moption[24m [4mvalue).[0m
               [4mOn[24m [4msystems[24m [4mthat[24m [4mallow[24m [4mthe[24m  [4mselection[24m  [4mof[24m  [4mremote[0m
               [4mhosts[24m  [4mby[24m  [4mattributes,[24m [4mother[24m [4msources[24m [4mfor[24m [1m.EXPORT[0m
               [4mare[24m [4minterpreted[24m [4mas[24m  [4mattributes[24m  [4mthat[24m  [4mshould[24m  [4mbe[0m
               [4mused[24m  [4mglobally[24m [4m(see[24m [4mthe[24m [4mdescription[24m [4mfor[24m [1m.EXPORT=[0m
               [4msources[24m [4mabove).[0m
               [4mSpecifying[24m [1m.EXPORT [4m[22mwithout[24m  [4msources[24m  [4mwill[24m  [4mreset[0m
               [4mthe[24m  [4mexportation[24m  [4msystems's[24m  [4mconfiguration[24m  [4mto[24m [4ma[0m
               [4mdefault[24m [4mstate[24m  [4mand[24m  [4mdiscard[24m  [4many[24m  [4mglobal[24m  [4mexport[0m
               [4mattributes.[24m   [4mThis[24m  [4mis[24m [4museful[24m [4mto[24m [4mundo[24m [4mthe[24m [4meffect[0m
               [4mof[24m [4mdirectives[24m [4mpresent[24m [4min[24m [4mthe[24m [4msystem[24m [4mmakefile.[0m

   .IGNORE     This target marks each of its sources  with  the
               [1m.IGNORE  [22mattribute.  If  you  don't  give it any
               sources, then it is like giving the [1m-i [22mflag when
               you  invoke  PMake -- errors are ignored for all
               commands.
               [4mNEW[24m [1mFixed: .IGNORE [4m[22mand[24m [1m-i [4m[22mnow[24m [4malso[24m  [4mignore[24m  [4mcom-[0m
               [4mmands[24m [4mthat[24m [4mfail[24m [4mas[24m [4ma[24m [4mresult[24m [4mof[24m [4mbeing[24m [4mkilled[24m [4mby[24m [4ma[0m
               [4msignal.[24m  [4mThis[24m [4mused[24m [4mto[24m [4mbe[24m [4mtrue[24m [4monly[24m  [4min[24m  [4mcompati-[0m
               [4mbility[24m [4mmode.[0m









                               -38-


   .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   [1m.SUFFIXES   [22m(see
               below).   Any  suffix  so  marked  will have the
               directories  on  its  search  path  (see  [1m.PATH[22m,
               below)  placed  in  the [1m.INCLUDES [22mvariable, each
               preceded by a [1m-I [22mflag. This variable can then be
               used as an argument for the compiler in the nor-
               mal fashion. The [1m.h [22msuffix is already marked  in
               this  way  in  the system makefile.  E.g. if you
               have

                    [1m.SUFFIXES       : .bitmap[0m
                    [1m.PATH.bitmap    : /usr/local/X/lib/bitmaps[0m
                    [1m.INCLUDES       : .bitmap[0m

               PMake will place  ``[1m-I/usr/local/X/lib/bitmaps[22m''
               in the [1m.INCLUDES [22mvariable and you can then say

                    [1mcc $(.INCLUDES) -c xprogram.c[0m

               (Note:  the  [1m.INCLUDES  [22mvariable 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.

   .LIBS       This  does for libraries what [1m.INCLUDES [22mdoes for
               include files, except the flag used  is  [1m-L[22m,  as
               required by those linkers that allow you to tell
               them where to find libraries. The variable  used
               is [1m.LIBS[22m.  Be forewarned that PMake may not have
               been compiled to do this if the linker  on  your
               system  doesn't  accept  the [1m-L [22mflag, though the
               [1m.LIBS [22mvariable 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 [1m-f [22mand [1m-r [22mflags 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









                               -39-


               designated. The last source  on  the  dependency
               line  is  the  suffix  that is used (you should,
               however, only give one suffix...).
               [4mNEW[24m [4mIf[24m [4mno[24m [4msources[24m [4mare[24m [4mgiven,[24m  [4mPMake[24m  [4mreverts[24m  [4mto[0m
               [4mthe[24m  [4mdefault[24m  [4mwhich[24m  [4mis[24m [4mto[24m [4mhave[24m [4mno[24m [4mimplicit[24m [4mnull[0m
               [4msuffix[24m [4mat[24m [4mall.[24m  [4mThis[24m [4mis[24m [4museful[24m [4mto[24m [4mundo[24m  [4mwhatever[0m
               [4mis[24m [4mdefined[24m [4min[24m [4mthe[24m [4msystem[24m [4mmakefile.[0m

   .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 [1m.ORDER [22mtargets 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.
               [4mNEW[24m  [4mThis[24m  [4mspecial[24m [4mtarget[24m [4mwas[24m [4mpreviously[24m [4mpresent[0m
               [4mbut[24m [4mundocumented.[0m

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

   .PATH[4msuffix[24m This  does a similar thing to [1m.PATH[22m, but it does
               it only for files with  the  given  suffix.  The
               suffix  must  have been defined already. Look at
               [1mSearch Paths [22m(section 4.1) for more information.

   .PRECIOUS   Similar  to  [1m.IGNORE[22m,  this  gives the [1m.PRECIOUS[0m
               attribute to each source on the dependency line,
               unless  there  are no sources, in which case the
               [1m.PRECIOUS [22mattribute is given to every target  in
               the file.

   .RECURSIVE  This  target  applies the [1m.MAKE [22mattribute 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 [1mA Shell is  a[0m
               [1mShell is a Shell [22m(section 4.4) for more informa-
               tion.
               [4mNEW[24m [1m.SHELL [4m[22mwith[24m [4mno[24m [4mdependencies[24m [4mreverts[24m  [4mto[24m  [4mthe[0m
               [4mconfigured[24m [4mdefault[24m [4m(usually[24m [4mthe[24m [4mBourne)[24m [4mshell.[0m









                               -40-


   .SILENT     When you use [1m.SILENT [22mas a target, it applies the
               [1m.SILENT [22mattribute to each  of  its  sources.  If
               there  are  no  sources  on the dependency line,
               then it is as if you gave PMake the [1m-s [22mflag  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 [1m.SUFFIXES [22mdependency
               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

        [4mattribute[24m [1m: [4m[22msources[0m

   applies  the [4mattribute[24m to all the targets listed as [4msources[24m.

   [1m3.6.  Modifying Variable Expansion[0m

   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:

        [1m${[4m[22mVARIABLE[24m[1m:[4m[22mmodifier[24m[1m}[0m

   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[4mpattern[0m
             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 [4mpattern[24m.   The  pattern
             is a wildcard pattern like that used by the shell,
             where [1m* [22mmeans 0 or more characters of any sort;  [1m?[0m
             is any single character; [1m[abcd] [22mmatches any single
             character that is either  `a',  `b',  `c'  or  `d'
             (there may be any number of characters between the
             brackets); [1m[0-9] [22mmatches 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









                               -41-


             characters  to  match  themselves  in a word.  For
             example, the system makefile [1m<makedepend.mk>  [22muses
             ``[1m$(CFLAGS:M-[ID]*)[22m'' to extract all the [1m-I [22mand [1m-D[0m
             flags that would be passed to the C compiler. This
             allows  it  to  properly  locate include files and
             generate the correct dependencies.

        N[4mpattern[0m
             This is identical to [1m:M [22mexcept it substitutes  all
             words that don't match the given pattern.

        S/[4msearch-string[24m/[4mreplacement-string[24m/[g]
             Causes  the  first  occurrence of [4msearch-string[24m in
             the variable to be replaced by [4mreplacement-string[24m,
             unless  the  [1mg  [22mflag 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 [4msearch-string[24m begins  with  a
             [1m^[22m, the string must match starting at the beginning
             of the word. If [4msearch-string[24m ends with a  [1m$[22m,  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  [4msearch-string[24m  and  the   [4mreplacement-string[24m,
             [1mexcept  [22mthat  a  backslash  is used to prevent the
             expansion of a [1m$[22m, not another dollar sign,  as  is
             usual.   Note that [4msearch-string[24m is just a string,
             not a pattern,  so  none  of  the  usual  regular-
             expression/wildcard  characters  have  any special
             meaning save [1m^ [22mand [1m$[22m.  In the replacement  string,
             the  [1m&  [22mcharacter is replaced by the [4msearch-string[0m
             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

                  [1mOBJS = ../lib/a.o b /usr/lib/libm.a[0m
                  [1mTAILS = $(OBJS:T)[0m

             the   variable  [1mTAILS  [22mwould  expand  to  ``[1ma.o  b[0m
             [1mlibm.a[22m.''

        H    This is similar to [1m:T[22m, except that every  word  is
             replaced   by   everything   but   the  tail  (the
             ``head''). Using the same definition of [1mOBJS[22m,  the
             string  ``[1m$(OBJS:H)[22m''  would  expand  to  ``[1m../lib[0m









                               -42-


             [1m/usr/lib[22m.''  Note that  the  final  slash  on  the
             heads  is  removed  and anything without a head is
             replaced by the empty string.

        E    [1m:E [22mreplaces each  word  by  its  suffix  (``exten-
             sion'').  So  ``[1m$(OBJS:E)[22m''  would  give  you ``[1m.o[0m
             [1m.a[22m.''

        R    This replaces each word by everything but the suf-
             fix  (the  ``root''  of  the word).  ``[1m$(OBJS:R)[22m''
             expands to `` [1m../lib/a b /usr/lib/libm[22m.''

   In addition, the System V style of substitution is also sup-
   ported.  This looks like:

        [1m$([4m[22mVARIABLE[24m[1m:[4m[22msearch-string[24m[1m=[4m[22mreplacement[24m[1m)[0m

   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.

   [1m3.7.  More on Debugging[0m

   [1m3.8.  More Exercises[0m

   (3.1)
        You've  got  a  set  programs, each of which is created
        from its  own  assembly-language  source  file  (suffix
        [1m.asm[22m).   Each  program  can  be assembled into two ver-
        sions, one with error-checking code  assembled  in  and
        one  without.  You  could assemble them into files with
        different suffixes ([1m.eobj [22mand [1m.obj[22m, for instance),  but
        your  linker  only  understands files that end in [1m.obj[22m.
        To top it all off, the final executables [4mmust[24m have  the
        suffix  [1m.exe[22m.   How  can  you  still use transformation
        rules to make your life easier (Hint: assume the error-
        checking versions have [1mec [22mtacked 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

             [1m$($(FOO))[0m

        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.
        [4mNEW[24m [1mCompatibility: [4m[22mAs[24m [4ma[24m [4mmatter[24m [4mof[24m [4mfact,[24m [4mPMake[24m [4mnow[24m  [4mdoes[0m
        [4mallow[24m  [4m`computed'[24m [4mvariable[24m [4mnames[24m [4mboth[24m [4min[24m [4mexpansions[24m [4mand[0m
        [4massignments,[24m [4mjust[24m [4mas[24m [4mother[24m [4mmodern[24m [4mMakes.[0m









                               -43-


   [1m4.  PMake for Gods[0m

   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.

   [1m4.1.  Search Paths[0m

   PMake supports the dispersal of files into multiple directo-
   ries  by  allowing you to specify places to look for sources
   with [1m.PATH [22mtargets in the makefile. The directories you give
   as sources for these targets make up a ``search path.''
   [4mOLD[24m  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.
   [4mNEW[24m [1mCompatibility: [4m[22mThis[24m [4mis[24m [4mno[24m [4mlonger[24m  [4mtrue.[24m   [4mBoth[24m  [4mto[24m  [4mmake[0m
   [4mPMake[24m  [4mmore[24m  [4mcompatible[24m  [4mand[24m [4mlife[24m [4measier[24m [4mwith[24m [4mthe[24m [4mMAKEOBJDIR[0m
   [4mfeature[24m [4mdescribed[24m [4mbelow,[24m [4mPMake[24m [4mwill[24m [4mtry[24m  [4mto[24m  [4mlocate[24m  [4mtargets[0m
   [4musing[24m [4msearch[24m [4mpaths[24m [4mas[24m [4mwell.[0m

   There  are  two  types of search paths in PMake: one is used
   for all types of files (including included makefiles) and is
   specified with a plain [1m.PATH [22mtarget (e.g.  ``[1m.PATH : RCS[22m''),
   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 [1m.PATH [22mwith the suffix
   of the file. For instance

        [1m.PATH.h         : /sprite/lib/include /sprite/att/lib/include[0m

   would    tell    PMake    to   look   in   the   directories
   [1m/sprite/lib/include  [22mand  [1m/sprite/att/lib/include  [22mfor   any
   files whose suffix is [1m.h[22m.

   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  ([1m.ALLSRC[22m,  and [1m.IMPSRC[22m) will instead contain









                               -44-


   the path to the file, as found by PMake.  Thus if you have a
   file [1m../lib/mumble.c [22mand a makefile

        [1m.PATH.c         : ../lib[0m
        [1mmumble          : mumble.c[0m
                [1m$(CC) -o $(.TARGET) $(.ALLSRC)[0m

   the  command executed to create [1mmumble [22mwould be ``[1mcc -o mum-[0m
   [1mble ../lib/mumble.c[22m.''  (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 [1m.out[0m
   is  the  null  suffix by default and a transformation exists
   from [1m.c [22mto [1m.out[22m.  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.
   [4mNEW[0m

   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
   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 [1m.out[22m.
   [4mNEW[0m

   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 [1mMAKEOBJDIR [22mis set and refers  to  a  direc-
   tory,  or  if  the  subdirectory  ``obj''  exists, the PMake
   process changes its working  directory  to  that  directory.
   The  PMake  variable  [1m.CURDIR [22mis set to the original working
   directory (``.'' if no MAKEOBJDIR was found).










                               -45-


   Unless specified otherwise, all commands  will  thus  create
   their  targets  in the object directory.  Since the value of
   [1m.CURDIR [22mis also added to the default file search path,  most
   target creation commands written in terms of variables [1m.ALL-[0m
   [1mSRC[22m,  [1m.IMPSRC[22m,  [1m.OODATE[22m,  etc.,  will  continue   to   work.
   Explicit path references may have to be modified to be rela-
   tive to [1m$(.CURDIR)[22m.  For example,  the  first  line  in  the
   example above would have to read thusly:

        [1m.PATH.c         : $(.CURDIR)/../lib[0m


   [1m4.2.  Archives and Libraries[0m

   UNIX  and  Sprite  allow  you to merge files into an archive
   using the [1mar [22mcommand. Further, if the files are  relocatable
   object  files,  you  can  run  [1mranlib [22mon 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  [1m-lm[0m
   rather  than  [1m/usr/lib/libm.a  [22mand 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
   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.
     [4mNEW[24m [4mActually,[24m [4mthis[24m [4mis[24m [4mtrue[24m [4monly[24m [4mif[24m [4mthe[24m [4mlibrary[24m [4mdepends[24m  [4mon[0m
     [4mone[24m  [4mof[24m [4mits[24m [4mmembers.[24m  [4mOtherwise[24m [4mwe[24m [4mcould[24m [4mnever[24m [4mcreate[24m [4mnon-[0m
     [4mobject[24m [4mfile[24m [4marchives[24m [4mthat[24m [4mare[24m [4mup-to-date.[0m

   A library is any target that looks like ``[1m-l[22mname''  or  that
   ends  in  a  suffix  that  was marked as a library using the
   [1m.LIBS [22mtarget.  [1m.a [22mis so marked in the system makefile.

   Members of an archive  are  specified  as  ``[4marchive[24m([4mmember[24m[
   [4mmember[24m...])''.   Thus  ``'[1mlibdix.a(window.o)[22m'' specifies the
   file [1mwindow.o [22min the archive [1mlibdix.a[22m.   You  may  also  use
   wildcards to specify the members of the archive. Just remem-
   ber that most the wildcard characters will only find  [4mexist-[0m
   [4ming[24m files.
   [4mNEW[0m

   [1mCompatibility:  [22mAdditionally,  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.,   ``[4marchive[24m(...    ([4msymbol[24m)    ...)''.     The









                               -46-


   corresponding  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 signaled.  Consider this clever way  to  extract  an
   object file from a library.

        [1mLIBC = /lib/libc.a[0m

        [1mmygetopt.o      : $(LIBC)((getopt))[0m
                [1mar x $(LIBC) $(.OODATE)[0m
                [1mmv $(.OODATE) $(.TARGET)[0m

   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 [4marchive[24m([4mmem-[0m
   [4mber[24m) target), special care is taken with the  transformation
   rules, as follows:

   +o [4marchive[24m([4mmember[24m) is made to depend on [4mmember[24m.

   +o The  transformation  from  the  [4mmember[24m's suffix to the [4mar-[0m
     [4mchive[24m's suffix is applied to the [4marchive[24m([4mmember[24m) target.

   +o The [4marchive[24m([4mmember[24m)'s [1m.TARGET [22mvariable is set to the  name
     of  the [4mmember[24m if [4mmember[24m is actually a target, or the path
     to the member file if [4mmember[24m is only a source.

   +o The [1m.ARCHIVE [22mvariable for the  [4marchive[24m([4mmember[24m)  target  is
     set to the name of the [4marchive[24m.

   +o The  [1m.MEMBER  [22mvariable  is set to the actual string inside
     the parentheses. In most cases, this will be the  same  as
     the [1m.TARGET [22mvariable.

   +o The  [4marchive[24m([4mmember[24m)'s place in the local variables of the
     targets that depend on it is taken by  the  value  of  its
     [1m.TARGET [22mvariable.

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

















                               -47-


        [1m.o.a            :[0m
                [1m...[0m
                [1mrm -f $(.TARGET:T)[0m
        [1mOBJS            = obj1.o obj2.o obj3.o[0m
        [1mlibprog.a       : libprog.a($(OBJS))[0m
                [1mar cru $(.TARGET) $(.OODATE)[0m
                [1mranlib $(.TARGET)[0m

   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 [1mlibprog.a[22m, a table
   of contents placed in the  archive  and  the  newly-archived
   object files to be removed.

   All this is used in the [1mmakelib.mk [22msystem makefile to create
   a single library with ease. This makefile looks like this:














































                               -48-


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

        [1m#ifndef _MAKELIB_MK[0m
        [1m_MAKELIB_MK    =[0m

        [1m#include  <po.mk>[0m

        [1m.po.a .o.a     :[0m
             [1m...[0m
             [1mrm -f $(.MEMBER)[0m

        [1mARFLAGS        ?= crl[0m

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

        [1m#endif _MAKELIB_MK[0m

   [4mNEW[0m

   [4mPMake[24m [4mknows[24m [4mhow[24m [4mto[24m [4mread[24m [4ma[24m [4mfair[24m [4mnumber[24m [4mof[24m  [4mdifferent[24m  [4marchive[0m
   [4mformats[24m  [4m(including[24m  [4mthe[24m  [4mranlib[24m  [4mformats[24m [4mused[24m [4mby[24m [4m4.4BSD[24m [4mand[0m
   [4mMIPS,[24m [4mas[24m [4mwell[24m [4mas[24m [4mSystem[24m [4mV[24m [4mcommon[24m [4marchives).[24m  [4mHowever,[24m [4mdue[24m [4mto[0m
   [4mdifferent[24m  [4mword[24m  [4msizes[24m  [4mand[24m  [4mbyte[24m [4morderings[24m [4mit[24m [4mis[24m [4mnot[24m [4malways[0m
   [4mpossible[24m [4mto[24m [4minterpret[24m [4mobject[24m [4mfile[24m  [4marchives[24m  [4mcreated[24m  [4mfor[24m  [4ma[0m
   [4mtarget[24m [4mplatform[24m [4mdifferent[24m [4mfrom[24m [4mthe[24m [4mhost[24m [4msystem.[0m

   [1m4.3.  On the Condition...[0m











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

        [1m#if [4m[22mboolean[24m [4mexpression[0m
        [4mlines[0m
        [1m#elif [4m[22manother[24m [4mboolean[24m [4mexpression[0m
        [4mmore[24m [4mlines[0m
        [1m#else[0m
        [4mstill[24m [4mmore[24m [4mlines[0m
        [1m#endif[0m

   They  may  be  nested to a maximum depth of 30 and may occur
   anywhere (except in a comment, of course).  The  ``[1m#[22m''  must
   the very first character on the line.

   Each  [4mboolean[24m  [4mexpression[24m is made up of terms that look like
   function calls, the standard C boolean operators [1m&&[22m, [1m||[22m, and
   [1m![22m,  and  the standard relational operators [1m==[22m, [1m!=[22m, [1m>[22m, [1m>=[22m, [1m<[22m,
   and [1m<=[22m, with [1m== [22mand [1m!= [22mbeing overloaded to allow string com-
   parisons  as well.  [1m&& [22mrepresents logical AND; [1m|| [22mis logical
   OR and [1m!  [22mis 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 [1mmake([4m[22mtarget[24m[1m) [22mwhere [4mtarget[24m 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 [1m.MAIN [22mtarget (note that the sources for [1m.MAIN[0m
            are only used if no targets were given on the  com-
            mand line).

   defined  The  syntax  is  [1mdefined([4m[22mvariable[24m[1m)  [22mand  is true if
            [4mvariable[24m is defined. Certain variables are  defined
            in  the system makefile that identify the system on
            which PMake is being run.

   exists   The syntax is [1mexists([4m[22mfile[24m[1m) [22mand is true if the  file
            can  be found on the global search path (i.e.  that
            defined by [1m.PATH [22mtargets, not by  [1m.PATH[4m[22msuffix[24m  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
            ``[1m!defined([4m[22mvar[24m[1m) || empty([4m[22mvar[24m[1m)[22m'' as  the  definition
            of [1m|| [22mwill prevent the [1mempty() [22mfrom 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:

                 [1m#if !empty([4m[22mvar[24m[1m:M[4m[22mword[24m[1m)[0m


   target   The syntax is [1mtarget([4m[22mname[24m[1m) [22mand is true if [4mname[24m 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.
            [4mNEW[24m [4mThis[24m [4mfunction[24m [4mwas[24m [4mpreviously[24m [4mpresent[24m [4mbut[24m [4mundoc-[0m
            [4mumented.[0m

   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.

        [1m#if $(OS) == 4.3[0m

        [1m#if $(MACHINE) == "sun3"[0m

        [1m#if $(LOAD_ADDR) < 0xc000[0m

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

        [1m#if $(LOAD)[0m

   would see if [1mLOAD [22mcontains a non-zero value and

        [1m#if !$(LOAD)[0m

   would test if [1mLOAD [22mcontains a zero value.

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

             [1mifdef     [22mdefined
             [1mifndef    [22m!defined
             [1mifmake    [22mmake
             [1mifnmake   [22m!make










                               -51-


   There  are also the ``else if'' forms: [1melif[22m, [1melifdef[22m, [1melifn-[0m
   [1mdef[22m, [1melifmake[22m, and [1melifnmake[22m.

   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 [1m-g [22mflag for the compilation, while the  other
   uses  the  [1m-O  [22mflag,  or you can use another target (call it
   [1mdebug[22m) to create the debug version. The construct below will
   take  care  of this for you. I have also made it so defining
   the variable [1mDEBUG [22m(say with [1mpmake -D DEBUG[22m) will also cause
   the debug version to be made.

        [1m#if defined(DEBUG) || make(debug)[0m
        [1mCFLAGS         += -g[0m
        [1m#else[0m
        [1mCFLAGS         += -O[0m
        [1m#endif[0m

   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

        [1m#if make(print)[0m
        [1mFORMATTER = ditroff -Plaser_printer[0m
        [1m#endif[0m
        [1m#if make(draft)[0m
        [1mFORMATTER = nroff -Pdot_matrix_printer[0m
        [1m#endif[0m

   would wreak havok if you tried ``[1mpmake draft  print[22m''  since
   you would use the same formatter for each target. As I said,
   this all gets somewhat complicated.
   [4mNEW[0m

   [4mEven[24m [4mmore[24m [4mso[24m [4mas[24m [4msome[24m [4mof[24m [4mthe[24m [4morginal[24m [4mvariants[24m  [4mof[24m  [1m#if  [4m[22mwhere[0m
   [4mhighly[24m  [4munintuitive,[24m  [4mas[24m [4mvariable[24m [4mexpansions[24m [4min[24m [4mconditionals[0m
   [4mwere[24m [4malways[24m [4minterpreted[24m [4mas[24m [4mnumeric[24m [4mcomparisons,[24m [4meven[24m [4min[24m  [4mthe[0m
   [4mabsence[24m [4mof[24m [4man[24m [4moperator.[24m  [4mThe[24m [4mfollowing[24m [4mnow[24m [4mwork[24m [4mas[24m [4mexpected:[0m

        [1m#ifmake $(VAR)[0m
        [1m#ifdef $(VAR)[0m

   expand the variable and apply the [1mmake() [22mor  [1mdefined()  [22mtest
   to the result, respectively.

        [1m#if arg[0m










                               -52-


   checks  non-zero-ness  if [1marg [22mis a number (either literal or
   the result of a variable expansion), and  otherwise  assumes
   [1marg [22mto be a variable name and checks for its existence.
   [4mNEW[0m

   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

        [1m#ifdef .PMAKE[0m
        [1mFOO = something_very_special[0m
        [1m#else[0m
        [1mFOO = standard_value[0m
        [1m#endif[0m

   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.

   [1m4.4.  A Shell is a Shell is a Shell[0m

   In normal operation,  the  Bourne  Shell  (better  known  as
   ``[1msh[22m'')  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 [1m.SHELL[0m
   target by keyword, as follows:

   [1mpath=[4m[22mpath[0m
        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).

   [1mname=[4m[22mname[0m
        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 [1mpath[22m), 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 (``[1m.SHELL: name=csh[22m''
        will do it).

   [1mquiet=[4m[22mecho-off[24m [4mcommand[0m
        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 [1mquiet [22mkeyword 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.

   [1mecho=[4m[22mecho-on[24m [4mcommand[0m
        The command PMake should give to turn echoing  back  on
        again.

   [1mfilter=[4m[22mprinted[24m [4mecho-off[24m [4mcommand[0m
        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.

   [1mechoFlag=[4m[22mflag[24m [4mto[24m [4mturn[24m [4mechoing[24m [4mon[0m
        Unless a target has been marked [1m.SILENT[22m, 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).

   [1merrFlag=[4m[22mflag[24m [4mto[24m [4mturn[24m [4merror[24m [4mchecking[24m [4mon[0m
        Likewise,  unless  a  target  is  marked [1m.IGNORE[22m, 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 [1mechoFlag[22m.

   [1mcheck=[4m[22mcommand[24m [4mto[24m [4mturn[24m [4merror[24m [4mchecking[24m [4mon[0m
        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.

   [1mignore=[4m[22mcommand[24m [4mto[24m [4mturn[24m [4merror[24m [4mchecking[24m [4moff[0m
        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.

   [1mhasErrCtl=[4m[22myes[24m [4mor[24m [4mno[0m
        This takes a value that is either [1myes [22mor [1mno[22m.   Now  you
        might  think that the existence of the [1mcheck [22mand [1mignore[0m
        keywords would be enough to tell PMake if the shell can
        do  error-control,  but you'd be wrong. If [1mhasErrCtl [22mis
        [1myes[22m, PMake uses the check  and  ignore  commands  in  a
        straight-forward manner.  If this is [1mno[22m, however, their









                               -54-


        use is rather different. In this case, the  check  com-
        mand  is  used as a template, in which the string [1m%s [22mis
        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 [1m%s [22mreplaced 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) (``[1m\n[22m'') 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 [1m<shx.mk>  [22msystem  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:

        [1m#[0m
        [1m# This is a shell specification to have the bourne shell echo[0m
        [1m# the commands just before executing them, rather than when it reads[0m
        [1m# them. Useful if you want to see how variables are being expanded, etc.[0m
        [1m#[0m
        [1m.SHELL    : path=/bin/sh \[0m
             [1mquiet="set -" \[0m
             [1mecho="set -x" \[0m
             [1mfilter="+ set - " \[0m
             [1mechoFlag=x \[0m
             [1merrFlag=e \[0m
             [1mhasErrCtl=yes \[0m
             [1mcheck="set -e" \[0m
             [1mignore="set +e"[0m


   It tells PMake the following:

   +o The shell is located in the file  [1m/bin/sh[22m.   It  need  not
     tell  PMake  that the name of the shell is [1msh [22mas PMake can
     figure that out for itself (it's the last component of the
     path).

   +o The command to stop echoing is [1mset -[22m.

   +o The command to start echoing is [1mset -x[22m.











                               -55-


   +o When  the  echo  off  command  is executed, the shell will
     print [1m+ set - [22m(The  `+'  comes  from  using  the  [1m-x  [22mflag
     (rather  than the [1m-v [22mflag 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 [1m-x [22mflag. The Bourne Shell will  only  take
     its  flag arguments concatenated as its first argument, so
     neither this nor the [1merrFlag [22mspecification begins  with  a
     -.

   +o The  flag  to use to turn error-checking on from the start
     is [1m-e[22m.

   +o The shell can turn error-checking on and off, and the com-
     mands to do so are [1mset +e [22mand [1mset -e[22m, 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:

             [1mhasErrCtl=no \[0m
             [1mcheck="echo \"+ %s\"\n" \[0m
             [1mignore="sh -c '%s || exit 0\n"[0m


   This will cause PMake to execute the two commands

        [1mecho "+ [4m[22mcmd[24m[1m"[0m
        [1msh -c '[4m[22mcmd[24m [1m|| true'[0m

   for each command for which errors are  to  be  ignored.  (In
   case you are wondering, the thing for [1mignore [22mtells the shell
   to execute another  shell  without  error  checking  on  and
   always exit 0, since the [1m|| [22mcauses the [1mexit 0 [22mto 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).
   [4mNEW[0m

   [1mCompatibility: [4m[22mPMake[24m [4mnow[24m [4malso[24m [4mrecognizes[24m [4mthe[24m [1mSHELL  [4m[22mvariable[0m
   [4mfound[24m  [4min[24m  [4mmost[24m [4mother[24m [4mMakes.[24m  [4mAs[24m [4man[24m [4mexception[24m [4mto[24m [4mthe[24m [4mgeneral[0m
   [4mrule,[24m [4mthis[24m [4mvariable[24m [4mis[24m [4mnot[24m [4mrelated[24m [4mto[24m [4mthe[24m [4menvironment[24m  [4mvari-[0m
   [4mable[24m  [4mholding[24m  [4mthe[24m  [4muser's[24m [4minteractive[24m [4mshell.[24m  [4mInstead[24m [4mit[24m [4mis[0m
   [4mset[24m [4mfrom,[24m [4mand[24m [4mmay[24m [4mbe[24m [4mused[24m [4mto[24m [4mset,[24m [4mthe[24m [4mcurrent[24m [1m.SHELL  [4m[22mspeci-[0m
   [4mfication.[24m  [4mThus,[0m

        [1mSHELL = /bin/csh[0m

   is equivalent to











                               -56-


        [1m.SHELL: path=/bin/csh[0m

   [4mAlso[24m  [1m#undef  SHELL  [4m[22mresets[24m  [4mthe[24m  [4mshell[24m  [4mto[24m  [4mthe[24m  [4mconfigured[0m
   [4mdefault.[0m

   [1m4.5.  Compatibility[0m

   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.  [1m-B [22m(one shell per  command)  implies  [1m-V[22m)  The
   three  levels are described in the following three sections.

   [1m4.5.1.  DEFCON 3 -- Variable Expansion[0m

   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 (``[1m${PRINTER}[22m''),  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.
   [4mOLD[0m

   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 [1m-V [22mflag (this  can  be
   done with the [1m.MAKEFLAGS [22mtarget, if you want).
   [4mNEW[0m

   [4mThis[24m  [4mis[24m [4mthe[24m [4mdefault[24m [4mconfiguration[24m [4mnow.[24m  [4mUse[24m [1m-C [4m[22mto[24m [4mchange[24m [4mit[0m
   [4mback.[0m

   [1m4.5.2.  DEFCON 2 -- The Number of the Beast[0m

   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 [1m-B [22mflag 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-


        [1minstall :: .MAKE[0m
             [1m(cd src; $(.PMAKE) install)[0m
             [1m(cd lib; $(.PMAKE) install)[0m
             [1m(cd man; $(.PMAKE) install)[0m

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

   [1m4.5.3.   DEFCON  1  --  Imitation is Not the Highest Form of[0m
   [1mFlattery[0m

   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 [1m-M [22mflag (for  Make),  or  by  executing  PMake  as
   ``[1mmake[22m.''   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 [1m$$ [22mvariable 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: [1m#[22m,  [1m=[22m,
     [1m|[22m,  [1m^[22m, [1m([22m, [1m)[22m, [1m{[22m, [1m}[22m, [1m;[22m, [1m&[22m, [1m<[22m, [1m>[22m, [1m*[22m, [1m?[22m, [1m[[22m, [1m][22m, [1m:[22m, [1m$[22m, [1m`[22m, and [1m\[22m.
     You should notice that these are all the  characters  that
     are  given  special  meaning by the shell (except [1m' [22mand  [1m,[0m
     which PMake deals with all by its lonesome).

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

   [1m4.6.  The Way Things Work[0m

   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 [1m.MAIN [22mtarget, or  by  the  target  being  the
   first  in  the file not labeled with the [1m.NOTMAIN [22mattribute)
   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 [1mSuff_FindDeps [22m(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 [1m.USE [22mrule, 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  [1m.USE [22mnode has been processed). You
   will note that this allows a [1m.USE [22mnode to have children that
   are  [1m.USE  [22mnodes  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
   process 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 [1m.END [22mtarget, 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.

   [1m5.  Answers to Exercises[0m











                               -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
        ([1m.ms[22m,  [1m.c[22m, 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:

             [1m.SUFFIXES       : ec.exe .exe ec.obj .obj .asm[0m
             [1mec.objec.exe .obj.exe :[0m
                     [1mlink -o $(.TARGET) $(.IMPSRC)[0m
             [1m.asmec.obj      :[0m
                     [1masm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC)[0m
             [1m.asm.obj        :[0m
                     [1masm -o $(.TARGET) $(.IMPSRC)[0m


   (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

             [1m$(PTR:S/^/\$(/:S/$/))[0m

        which  places  ``[1m$([22m'' at the front of the variable name
        and ``[1m)[22m'' at the end, thus  transforming  ``[1mVAR[22m,''  for
        example,  into  ``[1m$(VAR)[22m,'' 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  [4mall[0m
        you get. The solution is to make use of ``:='' to place
        that string into yet another variable, then invoke  the
        other variable directly:

             [1m*PTR            := $(PTR:S/^/\$(/:S/$/)/)[0m

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

   [1m6.  Glossary of Jargon[0m

   [1mattribute: [22mA property given to a target that causes PMake to
        treat it differently.

   [1mcommand script: [22mThe 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-


   [1mcommand-line  variable:  [22mA  variable  defined in an argument
        when PMake is first executed.   Overrides  all  assign-
        ments to the same variable name in the makefile.

   [1mconditional:  [22mA  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.

   [1mcreation script: [22mCommands  used  to  create  a  target.  See
        ``command script.''

   [1mdependency:  [22mThe 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.

   [1mdynamic  source:  [22mThis  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.

   [1mglobal variable: [22mAny variable defined in a  makefile.  Takes
        precedence  over  variables defined in the environment,
        but not over command-line or local variables.

   [1minput graph: [22mWhat 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.

   [1mlocal variable: [22mA 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:
        [1m.TARGET[22m,  [1m.ALLSRC[22m, [1m.OODATE[22m, [1m.PREFIX[22m, [1m.IMPSRC[22m, [1m.ARCHIVE[22m,
        and [1m.MEMBER[22m.  [1m.TARGET[22m, [1m.PREFIX[22m, [1m.ARCHIVE[22m,  and  [1m.MEMBER[0m
        may  be  used  on  dependency lines to create ``dynamic
        sources.''

   [1mmakefile: [22mA file that describes how a system  is  built.  If
        you  don't  know  what  it  is after reading this tuto-
        rial....

   [1mmodifier: [22mA letter, following a colon, used to alter  how  a
        variable is expanded.  It has no effect on the variable
        itself.











                               -61-


   [1moperator: [22mWhat separates a source from a target (on a depen-
        dency  line) and specifies the relationship between the
        two. There are three: `[1m:[22m', `[1m::[22m', and `[1m![22m'.

   [1msearch path: [22mA 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.

   [1mshell: [22mA program to which commands are passed  in  order  to
        create targets.

   [1msource: [22mAnything to the right of an operator on a dependency
        line. Targets on the dependency line are  usually  cre-
        ated from the sources.

   [1mspecial  target:  [22mA  target  that causes PMake to do special
        things when it's encountered.

   [1msuffix: [22mThe tail end of a file name. Usually begins  with  a
        period, [1m.c [22mor [1m.ms[22m, e.g.

   [1mtarget:  [22mA  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).

   [1mtransformation rule: [22mA 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.

   [1mvariable expansion: [22mThe process of substituting the value of
        a  variable  for  a  reference  to it. Expansion may be
        altered by means of modifiers.

   [1mvariable: [22mA 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-


                           [1mTable of Contents[0m


        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  . . . . . . . . . . . . . . . . . .  23
        3. Short-cuts and Other Nice Things . . . . . . . .  24
        3.1. Transformation Rules . . . . . . . . . . . . .  24
        3.2. Including Other Makefiles  . . . . . . . . . .  28
        3.3. Saving Commands  . . . . . . . . . . . . . . .  30
        3.4. Target Attributes  . . . . . . . . . . . . . .  31
        3.5. Special Targets  . . . . . . . . . . . . . . .  36
        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



















