





      


                          PPMMaakkee ---- AA TTuuttoorriiaall


                              _A_d_a_m _d_e _B_o_o_r
                           Berkeley Softworks
                      2150 Shattuck Ave, Penthouse
                           Berkeley, CA 94704
                            adam@bsw.uu.net
                           ...!uunet!bsw!adam



      11..  IInnttrroodduuccttiioonn
      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:
 NOTE
      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 has defi-
      nitions for the jargon I use and chapter 6 contains possible
      solutions to the problems presented throughout the tutorial.
      -----------
      Permission  to  use,  copy, modify, and distribute
      this software and its documentation for  any  pur-
      pose  and  without fee is hereby granted, provided
      that the above copyright  notice  appears  in  all
      copies.   The  University  of California, Berkeley
      Softworks, and Adam de Boor  make  no  representa-
      tions  about  the suitability of this software for
      any purpose.   It  is  provided  "as  is"  without
      express or implied warranty.









      PSD:12-2                                 PMake -- A Tutorial


      22..  TThhee BBaassiiccss ooff PPMMaakkee
      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 Makefile and makefile
      (in that order) in the current directory if you  don't  tell
      it  otherwise.   To specify a different makefile, use the --ff
      flag (e.g.  ``pmake -f program.mk'').
      A makefile has four different types of lines in it:
           +o File dependency specifications
           +o Creation commands
           +o Variable assignments
           +o Comments, include statements and  conditional  direc-
             tives
      Any  line  may be continued over multiple lines by ending it
      with a backslash.  The backslash, following newline and  any
      initial whitespace on the following line are compressed into
      a single space before the input line is examined by PMake.

      22..11..  DDeeppeennddeennccyy LLiinneess
      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:









      PMake -- A Tutorial                                 PSD:12-3


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

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

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









      PSD:12-4                                 PMake -- A Tutorial


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

      22..22..  SShheellll CCoommmmaannddss
      ``Isn't that nice,'' you say  to  yourself,  ``but  how  are
      files actually `re-created,' as he likes to spell it?''  The
      re-creation is accomplished by commands  you  place  in  the
      makefile.   These  commands  are  passed to the Bourne shell
      (better  known  as  ``/bin/sh'')  to  be  executed  and  are
      expected  to  do  what's necessary to update the target file
      (PMake doesn't actually check to see if the target was  cre-
      ated. It just assumes it's there).
      Shell  commands in a makefile look a lot like shell commands
      you would type at a terminal, with one important  exception:
      each  command in a makefile _m_u_s_t be preceded by at least one
      tab.
      Each target has associated with it a shell script made up of
      one or more of these shell commands. The creation script for
      a target should immediately follow the dependency  line  for
      that  target. While any given target may appear on more than
      one dependency line, only one of these dependency lines  may
      be  followed  by a creation script, unless the `::' operator
      was used on the dependency line.
 NOTE
      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:









      PMake -- A Tutorial                                 PSD:12-5


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

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

           echo Linking index
           Linking index

      so  PMake  allows  you  to  place  an `@' before the command
      (``@echo Linking index'') to prevent the command from  being
      printed.
      The  other  special character is the `-'. In case you didn't
      know, shell commands finish with a certain ``exit  status.''
      This  status  is  made  available by the operating system to
      whatever program invoked the command. Normally  this  status
      will  be  0  if everything went ok and non-zero if something
      went wrong. For this reason, 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  (``-mv  index  index.old''),  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.









      PSD:12-6                                 PMake -- A Tutorial


 NOTE
      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  for
      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 --BB flag
      (it stands for backwards-compatible) that forces  each  com-
      mand  to  be given to a separate shell. It also does several
      other things, all of which I discourage since they  are  now
      old-fashioned....
 NOTE
      A  target's  shell  script  is  fed to the shell on its (the
      shell's) input stream.  This means that any  commands,  such
      as  ci  that  need to get input from the terminal won't work
      right -- they'll get the shell's input, something they prob-
      ably won't find to their liking. A simple way around this is
      to give a command like this:

           ci $(SRCS) < /dev/tty

      This would force the program's input to come from the termi-
      nal.  If  you can't do this for some reason, your only other
      alternative is to use PMake  in  its  fullest  compatibility
      mode. See CCoommppaattiibbiilliittyy in chapter 4.

      22..33..  VVaarriiaabblleess
      PMake,  like Make before it, has the ability to save text in
      variables to be recalled later at  your  convenience.  Vari-
      ables  in  PMake  are  used much like variables in the shell
      and, by tradition, consist of all  upper-case  letters  (you
      don't  _h_a_v_e  to use all upper-case letters.  In fact there's
      nothing to stop you from calling a  variable  @^&$%$.   Just
      tradition).  Variables  are  assigned-to  using lines of the
      form

           VARIABLE = value

      appended-to by

           VARIABLE += value

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

           VARIABLE ?= value

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











      PMake -- A Tutorial                                 PSD:12-7


           VARIABLE := value

      Any whitespace before _v_a_l_u_e is stripped off. When appending,
      a  space is placed between the old value and the stuff being
      appended.
      The final way a variable may be assigned to is using

           VARIABLE != shell-command

      In this case, _s_h_e_l_l_-_c_o_m_m_a_n_d has all its  variables  expanded
      (see  below)  and  is  passed off to a shell to execute. The
      output of the shell is then placed in the variable. Any new-
      lines  (other  than  the  final  one) are replaced by spaces
      before the assignment is made. This  is  typically  used  to
      find the current directory via a line like:

           CWD             != pwd

      NNoottee::  this  is intended to be used to execute commands that
      produce small amounts of output (e.g. ``pwd'').  The  imple-
      mentation is less than intelligent and will likely freeze if
      you execute something that produces thousands  of  bytes  of
      output (8 Kb is the limit on many UNIX systems).
      The  value  of  a variable may be retrieved by enclosing the
      variable name in parentheses or curly braces  and  preceding
      the whole thing with a dollar sign.
      For  example,  to  set  the  variable  CFLAGS  to the string
      ``-I/sprite/src/lib/libc -O,'' you would place a line

           CFLAGS = -I/sprite/src/lib/libc -O

      in the makefile and use  the  word  $(CFLAGS)  wherever  you
      would  like  the string -I/sprite/src/lib/libc -O to appear.
      This is called variable expansion.
 NOTE
      Unlike Make, PMake will not  expand  a  variable  unless  it
      knows  the  variable  exists.  E.g.  if you have a ${i} in a
      shell command and you have not assigned a value to the vari-
      able i (the empty string is considered a value, by the way),
      where Make would have substituted the  empty  string,  PMake
      will  leave the ${i} alone.  To keep PMake from substituting
      for a variable  it  knows,  precede  the  dollar  sign  with
      another  dollar  sign.   (e.g. to pass ${HOME} to the shell,
      use $${HOME}).  This causes PMake, in effect, to expand  the
      $  macro,  which  expands to a single $.  For compatibility,
      Make's style of variable  expansion  will  be  used  if  you
      invoke  PMake with any of the compatibility flags (--VV, --BB or
      --MM.  The --VV flag alters just the variable expansion).
      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









      PSD:12-8                                 PMake -- A Tutorial


      are expanded whenever the outer variable  is  expanded  (the
      expansion  of  an  inner variable has no effect on the outer
      variable. I.e. if the outer variable is used on a dependency
      line  and in a shell command, and the inner variable changes
      value between when the dependency line is read and the shell
      command  is  executed,  two different values will be substi-
      tuted for the outer variable).
      Variables come in four flavors, though they are all expanded
      the  same and all look about the same. They are (in order of
      expanding scope):
           +o Local variables.
           +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.

      22..33..11..  LLooccaall VVaarriiaabblleess
      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
                The target without  its  suffix  and  without  any
                leading  path.  E.g. for the target ../../lib/com-
                pat/fsRead.c, this variable would contain  fsRead.
      Three other local variables are set only for certain targets
      under special  circumstances.  These  are  the  ``.IMPSRC,''
      ``.ARCHIVE,''  and  ``.MEMBER'' variables. When they are set
      and how they are used is described later.
      Four of these variables may be used in sources as well as in
      shell  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,

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

      will  create  a  dependency between each object file and its









      PMake -- A Tutorial                                 PSD:12-9


      corresponding C source file.

      22..33..22..  CCoommmmaanndd--lliinnee VVaarriiaabblleess
      Command-line variables are set when PMake is  first  invoked
      by giving a variable assignment as one of the arguments. For
      example,

           pmake "CFLAGS = -I/sprite/src/lib/libc -O"

      would make CFLAGS be a command-line variable with the  given
      value.  Any  assignments to CFLAGS in the makefile will have
      no effect, because once it is set, there is (almost) nothing
      you  can  do  to  change a command-line variable (the search
      order, you see). Command-line variables may be set using any
      of  the  four  assignment  operators,  though  only = and ?=
      behave 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.  += is the same as =, because the
      old value of the variable is sought only  in  the  scope  in
      which  the  assignment is taking place (for reasons of effi-
      ciency that I won't get into here).  := and ?= will work  if
      the  only variables used are in the environment.  != is sort
      of pointless to use from the command line,  since  the  same
      effect  can  no  doubt be accomplished using the shell's own
      command substitution mechanisms (backquotes and all that).

      22..33..33..  GGlloobbaall VVaarriiaabblleess
      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  --ff
                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:

           #undef _v_a_r_i_a_b_l_e

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









      PSD:12-10                                PMake -- A Tutorial


      22..33..44..  EEnnvviirroonnmmeenntt VVaarriiaabblleess
      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,  PMAKE,  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 PMAKE variable or on  the  com-
      mand  line,  are  placed  in  this  environment variable and
      exported to each shell PMake executes. Thus recursive  invo-
      cations of PMake automatically receive the same flags as the
      top-most one.
      Using all these variables, you can compress the sample make-
      file even more:

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


      22..44..  CCoommmmeennttss
      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).

      22..55..  PPaarraalllleelliissmm
 NNOOTTEE
      PMake was specifically designed to re-create several targets
      at  once, when possible. You do not have to do anything spe-
      cial to cause this to happen (unless PMake was configured to
      not act in parallel, in which case you will have to make use
      of the --LL and --JJ flags (see below)), but you do have  to  be
      careful at times.
      There  are several problems you are likely to encounter. One
      is that some makefiles (and programs) are written in such  a
      way  that  it  is  impossible  for two targets to be made at
      once. The program xstr, for  example,  always  modifies  the
      files  strings  and x.c.  There is no way to change it. Thus
      you cannot run two of them at once without  something  being
      trashed.  Similarly,  if  you  have commands in the makefile









      PMake -- A Tutorial                                PSD:12-11


      that always send output to the same file, you  will  not  be
      able  to make more than one target at once unless you change
      the file you use. You can, for instance, add a $$$$  to  the
      end  of the file name to tack on the process ID of the shell
      executing the command (each $$ expands to a single  $,  thus
      giving  you the shell variable $$).  Since only one shell is
      used for all the commands, you'll get the same file name for
      each command in the script.
      The  other problem comes from improperly-specified dependen-
      cies that worked in Make because of its  sequential,  depth-
      first  way  of examining them. While I don't want to go into
      depth on how PMake works (look in chapter 4 if you're inter-
      ested),  I  will warn you that files in two different ``lev-
      els'' of the dependency tree may be examined in a  different
      order  in  PMake  than they were in Make. For example, given
      the makefile

           a               : b c
           b               : d

      PMake will examine the targets in the order c, d, b, a.   If
      the  makefile's author expected PMake to abort before making
      c if an error occurred while making b, or  if  b  needed  to
      exist  before  c was made, s/he will be sorely disappointed.
      The dependencies are incomplete, since in both these  cases,
      c would depend on b.  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  /dev/tty  I mentioned earlier, you must be careful not
      to run two of the jobs at once.

      22..66..  WWrriittiinngg aanndd DDeebbuuggggiinngg aa MMaakkeeffiillee
      Now you know most of what's in a makefile, what  do  you  do
      next?  There are two choices: (1) use one of the uncommonly-
      available makefile generators or (2) write your own makefile
      (I  leave  out  the third choice of ignoring PMake and doing
      everything by hand as being  beyond  the  bounds  of  common
      sense).
      When  faced  with  the  writing of a makefile, it is usually
      best to start from first principles: just what _a_r_e you  try-
      ing to do? What do you want the makefile finally to produce?
      To begin with a somewhat traditional example, let's say  you
      need  to  write  a  makefile to create a program, expr, that
      takes standard infix expressions and converts them to prefix
      form  (for  no  readily  apparent  reason). You've got three
      source files, in  C,  that  make  up  the  program:  main.c,
      parse.c,  and  output.c.   Harking  back  to my pithy advice
      about dependency lines, you write  the  first  line  of  the
      file:

           expr            : main.o parse.o output.o










      PSD:12-12                                PMake -- A Tutorial


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

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

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

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

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

                cc -c $(.ALLSRC)

      you'd get (for main.o):

                cc -c main.c defs.h

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

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

      The  makefile  is now complete and will, in fact, create the
      program you want it to without unnecessary  compilations  or
      excessive  typing  on  your part. There are two things wrong
      with it, however (aside from it being altogether  too  long,
      something I'll address in chapter 3):
      1)   The  string  ``main.o  parse.o  output.o''  is repeated
           twice, necessitating two changes when you  add  postfix
           (you  were  planning on that, weren't you?). This is in
           direct violation of de Boor's  First  Rule  of  writing
           makefiles:
           _A_n_y_t_h_i_n_g  _t_h_a_t  _n_e_e_d_s _t_o _b_e _w_r_i_t_t_e_n _m_o_r_e _t_h_a_n _o_n_c_e
           _s_h_o_u_l_d _b_e _p_l_a_c_e_d _i_n _a _v_a_r_i_a_b_l_e_.
           I cannot emphasize this enough as being very  important
           to the maintenance of a makefile and its program.









      PMake -- A Tutorial                                PSD:12-13


      2)   There  is no way to alter the way compilations are per-
           formed short of editing the  makefile  and  making  the
           change  in  all  places.  This  is evil and violates de
           Boor's Second Rule, which  follows  directly  from  the
           first:
           _A_n_y  _f_l_a_g_s  _o_r  _p_r_o_g_r_a_m_s  _u_s_e_d  _i_n_s_i_d_e  _a _m_a_k_e_f_i_l_e
           _s_h_o_u_l_d _b_e _p_l_a_c_e_d _i_n _a  _v_a_r_i_a_b_l_e  _s_o  _t_h_e_y  _m_a_y  _b_e
           _c_h_a_n_g_e_d_,  _t_e_m_p_o_r_a_r_i_l_y  _o_r  _p_e_r_m_a_n_e_n_t_l_y_,  _w_i_t_h  _t_h_e
           _g_r_e_a_t_e_s_t _e_a_s_e_.
      The makefile should more properly read:

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

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

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

      These  two rules and examples lead to de Boor's First Corol-
      lary:
           _V_a_r_i_a_b_l_e_s _a_r_e _y_o_u_r _f_r_i_e_n_d_s_.
      Once you've written the makefile comes the  sometimes-diffi-
      cult  task  of  making  sure the darn thing works. Your most
      helpful tool to make sure the makefile is at least syntacti-
      cally  correct  is  the  --nn flag, which allows you to see if
      PMake will choke on the makefile. The second  thing  the  --nn
      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: ``-d m'' and ``-p  2.''   The  first  causes
      PMake to tell you as it examines each target in the makefile
      and indicate why it is deciding whatever it is deciding. You
      can  then  use  the information printed for other targets to
      see where you went wrong. The  ``-p  2''  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 ``-p 2'' is intended to  resemble  closely  a  real









      PSD:12-14                                PMake -- A Tutorial


      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.

           a         : b
           b         : c d
           d         : a

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

      22..77..  IInnvvookkiinngg PPMMaakkee
      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:
      --dd _w_h_a_t
           This causes PMake to  spew  out  debugging  information
           that  may  prove useful to you. If you can't figure out
           why PMake is doing what it's doing, you might try using
           this  flag.  The  _w_h_a_t  parameter is a string of single
           characters that tell PMake what aspects you are  inter-
           ested  in.  Most  of  what  I describe will make little
           sense to you, unless you've  dealt  with  Make  before.
           Just  remember  where this table is and come back to it
           as you read on.  The  characters  and  the  information
           they produce are as follows:
           a    Archive searching and caching.
           c    Conditional evaluation.
           d    The searching and caching of directories.
           j    Various  snippets  of  information  related to the
                running of the multiple shells.  Not  particularly
                interesting.
           m    The  making  of  each target: what target is being
                examined; when it was last modified; whether it is
                out-of-date; etc.
           p    Makefile parsing.
           r    Remote execution.
           s    The  application  of  suffix-transformation rules.
                (See chapter 3)
           t    The maintenance of the list of targets.
           v    Variable assignment.
           Of these all, the m and s letters will be  most  useful
           to  you.   If  the  --dd  is  the  final  argument or the









      PMake -- A Tutorial                                PSD:12-15


           argument from which it would get these key letters (see
           below  for  a  note about which argument would be used)
           begins with a --, all of these debugging flags  will  be
           set, resulting in massive amounts of output.
      --ff _m_a_k_e_f_i_l_e
           Specify  a makefile to read different from the standard
           makefiles  (Makefile  or  makefile).   If  _m_a_k_e_f_i_l_e  is
           ``-'',  PMake  uses  the standard input. This is useful
           for making quick and dirty makefiles...
      --hh   Prints  out  a  summary  of  the  various  flags  PMake
           accepts.  It can also be used to find out what level of
           concurrency was compiled into the version of PMake  you
           are  using (look at --JJ and --LL) and various other infor-
           mation on how PMake was configured.
      --ii   If you give this flag, PMake will ignore non-zero  sta-
           tus  returned by any of its shells. It's like placing a
           `-' before all the commands in the makefile.
      --kk   This is similar to --ii in that it allows PMake  to  con-
           tinue when it sees an error, but unlike --ii, where PMake
           continues blithely as if nothing went wrong, --kk  causes
           it  to  recognize  the  error and only continue work on
           those things that don't depend on  the  target,  either
           directly  or indirectly (through depending on something
           that depends on it), whose creation returned the error.
           The `k' is for ``keep going''...
      --ll   PMake has the ability to lock a directory against other
           people executing it in the same directory (by means  of
           a  file called ``LOCK.make'' that it creates and checks
           for in the directory). This is a Good Thing because two
           people  doing  the  same thing in the same place can be
           disastrous for the final product (too  many  cooks  and
           all  that).   Whether this locking is the default is up
           to your system administrator. If locking is on, --ll will
           turn  it  off,  and  vice versa. Note that this locking
           will not prevent _y_o_u from invoking PMake twice  in  the
           same place -- if you own the lock file, PMake will warn
           you about it but continue to execute.
      --mm _d_i_r_e_c_t_o_r_y
           Tells PMake another place to search for included  make-
           files  via  the <...> style.  Several --mm options can be
           given to form a search path.  If this construct is used
           the  default  system makefile search path is completely
           overridden.  To be explained in chapter 3, section 3.2.
      --nn   This  flag  tells  PMake  not  to  execute the commands
           needed to update the out-of-date targets in  the  make-
           file.  Rather,  PMake will simply print the commands it
           would have executed and exit. This is particularly use-
           ful  for  checking  the  correctness  of a makefile. If
           PMake doesn't do what you expect it  to,  it's  a  good
           chance the makefile is wrong.
      --pp _n_u_m_b_e_r
           This  causes  PMake  to print its input in a reasonable
           form, though not necessarily one that would make  imme-
           diate  sense to anyone but me. The _n_u_m_b_e_r is a bitwise-









      PSD:12-16                                PMake -- A Tutorial


           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  -p  3
           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.
      --qq   If you give PMake this flag, it will not try to re-cre-
           ate  anything.  It will just see if anything is out-of-
           date and exit non-zero if so.
      --rr   When 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.
      --ss   This causes PMake to not print commands before  they're
           executed. It is the equivalent of putting an `@' before
           every command in the makefile.
      --tt   Rather than try to re-create a target, PMake will  sim-
           ply ``touch'' it so as to make it appear up-to-date. If
           the target didn't exist before, it will when PMake fin-
           ishes,  but  if the target did exist, it will appear to
           have been updated.
      --vv   This is a mixed-compatibility flag  intended  to  mimic
           the  System V version of Make. It is the same as giving
           --BB, and --VV as well as turning  off  directory  locking.
           Targets can still be created in parallel, however. This
           is the mode PMake will enter if it is invoked either as
           ``smake'' or ``vmake''.
      --xx   This  tells  PMake  it's  ok  to  export  jobs to other
           machines, if they're available. It is used when running
           in  Make  mode, as exporting in this mode tends to make
           things run slower than if the commands were  just  exe-
           cuted locally.
      --BB   Forces PMake to be as backwards-compatible with Make as
           possible while still being itself.  This includes:
           +o Executing one shell per shell command
           +o Expanding anything that looks  even  vaguely  like  a
             variable,  with  the empty string replacing any vari-
             able PMake doesn't know.
           +o Refusing to allow you to escape a `#'  with  a  back-
             slash.
           +o Permitting  undefined  variables  on dependency lines
             and conditionals (see below).  Normally  this  causes
             PMake to abort.
      --CC   This nullifies any and all compatibility mode flags you
           may have given or implied up to  the  time  the  --CC  is
           encountered. It is useful mostly in a makefile that you
           wrote for PMake to  avoid  bad  things  happening  when
           someone runs PMake as ``make'' or has things set in the
           environment that tell it to be compatible.  --CC  is  _n_o_t
           placed  in the PMAKE environment variable or the .MAKE-
           FLAGS or MFLAGS global variables.










      PMake -- A Tutorial                                PSD:12-17


      --DD _v_a_r_i_a_b_l_e
           Allows you to define a variable to have  ``1''  as  its
           value.   The  variable is a global variable, not a com-
           mand-line variable. This is useful  mostly  for  people
           who  are  used  to  the  C compiler arguments and those
           using conditionals, which I'll get into in section 4.3
      --II _d_i_r_e_c_t_o_r_y
           Tells PMake another place to search for included  make-
           files.  Yet  another thing to be explained in chapter 3
           (section 3.2, to be precise).
      --JJ _n_u_m_b_e_r
           Gives the absolute maximum number of targets to  create
           at once on both local and remote machines.
      --LL _n_u_m_b_e_r
           This  specifies the maximum number of targets to create
           on the local machine at once. This may be 0, though you
           should be wary of doing this, as PMake may hang until a
           remote machine becomes available, if one is not  avail-
           able when it is started.
      --MM   This is the flag that provides absolute, complete, full
           compatibility with Make. It still allows you to use all
           but  a few of the features of PMake, but it is non-par-
           allel. This is the mode PMake enters  if  you  call  it
           ``make.''
      --PP   When  creating  targets in parallel, several shells are
           executing at once, each wanting to write  its  own  two
           cent's-worth  to  the screen.  This output must be cap-
           tured by PMake in some way  in  order  to  prevent  the
           screen from being filled with garbage even more indeci-
           pherable than you usually see. PMake has  two  ways  of
           doing this, one of which provides for much cleaner out-
           put and a clear separation between the output  of  dif-
           ferent jobs, the other of which provides a more immedi-
           ate response so one can tell what is really  happening.
           The  former  is done by notifying you when the creation
           of a target starts, capturing the output and  transfer-
           ring  it  to  the  screen all at once when the job fin-
           ishes. The latter is done by catching the output of the
           shell  (and  its  children)  and  buffering it until an
           entire line is received, then printing that  line  pre-
           ceded  by  an indication of which job produced the out-
           put. Since I prefer this second method, it is  the  one
           used  by  default. The first method will be used if you
           give the --PP flag to PMake.
      --VV   As mentioned before, the --VV flag  tells  PMake  to  use
           Make's  style  of expanding variables, substituting the
           empty string for any variable it doesn't know.
      --WW   There are several times when PMake will print a message
           at  you that is only a warning, i.e. it can continue to
           work in spite of your having done something silly (such
           as  forgotten a leading tab for a shell command). Some-
           times you are well aware of silly things you have  done
           and  would  like PMake to stop bothering you. This flag
           tells it to shut up about anything non-fatal.









      PSD:12-18                                PMake -- A Tutorial


      --XX   This flag causes PMake to not  attempt  to  export  any
           jobs to another machine.
      Several  flags  may  follow  a  single `-'. Those flags that
      require arguments take them from successive parameters. E.g.

           pmake -fDnI server.mk DEBUG /chip2/X/server/include

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

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

      22..99..  EExxeerrcciisseess
                                  TTBBAA












      PMake -- A Tutorial                                PSD:12-19


      33..  SShhoorrtt--ccuuttss aanndd OOtthheerr NNiiccee TThhiinnggss
      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...

      33..11..  TTrraannssffoorrmmaattiioonn RRuulleess
      As  you  know,  a  file's name consists of two parts: a base
      name, which gives some hint as to the contents of the  file,
      and  a  suffix,  which  usually  indicates the format of the
      file.  Over the years, as UNIX(R) has developed, naming con-
      ventions,  with regard to suffixes, have also developed that
      have become almost as incontrovertible as Law. E.g.  a  file
      ending in .c is assumed to contain C source code; one with a
      .o suffix is assumed to be a  compiled,  relocatable  object
      file  that may be linked into any program; a file with a .ms
      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 solely 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 .SUFFIXES.  E.g.

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

      The creation script attached to the target is used to trans-
      form a file with the first suffix (in this case, .c) into  a
      file  with  the  second suffix (here, .o).  In addition, the
      target inherits whatever attributes have been applied to the
      transformation  rule.  The simple rule given above says that
      to transform a C source file into an object file,  you  com-
      pile  it  using  cc  with  the  -c flag.  This rule is taken
      straight from the system makefile. Many transformation rules
      (and  suffixes) are defined there, and I refer you to it for
      more examples (type ``pmake -h'' to find out where it is).
      There are several things to note  about  the  transformation
      rule given above:










      PSD:12-20                                PMake -- A Tutorial


           1)   The .IMPSRC variable.  This variable is set to the
                ``implied source'' (the file from which the target
                is  being created; the one with the first suffix),
                which, in this case, is the .c file.
           2)   The CFLAGS variable. Almost all of the transforma-
                tion rules in the system makefile are set up using
                variables that you can alter in your  makefile  to
                tailor  the  rule  to your needs. In this case, if
                you want all your C files to be compiled with  the
                --gg flag, to provide information for dbx, you would
                set the CFLAGS variable to contain -g (``CFLAGS  =
                -g'') 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:

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

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

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

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

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

      and a directory set up like this:

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

      While just typing ``pmake'' will do the  right  thing,  it's
      -----------
        1 This  is  also somewhat cleaner, I think, than
      the dynamic source solution presented in 2.6









      PMake -- A Tutorial                                PSD:12-21


      much  more  informative  to  type ``pmake -d s''.  This will
      show you what PMake is up to as it processes the  files.  In
      this case, PMake prints the following:

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

      Suff_FindDeps  is  the  name  of a function in 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,
      however, that there is a definite order to the suffixes that
      are tried. This order is set by the  relative  positions  of
      the  suffixes  on the .SUFFIXES line -- the earlier a suffix
      appears, the earlier it is checked as the source of a trans-
      formation.  Once  a suffix has been defined, the only way to
      change its position in the pecking order is  to  remove  all
      the  suffixes (by having a .SUFFIXES dependency line with no
      sources) and redefine them in the order  you  want.  (Previ-
      ously-defined  transformation  rules  will  be automatically
      redefined as the suffixes they involve are re-entered.)
      Another way to affect the search order is to make the depen-
      dency  explicit.  In the above example, a.out depends on a.o
      and b.o.  Since a transformation exists  from  .o  to  .out,
      PMake uses that, as indicated by the ``using existing source
      a.o'' message.










      PSD:12-22                                PMake -- A Tutorial


      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:

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

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

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

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












      PMake -- A Tutorial                                PSD:12-23


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

      One final question remains: what does 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 .NULL
      target mentioned later. In the system makefile, .out is 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).

      33..22..  IInncclluuddiinngg OOtthheerr MMaakkeeffiilleess
      Just  as for programs, it is often useful to extract certain
      parts of a makefile into another file and just include it in
      other  makefiles somehow. Many compilers allow you say some-
      thing like

           #include "defs.h"

      to include the contents of defs.h in the source file.  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:

           #include <file>

      or this

           #include "file"

      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 (or directories) (to find
      out what that directory is, give PMake the  --hh  flag).   The









      PSD:12-24                                PMake -- A Tutorial


      system  makefile directory search path can be overridden via
      the --mm option.  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 --II flags, in
                the order in which you gave them.
           4)   Directories given by .PATH dependency  lines  (see
                chapter 4).
           5)   The system makefile directory.
      in that order.
      You  are  free to use PMake variables in the filename--PMake
      will expand them before searching for  the  file.  You  must
      specify  the  searching method with either angle brackets or
      double-quotes _o_u_t_s_i_d_e of a variable expansion. I.e. the fol-
      lowing

           SYSTEM    = <command.mk>

           #include $(SYSTEM)

      won't work.

      33..33..  SSaavviinngg CCoommmmaannddss
      There  may  come  a  time when you will want to save certain
      commands to be executed when everything else  is  done.  For
      instance:  you're  making several different libraries at one
      time and you want to create the members in parallel. Problem
      is,  ranlib  is  another one of those programs that can't be
      run more than once in the same directory at  the  same  time
      (each  one  creates  a  file  called __.SYMDEF into which it
      stuffs information for the linker to use. Two of  them  run-
      ning at once will overwrite each other's file and the result
      will be garbage for both parties). You might want a  way  to
      save  the ranlib commands til the end so they can be run one
      after the  other,  thus  keeping  them  from  trashing  each
      other's  file.  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 ranlib case above, you might do this:





















      PMake -- A Tutorial                                PSD:12-25


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

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

      This would save both

           ranlib $(.TARGET)

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

      33..44..  TTaarrggeett AAttttrriibbuutteess
      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 --tt flag.
      .EXEC       This  attribute  causes  its  shell script to be
                  executed while having no effect on targets  that
                  depend  on it. This makes the target into a sort
                  of subroutine.  An example. Say  you  have  some
                  LISP  files  that need to be compiled and loaded
                  into a LISP process. To do this, you  echo  LISP
                  commands  into  a  file  and execute a LISP with
                  this file as its input when  everything's  done.
                  Say  also that you have to load other files from









      PSD:12-26                                PMake -- A Tutorial


                  another system before you can compile your files
                  and  further,  that you don't want to go through
                  the loading and dumping unless one of _y_o_u_r files
                  has  changed.  Your makefile might look a little
                  bit like this (remember, this is an  educational
                  example, and don't worry about the COMPILE rule,
                  all will soon become clear, grasshopper):

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

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

                  .EXEC sources, don't appear in the  local  vari-
                  ables  of  targets  that depend on them (nor are
                  they touched if PMake is  given  the  --tt  flag).
                  Note  that all the rules, not just that for sys-
                  tem, include init as a source. This  is  because
                  none of the other targets can be made until init
                  has been made, thus they depend on it.
      .EXPORT     This is used to mark those  targets  whose  cre-
                  ation  should  be  sent to another machine if at
                  all possible. This may be used by some  exporta-
                  tion  schemes  if  the exportation is expensive.
                  You should ask your system administrator  if  it
                  is necessary.
      .EXPORTSAME Tells  the  export system that the job should be
                  exported to a machine of the  same  architecture
                  as  the  current  one.  Certain operations (e.g.
                  running text through nroff) can be performed the
                  same on any architecture (CPU and operating sys-
                  tem type), while others (e.g. compiling  a  pro-
                  gram  with  cc)  must  be performed on a machine
                  with the same architecture. Not all export  sys-
                  tems will support this attribute.
      .IGNORE     Giving  a  target  the  .IGNORE attribute causes
                  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









      PMake -- A Tutorial                                PSD:12-27


                  have a makefile that creates two  programs,  one
                  of which is used to create the other, so it must
                  exist before the other is created. You could say

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

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

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

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

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









      PSD:12-28                                PMake -- A Tutorial


                  target (if it's out-of-date) even  if  you  gave
                  the  --nn or --tt flag. By doing this, you can start
                  at the top of a system and type

                       pmake -n

                  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 --nn flag.
      .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 tar-
                  get the .NOEXPORT  attribute,  it  will  be  run
                  locally,  even  if  you've  given PMake the --LL 00
                  flag.
      .NOTMAIN    Normally, if you do not specify a target to make
                  in any other way, PMake will take the first tar-
                  get on the first dependency line of  a  makefile
                  as the target to create. That target is known as
                  the ``Main Target'' and is labeled  as  such  if
                  you  print  the  dependencies  out  using the --pp
                  flag.  Giving  a  target  this  attribute  tells
                  PMake that the target is definitely _n_o_t the Main
                  Target.  This allows you to place targets in  an
                  included  makefile  and  have PMake create some-
                  thing else by default.
      .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  .PRECIOUS  attribute, however,
                  PMake will leave it alone.  An  additional  side
                  effect  of the `::' operator is to mark the tar-
                  gets as .PRECIOUS.
      .SILENT     Marking a target with this attribute  keeps  its
                  commands  from  being  printed when they're exe-
                  cuted, just as if they had an `@'  in  front  of
                  them.
      .USE        By  giving  a target this attribute, you turn it
                  into 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 .USE) of the source.  If
                  the target already has commands, the  .USE  tar-
                  get's  commands  are  added  to the end. If more
                  than one .USE-marked source is given to  a  tar-
                  get, the rules are applied sequentially.
                  The  typical .USE rule (as I call them) will use
                  the sources of the target to which it is applied
                  (as  stored in the .ALLSRC variable for the tar-
                  get) as its ``arguments,''  if  you  will.   For
                  example,  you probably noticed that the commands









      PMake -- A Tutorial                                PSD:12-29


                  for creating lib1.a and lib2.a in the example in
                  section  3.3  were exactly the same. You can use
                  the .USE attribute to eliminate the  repetition,
                  like so:

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

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

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

      33..55..  SSppeecciiaall TTaarrggeettss
      As  there  were  in  Make, so there are certain targets that
      have special meaning to PMake. When you use one on a  depen-
      dency  line,  it  is  the only target that may appear on the
      left-hand-side of the operator.  As for the  attributes  and
      variables,  all  the special targets begin with a period and
      consist of upper-case letters only.  I won't  describe  them
      all  in  detail  because some of them are rather complex and
      I'll describe them in more detail than you'll want in  chap-
      ter 4.  The targets are as follows:
      .BEGIN    Any  commands attached to this target are executed
                before anything else is done. You can use  it  for
                any initialization that needs doing.
      .DEFAULT  This  is  sort of a .USE rule for any target (that
                was used only as a source) that PMake can't figure
                out any other way to create. It's only ``sort of''
                a .USE rule because only the shell script attached
                to  the .DEFAULT target is used. The .IMPSRC vari-
                able of a target that inherits .DEFAULT's commands
                is set to the target's own name.
      .END      This  serves a function similar to .BEGIN, in that
                commands attached to it are executed  once  every-
                thing  has  been  re-created (so long as no errors
                occurred). It also serves the  extra  function  of
                being a place on which PMake can hang commands you
                put off to the end. Thus the script for this  tar-
                get  will  be  executed before any of the commands
                you save with the ``...''.









      PSD:12-30                                PMake -- A Tutorial


      .EXPORT   The sources for this  target  are  passed  to  the
                exportation  system compiled into PMake. Some sys-
                tems will use these  sources  to  configure  them-
                selves.  You  should ask your system administrator
                about this.
      .IGNORE   This target marks each of  its  sources  with  the
                .IGNORE  attribute.  If  you  don't  give  it  any
                sources, then it is like giving the --ii  flag  when
                you  invoke  PMake  --  errors are ignored for all
                commands.
      .INCLUDES The sources for this target are taken to  be  suf-
                fixes that indicate a file that can be included in
                a program  source  file.   The  suffix  must  have
                already  been declared with .SUFFIXES (see below).
                Any suffix so marked will have the directories  on
                its  search  path (see .PATH, below) placed in the
                .INCLUDES variable, each preceded by  a  --II  flag.
                This  variable can then be used as an argument for
                the compiler in the normal fashion. The .h  suffix
                is  already marked in this way in the system make-
                file.  E.g. if you have

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

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

                     cc $(.INCLUDES) -c xprogram.c

                (Note:  the  .INCLUDES  variable  is  not actually
                filled in  until  the  entire  makefile  has  been
                read.)
      .INTERRUPT
                When  PMake  is  interrupted,  it will execute the
                commands in the script  for  this  target,  if  it
                exists.
      .LIBS     This  does  for  libraries what .INCLUDES does for
                include files, except the  flag  used  is  --LL,  as
                required  by  those linkers that allow you to tell
                them where to find libraries. The variable used is
                .LIBS.  Be forewarned that PMake may not have been
                compiled to do this if the linker on  your  system
                doesn't accept the --LL flag, though the .LIBS vari-
                able will always be defined once the makefile  has
                been read.
      .MAIN     If you didn't give a target (or targets) to create
                when you invoked PMake, it will take  the  sources
                of this target as the targets to create.
      .MAKEFLAGS
                This target provides a way for you to always spec-
                ify flags for PMake when the makefile is used. The
                flags are just as they would be typed to the shell









      PMake -- A Tutorial                                PSD:12-31


                (except  you  can't  use  shell  variables  unless
                they're  in the environment), though the --ff and --rr
                flags have no effect.
      .NULL     This allows  you  to  specify  what  suffix  PMake
                should  pretend  a file has if, in fact, it has no
                known suffix. Only one suffix  may  be  so  desig-
                nated.  The  last source on the dependency line is
                the suffix that is used (you should, however, only
                give one suffix...).
      .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  direc-
                tories  added to the search path before. Since the
                effects of this all get very complex,  I'll  leave
                it  til chapter four to give you a complete expla-
                nation.
      .PATH_s_u_f_f_i_x
                This does a similar thing to .PATH, but it does it
                only  for  files with the given suffix. The suffix
                must have been defined  already.  Look  at  SSeeaarrcchh
                PPaatthhss (section 4.1) for more information.
      .PRECIOUS Similar  to  .IGNORE,  this  gives  the  .PRECIOUS
                attribute to each source on the  dependency  line,
                unless  there  are  no  sources, in which case the
                .PRECIOUS attribute is given to  every  target  in
                the file.
      .RECURSIVE
                This target applies the .MAKE attribute to all its
                sources. It does nothing if you don't give it  any
                sources.
      .SHELL    PMake  is not constrained to only using the Bourne
                shell to execute the commands you put in the make-
                file. You can tell it some other shell to use with
                this target. Check out AA SShheellll iiss  aa  SShheellll  iiss  aa
                SShheellll (section 4.4) for more information.
      .SILENT   When  you  use .SILENT as a target, it applies the
                .SILENT attribute to each of its sources. If there
                are  no sources on the dependency line, then it is
                as if you gave PMake the --ss flag and  no  commands
                will be echoed.
      .SUFFIXES This  is  used to give new file suffixes for PMake
                to handle. Each source is a  suffix  PMake  should
                recognize. If you give a .SUFFIXES dependency line
                with no sources, PMake will forget about  all  the
                suffixes  it  knew  (this also nukes the null suf-
                fix).  For those targets that need  to  have  suf-
                fixes defined, this is how you do it.
      In addition to these targets, a line of the form

           _a_t_t_r_i_b_u_t_e : _s_o_u_r_c_e_s

      applies  the _a_t_t_r_i_b_u_t_e to all the targets listed as _s_o_u_r_c_e_s.










      PSD:12-32                                PMake -- A Tutorial


      33..66..  MMooddiiffyyiinngg VVaarriiaabbllee EExxppaannssiioonn
      Variables  need  not  always  be  expanded  verbatim.  PMake
      defines  several  modifiers  that  may be applied to a vari-
      able's value before it is expanded. You apply a modifier  by
      placing  it after the variable name with a colon between the
      two, like so:

           ${_V_A_R_I_A_B_L_E:_m_o_d_i_f_i_e_r}

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









      PMake -- A Tutorial                                PSD:12-33


                the  _s_e_a_r_c_h_-_s_t_r_i_n_g  and  the   _r_e_p_l_a_c_e_m_e_n_t_-_s_t_r_i_n_g,
                eexxcceepptt  that  a  backslash  is used to prevent the
                expansion of a $, not another dollar sign,  as  is
                usual.   Note that _s_e_a_r_c_h_-_s_t_r_i_n_g is just a string,
                not a pattern,  so  none  of  the  usual  regular-
                expression/wildcard  characters  have  any special
                meaning save ^ and $.  In the replacement  string,
                the  &  character is replaced by the _s_e_a_r_c_h_-_s_t_r_i_n_g
                unless it is preceded by  a  backslash.   You  are
                allowed  to  use  any  character  except  colon or
                exclamation point to  separate  the  two  strings.
                This  so-called  delimiter character may be placed
                in either string by preceding it with a backslash.
           T    Replaces  each  word  in the variable expansion by
                its last component (its  ``tail'').  For  example,
                given

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

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

           $(_V_A_R_I_A_B_L_E:_s_e_a_r_c_h_-_s_t_r_i_n_g=_r_e_p_l_a_c_e_m_e_n_t)

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

      33..77..  MMoorree oonn DDeebbuuggggiinngg

      33..88..  MMoorree EExxeerrcciisseess
      (3.1)
           You've  got  a  set  programs, each of which is created
           from its  own  assembly-language  source  file  (suffix
           .asm).   Each  program  can  be assembled into two ver-
           sions, one with error-checking code  assembled  in  and
           one  without.  You  could assemble them into files with
           different suffixes (.eobj and .obj, for instance),  but









      PSD:12-34                                PMake -- A Tutorial


           your  linker  only  understands files that end in .obj.
           To top it all off, the final executables _m_u_s_t have  the
           suffix  .exe.   How  can  you  still use transformation
           rules to make your life easier (Hint: assume the error-
           checking versions have ec tacked onto their prefix)?
      (3.2)
           Assume, for a moment or two, you want to perform a sort
           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

                $($(FOO))

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

      44..  PPMMaakkee ffoorr GGooddss
      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.

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

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

      would    tell    PMake    to   look   in   the   directories
      /sprite/lib/include  and  /sprite/att/lib/include  for   any
      files whose suffix is .h.
      The  current directory is always consulted first to see if a
      file exists. Only if it cannot be found there are the direc-
      tories in the specific search path, followed by those in the









      PMake -- A Tutorial                                PSD:12-35


      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  (.ALLSRC,  and .IMPSRC) will instead contain
      the path to the file, as found by PMake.  Thus if you have a
      file ../lib/mumble.c and a makefile

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

      the  command executed to create mumble would be ``cc -o mum-
      ble ../lib/mumble.c.''  (As an aside, the  command  in  this
      case  isn't strictly necessary, since it will be found using
      transformation rules if it isn't given. This is because .out
      is  the  null  suffix by default and a transformation exists
      from .c to .out.  Just thought I'd throw that in.)
      If a file exists in two directories on the same search path,
      the  file in the first directory on the path will be the one
      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.
      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 .out.

      44..22..  AArrcchhiivveess aanndd LLiibbrraarriieess
      UNIX  and  Sprite  allow  you to merge files into an archive
      using the ar command. Further, if the files are  relocatable
      object  files,  you  can  run  ranlib on the archive and get
      yourself a library that you can link into  any  program  you
      want.  The  main  problem  with  archives is they double the
      space you need to store the archived  files,  since  there's
      one  copy  in  the  archive  and one copy out by itself. The
      problem with libraries is you usually think of them  as  -lm
      rather  than  /usr/lib/libm.a  and the linker thinks they're
      out-of-date if you so much as look at them.









      PSD:12-36                                PMake -- A Tutorial


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

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

      This  will  cause  the three object files to be compiled (if
      the corresponding  source  files  were  modified  after  the
      object  file  or, if that doesn't exist, the archived object
      file), the out-of-date ones archived in libprog.a,  a  table









      PMake -- A Tutorial                                PSD:12-37


      of  contents  placed  in  the archive and the newly-archived
      object files to be removed.
      All this is used in the makelib.mk system makefile to create
      a single library with ease. This makefile looks like this:

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

           #ifndef _MAKELIB_MK
           _MAKELIB_MK    =

           #include  <po.mk>

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

           ARFLAGS        ?= crl

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

           #endif /* _MAKELIB_MK */


      44..33..  OOnn tthhee CCoonnddiittiioonn......
      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:











      PSD:12-38                                PMake -- A Tutorial


           #if _b_o_o_l_e_a_n _e_x_p_r_e_s_s_i_o_n
           _l_i_n_e_s
           #elif _a_n_o_t_h_e_r _b_o_o_l_e_a_n _e_x_p_r_e_s_s_i_o_n
           _m_o_r_e _l_i_n_e_s
           #else
           _s_t_i_l_l _m_o_r_e _l_i_n_e_s
           #endif

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










      PMake -- A Tutorial                                PSD:12-39


                    #if !empty(_v_a_r:M_w_o_r_d)

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

           #if $(OS) == 4.3

           #if $(MACHINE) == "sun3"

           #if $(LOAD_ADDR) < 0xc000

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

           #if $(LOAD)

      would see if LOAD contains a non-zero value and

           #if !$(LOAD)

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

                ifdef     defined
                ifndef    !defined
                ifmake    make
                ifnmake   !make

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

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

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









      PSD:12-40                                PMake -- A Tutorial


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

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

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

      44..44..  AA SShheellll iiss aa SShheellll iiss aa SShheellll
      In  normal  operation,  the  Bourne  Shell  (better known as
      ``sh'') 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  .SHELL
      target by keyword, as follows:
      ppaatthh==_p_a_t_h
           PMake  needs  to know where the shell actually resides,
           so it can execute it. If you specify this  and  nothing
           else, PMake will use the last component of the path and
           look in its table of the shells it knows  and  use  the
           specification  it  finds,  if any. Use this if you just
           want to use a different version  of  the  Bourne  or  C
           Shell (yes, PMake knows how to use the C Shell too).
      nnaammee==_n_a_m_e
           This  is the name by which the shell is to be known. It
           is a single word and, if no other keywords  are  speci-
           fied  (other  than ppaatthh), 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 (``.SHELL: name=csh''
           will do it).
      qquuiieett==_e_c_h_o_-_o_f_f _c_o_m_m_a_n_d
           As  mentioned  before,  PMake actually controls whether
           commands are printed by introducing commands  into  the
           shell's  input  stream. This keyword, and the next two,
           control what those commands are. The qquuiieett  keyword  is
           the command used to turn echoing off. Once it is turned
           off, echoing is expected to remain off until the  echo-
           on command is given.
      eecchhoo==_e_c_h_o_-_o_n _c_o_m_m_a_n_d
           The  command  PMake should give to turn echoing back on
           again.









      PMake -- A Tutorial                                PSD:12-41


      ffiilltteerr==_p_r_i_n_t_e_d _e_c_h_o_-_o_f_f _c_o_m_m_a_n_d
           Many shells will echo the echo-off command when  it  is
           given.  This  keyword  tells  PMake  in what format the
           shell actually prints the  echo-off  command.  Wherever
           PMake  sees  this string in the shell's output, it will
           delete it and  any  following  whitespace,  up  to  and
           including  the next newline. See the example at the end
           of this section for more details.
      eecchhooFFllaagg==_f_l_a_g _t_o _t_u_r_n _e_c_h_o_i_n_g _o_n
           Unless a target has been marked .SILENT, 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).
      eerrrrFFllaagg==_f_l_a_g _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_n
           Likewise, unless a  target  is  marked  .IGNORE,  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 eecchhooFFllaagg.
      cchheecckk==_c_o_m_m_a_n_d _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_n
           Just as for echo-control, error-control is achieved  by
           inserting  commands into the shell's input stream. This
           is the command to make the shell check for  errors.  It
           also  serves  another purpose if the shell doesn't have
           error-control as commands, but I'll get into that in  a
           minute.  Again, once error checking has been turned on,
           it is expected to remain on  until  it  is  turned  off
           again.
      iiggnnoorree==_c_o_m_m_a_n_d _t_o _t_u_r_n _e_r_r_o_r _c_h_e_c_k_i_n_g _o_f_f
           This  is  the command PMake uses to turn error checking
           off. It has another use if the shell doesn't do  error-
           control, but I'll tell you about that...now.
      hhaassEErrrrCCttll==_y_e_s _o_r _n_o
           This  takes  a value that is either yyeess or nnoo.  Now you
           might think that the existence of the cchheecckk and  iiggnnoorree
           keywords would be enough to tell PMake if the shell can
           do error-control, but you'd be wrong. If  hhaassEErrrrCCttll  is
           yyeess,  PMake  uses  the  check  and ignore commands in a
           straight-forward manner.  If this is nnoo, however, their
           use  is  rather different. In this case, the check com-
           mand is used as a template, in which the string  %%ss  is
           replaced by the command that's about to be executed, to
           produce a command for the shell that will echo the com-
           mand to be executed. The ignore command is also used as
           a template, again with %%ss replaced by the command to be
           executed,  to  produce  a command that will execute the
           command to be executed and ignore any error it returns.
           When these strings are used as templates, you must pro-
           vide newline(s) (``\n'') in the appropriate place(s).
      The strings that follow these keywords may  be  enclosed  in
      single  or  double  quotes (the quotes will be stripped off)









      PSD:12-42                                PMake -- A Tutorial


      and may contain the usual C backslash-characters (\n is new-
      line,  \r  is  return, \b is backspace, \' escapes a single-
      quote inside single-quotes, \" escapes a double-quote inside
      double-quotes). Now for an example.
      This  is  actually the contents of the <shx.mk> system make-
      file, and causes 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:

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

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











      PMake -- A Tutorial                                PSD:12-43


                hasErrCtl=no \
                check="echo \"+ %s\"\n" \
                ignore="sh -c '%s || exit 0\n"

      This will cause PMake to execute the two commands

           echo "+ _c_m_d"
           sh -c '_c_m_d || true'

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

      44..55..  CCoommppaattiibbiilliittyy
      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.   --BB  (one  shell per command) implies --VV) The
      three levels are described in the following three  sections.

      44..55..11..  DDEEFFCCOONN 33 ---- VVaarriiaabbllee EExxppaannssiioonn
      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 (``${PRINTER}''), the shell will
      let them pass. If they are enclosed in parentheses, however,
      the shell will declare a syntax error and the make will come
      to a grinding halt.
      You have two choices: change  the  makefile  to  define  the
      variables  (their  values  can  be overridden on the command
      line, since that's where they would have  been  set  if  you
      used  Make,  anyway) or always give the --VV flag (this can be
      done with the .MAKEFLAGS target, if you want).

      44..55..22..  DDEEFFCCOONN 22 ---- TThhee NNuummbbeerr ooff tthhee BBeeaasstt
      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 --BB flag forces PMake to  do),  which
      slows  the  process  down a good bit and requires you to use
      semicolons and escaped newlines for shell constructs, or  by
      changing the makefile to execute the offending command(s) in
      a subshell (by placing the line  inside  parentheses),  like
      so:










      PSD:12-44                                PMake -- A Tutorial


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

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

      44..55..33..  DDEEFFCCOONN 11 ---- IImmiittaattiioonn iiss tthhee NNoott tthhee HHiigghheesstt FFoorrmm ooff
      FFllaatttteerryy
      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 --MM flag (for  Make),  or  by  executing  PMake  as
      ``make.''   In  either  case,  PMake performs things exactly
      like Make (while still supporting most of the nice new  fea-
      tures PMake provides). This includes:
      +o No parallel execution.
      +o Targets are made in the exact order specified by the make-
        file. The sources for each target are made in strict left-
        to-right order, etc.
      +o A  single  Bourne  shell  is used to execute each command,
        thus the shell's $$ variable is useless, changing directo-
        ries doesn't work across command lines, etc.
      +o If  no  special  characters exist in a command line, PMake
        will break the command into words itself and  execute  the
        command  directly,  without  executing  a shell first. The
        characters that cause PMake to execute a shell are: #,  =,
        |,  ^, (, ), {, }, ;, &, <, >, *, ?, [, ], :, $, `, and \.
        You should notice that these are all the  characters  that
        are  given  special  meaning by the shell (except ' and  ,
        which PMake deals with all by its lonesome).
      +o The use of the null suffix is turned off.

      44..66..  TThhee WWaayy TThhiinnggss WWoorrkk
      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,  respec-
      tively), but also a count of the number of its children that
      have already been processed.
      The most important thing to know about how 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









      PMake -- A Tutorial                                PSD:12-45


      line, or via a .MAIN target, or  by  the  target  being  the
      first  in  the file not labeled with the .NOTMAIN attribute)
      placed in a queue. It continues to take  the  node  off  the
      front  of  the  queue, mark it as something that needs to be
      made, pass the node to Suff_FindDeps (mentioned earlier)  to
      find  any  implicit  sources for the node, and place all the
      node's children that have yet to be marked at the end of the
      queue. If any of the children is a .USE rule, its attributes
      are applied to the parent, then its commands are appended to
      the parent's list of commands and its children are linked to
      its parent. The parent's unmade  children  counter  is  then
      decremented  (since  the  .USE node has been processed). You
      will note that this allows a .USE node to have children that
      are  .USE  nodes  and the rules will be applied in sequence.
      If the node has no children, it is  placed  at  the  end  of
      another  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 .END target, either explicitly or
      through the use of an ellipsis in a shell script.  If  there
      were no errors during the entire process but there are still
      some targets unmade (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.

      55..  AAnnsswweerrss ttoo EExxeerrcciisseess
      (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
           (.ms,  .c, 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:












      PSD:12-46                                PMake -- A Tutorial


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

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

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

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

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

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

      66..  GGlloossssaarryy ooff JJaarrggoonn
      aattttrriibbuuttee:: A property given to a target that causes PMake to
           treat it differently.
      ccoommmmaanndd ssccrriipptt:: The lines immediately following a dependency
           line that specify commands to execute to create each of
           the targets on the dependency line. Each  line  in  the
           command script must begin with a tab.
      ccoommmmaanndd--lliinnee  vvaarriiaabbllee::  A  variable  defined in an argument
           when PMake is first executed.   Overrides  all  assign-
           ments to the same variable name in the makefile.
      ccoonnddiittiioonnaall::  A  construct  much  like  that  used in C that
           allows a makefile to be configured on the fly based  on
           the local environment, or on what is being made by that
           invocation of PMake.
      ccrreeaattiioonn ssccrriipptt:: Commands  used  to  create  a  target.  See
           ``command script.''
      ddeeppeennddeennccyy::  The relationship between a source and a target.
           This comes in three flavors, as indicated by the opera-
           tor  between  the  target  and  the source. `:' gives a
           straight time-wise dependency (if the target  is  older
           than  the source, the target is out-of-date), while `!'
           provides simply an ordering and  always  considers  the









      PMake -- A Tutorial                                PSD:12-47


           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.
      ddyynnaammiicc  ssoouurrccee::  This  refers  to a source that has a local
           variable invocation in it. It allows  a  single  depen-
           dency  line to specify a different source for each tar-
           get on the line.
      gglloobbaall vvaarriiaabbllee:: Any variable defined in a  makefile.  Takes
           precedence  over  variables defined in the environment,
           but not over command-line or local variables.
      iinnppuutt ggrraapphh:: What PMake constructs from a makefile. Consists
           of  nodes  made of the targets in the makefile, and the
           links between them (the dependencies).  The  links  are
           directed  (from  source to target) and there may not be
           any cycles (loops) in the graph.
      llooccaall vvaarriiaabbllee:: A variable defined by PMake visible only  in
           a  target's  shell script.  There are seven local vari-
           ables, not all of which are defined for  every  target:
           .TARGET,  .ALLSRC, .OODATE, .PREFIX, .IMPSRC, .ARCHIVE,
           and .MEMBER.  .TARGET, .PREFIX, .ARCHIVE,  and  .MEMBER
           may  be  used  on  dependency lines to create ``dynamic
           sources.''
      mmaakkeeffiillee:: A file that describes how a system  is  built.  If
           you  don't  know  what  it  is after reading this tuto-
           rial....
      mmooddiiffiieerr:: A letter, following a colon, used to alter  how  a
           variable is expanded.  It has no effect on the variable
           itself.
      ooppeerraattoorr:: What separates a source from a target (on a depen-
           dency  line) and specifies the relationship between the
           two. There are three: `:', `::', and `!'.
      sseeaarrcchh ppaatthh:: A list of directories in which a file should be
           sought.  PMake's view of the contents of directories in
           a search path does not change  once  the  makefile  has
           been read. A file is sought on a search path only if it
           is exclusively a source.
      sshheellll:: A program to which commands are passed  in  order  to
           create targets.
      ssoouurrccee:: Anything to the right of an operator on a dependency
           line. Targets on the dependency line are  usually  cre-
           ated from the sources.
      ssppeecciiaall  ttaarrggeett::  A  target  that causes PMake to do special
           things when it's encountered.
      ssuuffffiixx:: The tail end of a file name. Usually begins  with  a
           period, .c or .ms, e.g.
      ttaarrggeett::  A  word to the left of the operator on a dependency
           line. More generally, any file that PMake might create.
           A file may be (and often is) both a target and a source
           (what it is depends on how PMake is looking  at  it  at
           the  time  -- sort of like the wave/particle duality of
           light, you know).
      ttrraannssffoorrmmaattiioonn rruullee:: A special construct in a makefile  that
           specifies  how to create a file of one type from a file
           of another, as indicated by their suffixes.









      PSD:12-48                                PMake -- A Tutorial


      vvaarriiaabbllee eexxppaannssiioonn:: The process of substituting the value of
           a  variable  for  a  reference  to it. Expansion may be
           altered by means of modifiers.
      vvaarriiaabbllee:: A place  in  which  to  store  text  that  may  be
           retrieved later. Also used to define the local environ-
           ment. Conditionals exist that test whether  a  variable
           is defined or not.
























































      PMake -- A Tutorial                                PSD:12-49



                              TTaabbllee ooff CCoonntteennttss


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



















