


       



       .














                                   Cook

                         A File Construction Tool




                                User Guide







                               Peter Miller

                         [4mmillerp@canb.auug.org.au[0m

































       .












       This document describes Cook version 2.26
       and was prepared 25 November 2024.






       This  document  describing  the  Cook  program, and the Cook
       program itself, are
       Copyright (C) 1988, 1989,  1990,  1991,  1992,  1993,  1994,
       1995,  1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
       2005, 2006 Peter Miller; All rights reserved.

       This program is  free  software;  you  can  redistribute  it
       and/or  modify  it under the terms of the GNU General Public
       License as published by the Free Software Foundation; either
       version  2  of  the  License,  or (at your option) any later
       version.

       This program is distributed in the  hope  that  it  will  be
       useful,  but  WITHOUT ANY WARRANTY; without even the implied
       warranty of MERCHANTABILITY  or  FITNESS  FOR  A  PARTICULAR
       PURPOSE.   See  the  GNU  General  Public  License  for more
       details.

       You should have received a copy of the  GNU  General  Public
       License  along  with this program; if not, write to the Free
       Software Foundation,  Inc.,  59  Temple  Place,  Suite  330,
       Boston, MA 02111, USA.



















       Cook                                              User Guide



       _1_.  _I_n_t_r_o_d_u_c_t_i_o_n

       This document describes ccooookk, a maintenance tool designed to
       construct files.  CCooookk may be used to  maintain  consistency
       between  executable  files  and  the associated source files
       that  are  used  to  generate  them.   The  consistency   is
       designated  by the relative last-modified times of files and
       is thus automatically adjusted each time a file  is  edited,
       compiled   or   otherwise   modified.   CCooookk  validates  the
       consistency of a system of files and executes  all  commands
       necessary to maintain that consistency.

       CCooookk is a tool for constructing files.  It is given a set of
       files to create, and instructions detailing how to construct
       them.    In   any   non-trivial   program   there   will  be
       prerequisites  to  performing  the  actions   necessary   to
       creating  any file, such as extraction from a source-control
       system.  CCooookk provides a mechanism to define these.

       When  a  program  is  being  developed  or  maintained,  the
       programmer  will  typically change one file of several which
       comprise the program.  CCooookk examines the last-modified times
       of  the  files  to see when the prerequisites of a file have
       changed, implying that the file needs to be recreated as  it
       is logically out of date.

       CCooookk also provides a facility for implicit recipes, allowing
       users to specify how to form a file with a given suffix from
       a  file  with  a  different  suffix.  For example, to create
       _f_i_l_e_n_a_m_e..oo from _f_i_l_e_n_a_m_e..cc

       _1_._1  _W_h_y _Y_o_u _W_a_n_t _T_o _U_s_e _C_o_o_k



          +o Cook is a replacement for the traditional _m_a_k_e(1) tool.

          +o There   is   a   _m_a_k_e_2_c_o_o_k   utility  included  in  the
            distribution to help convert makefiles into  cookbooks.

          +o Cook is more powerful than the traditional _m_a_k_e tool.

          +o Cook has true variables, not simple macros.

          +o Cook has a simple but powerful string-based description
            language with many  built-in  functions.   This  allows
            sophisticated  filename  specification and manipulation
            without loss of readability or performance.

          +o Cook has user defined functions.

          +o Cook can build in parallel.




       Peter Miller                                          Page 1





       Cook                                              User Guide



          +o Cook can distribute builds across your LAN.

          +o Cook is  able  to  build  your  project  with  multiple
            parallel  threads, with support for rules which must be
            single threaded.  It is possible to distribute parallel
            builds over your LAN, allowing you to turn your network
            into a virtual parallel build engine.

          +o Cook is able to use  fingerprints  to  supplement  file
            modification  times.   This  allows  build optimization
            without contorted rules.

          +o Cook can be configured with an explicit list of primary
            source  files.   This  allow the dependency graph to be
            constructed faster by not going  down  dead  ends,  and
            also  allows better error messages when the graph can't
            be constructed.  This requires an accurate source  file
            manifest.

          +o In  addition  to walking the dependency graph, Cook can
            turn the input rules into a  shell  script,  or  a  web
            page.

          +o Cook   has   special   _c_a_s_c_a_d_e  dependencies,  allowing
            powerful  include  dependency  specification,   amongst
            other things.

          +o And  Cook  doesn't interpret tab differently to 8 space
            characters!

       If you are putting together a source-code  distribution  and
       planning  to  write  a makefile, consider writing a cookbook
       instead.  Although Cook takes a day or two to learn,  it  is
       much  more  powerful  and  a  bit  more  intuitive  than the
       traditional _m_a_k_e(1) tool.

       _1_._2  _H_o_w _t_o _U_s_e _t_h_i_s _M_a_n_u_a_l

       This manual is divided into two parts.

       The first part is tutorial introduction to ccooookk.  This  part
       runs from chapter 4 to chapter 5.

       The  second  part is for reference and details precisely how
       ccooookk works.  This part runs from chapter 6 to chapter 14.

       Users familiar with  other  programs  similar  to  ccooookk  are
       advised  to  skim  the  tutorial part before diving into the
       reference part.

       _1_._3  _A_n_c_i_e_n_t _H_i_s_t_o_r_y

       CCooookk was originally developed because I was marooned  on  an
       operating  system  without  anything even vaguely resembling


       Peter Miller                                          Page 2





       Cook                                              User Guide



       _m_a_k_e(1).  This was in 1988.  Since I had to write my own,  I
       added  a  few  improvements.  When I finally escaped back to
       UNIX, in 1990, it  took  only  two  days  to  port  ccooookk  to
       SystemV.   I  have  since deleted all code for that original
       operating system, although clues to its identity  are  still
       present.

       After I had ccooookk up on UNIX, the progress the world had made
       caught up with me.  It  was  gratifying  that  many  of  the
       features  other  make-oid authors had thought necessary were
       either already present, or easily and seamlessly added.

       CCooookk was written with portability in mind.   This  does  not
       means it is entirely portable, but it comes close.  CCooookk has
       been tested on numerous UNIX flavors.  This  was  made  much
       simpler  in  1994  when  I  started  using  the GNU Autoconf
       utility.  This means that when you obtain  the  sources  for
       Cook,  all  you  have  to  do  is  run  the _c_o_n_f_i_g_u_r_e script
       included in the distribution and Cook will be configured for
       your   system.    See   the  BUILDING  file  in  the  source
       distribution for more information.

       In 1996 Cook had internationalization support added, so that
       users  could  have  error  messages  and  other  warning and
       informational messages printed  in  their  native  language.
       This was made possible by the GNU Gettext utilities.

       In 1997 Cook had a major re-write of significant portions of
       its inference engine.  This enabled the addition of parallel
       processing   support,  and  simplified  adding  user-defined
       functions to the cookbook language.

























       Peter Miller                                          Page 3





       Cook                                              User Guide



       _2_.  _C_o_o_k _f_r_o_m _t_h_e _O_u_t_s_i_d_e

       This chapter is part of the tutorial on how to use the  ccooookk
       program.   It focuses on how to use ccooookk, without needing to
       know how ccooookk works internally.

       _2_._1  _W_h_a_t _c_a_n _c_o_o_k _d_o _f_o_r _m_e_?

       By far the most common use of cook, by experts and beginners
       alike, is to issue the command
            cook
       and  cook  will consult its cookbook to see what needs to be
       done.

       In general, ccooookk is used to take a set of files and chew  on
       them  in  some  way to produce another set of files; such as
       the source files for a program, and how to  turn  them  into
       the  executable  program  file.   In  order  for  ccooookk to do
       anything useful, it needs to know what to do.  "What to  do"
       is  contained  in  a  file  called  _H_o_w_t_o_._c_o_o_k  in  the same
       directory as the files it is going to work on.  You need  to
       execute  the   cook  command in the same directory as all of
       the files.

       _2_._2  _W_h_a_t _i_s _c_o_o_k _d_o_i_n_g_?

       The _H_o_w_t_o_._c_o_o_k file was written by the same person who wrote
       the  source  files.   It  contains a set of recipes; each of
       which, among other  things,  contain  commands  for  how  to
       manipulate  the  files.   The ccooookk program echos each of the
       commands it is about to execute, so that you can watch  what
       it is doing as it goes.

       If the _H_o_w_t_o_._c_o_o_k file contained only commands, you would be
       better off  using  a  shell  script.   In  addition  to  the
       commands  is information telling ccooookk which files need to be
       constructed  before  other  files  can  be,  and  from  this
       information  ccooookk  determines  the order in which to execute
       the commands.  Also,  ccooookk  examines  other  information  to
       determine  which  commands  it  need  not  do,  because  the
       associated files are already up-to-date.

       _2_._3  _W_h_a_t _c_a_n _c_o_o_k _a_l_w_a_y_s _d_o_?

       If you are in a directory with a _H_o_w_t_o_._c_o_o_k  file,  you  can
       expect a few common requests to work

       cook clobber   This  command  can  be expected to remove any
                      files from the directory which ccooookk  is  able
                      to reconstruct.

       cook all       This  is  the  default  action, and so can be
                      obtained by a simple cook request.  It causes
                      ccooookk  to  construct some specific file or set


       Peter Miller                                          Page 4





       Cook                                              User Guide



                      of files.

       cook clean     This is similar to "cook clobber" above,  but
                      it  only  removes intermediate files, and not
                      not the final file or files which "cook  all"
                      constructs.

       In  addition  to  the above, many _H_o_w_t_o_._c_o_o_k files will also
       define

       cook install   If  a  program  or  library  or  document  is
                      constructed   in   the  directory,  the  this
                      command will  install  it  into  the  correct
                      place in the system.

       cook uninstall The   reverse   of   the  above,  it  removes
                      something from the system.

       _2_._4  _I_f _s_o_m_e_t_h_i_n_g _g_o_e_s _w_r_o_n_g

       Most errors while ccooookk is constructing file  are  caused  by
       errors in the source files, and not the _H_o_w_t_o_._c_o_o_k file.  In
       general, you can fix the problems in the source  files,  and
       execute  the  ccooookk  command again, and ccooookk will resume from
       the command which incurred the error.

       To help you while editing the files with  the  errors,  ccooookk
       keeps  a  listing  file of all the commands it executed, and
       any output of those commands, in a file called _H_o_w_t_o_._l_i_s_t in
       the current directory.

       You  may  want ccooookk to find all the errors it can before you
       do any editing, do do this, use the --CCoonnttiinnuuee option (it may
       be abbreviated to --cc for convenience).

       _2_._5  _T_h_e _R_e_f_e_r_e_n_c_e _M_a_n_u_a_l

       For  more  information  about the command line arguments and
       options  of  the  various  commands  mentioned,  you  should
       consult the on-line manual pages.  The Cook Reference Manual
       is also a good source of this information, and is  available
       from the same place as you obtained this manual.














       Peter Miller                                          Page 5





       Cook                                              User Guide



       _3_.  _C_o_o_k _f_r_o_m _a _C_o_o_k_b_o_o_k

       This  chapter  describes  the  contents  and  meaning  of  a
       cookbook, a file which contains information ccooookk needs to do
       its  job.   It  focuses  on  what a cookbook looks like, and
       touches on a few areas of how ccooookk works does its job.

       _3_._1  _W_h_a_t _d_o_e_s _C_o_o_k _d_o_?

       The basic building block  for  ccooookk  is  the  concept  of  a
       _r_e_c_i_p_e.  A recipe has three parts:

         1.  one  or  more files which the recipe constructs, known
             as the _t_a_r_g_e_t_s of the recipe

         2.  zero or more files which are used  by  the  recipe  to
             construct  the target, known as the _i_n_g_r_e_d_i_e_n_t_s of the
             recipe

         3.  one or more commands to execute  which  construct  the
             targets from the ingredients, known as the _b_o_d_y of the
             recipe.

       When a  number  of  recipes  are  given,  some  recipes  may
       describe how to cook the ingredients of other recipes.  When
       ccooookk  is  asked  to  construct  a   particular   target   it
       automatically  determines  the  correct order to perform the
       recipe bodies to cook the requested target.

       CCooookk would not be especially  useful  if  you  had  to  give
       explicit  recipes  for how to cook every little thing.  As a
       result, ccooookk has the concept  of  an  _i_m_p_l_i_c_i_t  recipe.   An
       implicit  recipe  is  very  similar  to  an explicit recipe,
       except that the targets and ingredients of  the  recipe  are
       _p_a_t_t_e_r_n_s  to  be matched to file names, rather than explicit
       file names.  This means it is possible to  write  a  recipe,
       for  example  which constructs a files with a name ending in
       `..oo' from a file of the same name, but ending in `..cc' rather
       than `..oo'.

       In addition to recipes, ccooookk needs to know _w_h_e_n to construct
       targets from ingredients.  CCooookk has been designed to cook as
       little  as  possible.  "As little as possible" is determined
       by examining when each file  was  last  modified,  and  only
       constructing  targets  when  that  are  out of date with the
       ingredients.


       _3_._1_._1  _W_h_e_n _i_s _C_o_o_k _u_s_e_f_u_l_?
       From the above description, ccooookk may be described as a  tool
       for maintaining consistency of sets of files.





       Peter Miller                                          Page 6





       Cook                                              User Guide



       _3_._1_._2  _W_h_e_n _i_s _C_o_o_k _n_o_t _u_s_e_f_u_l_?
       Cook  is  not  useful for maintaining consistency of sets of
       things which are _w_i_t_h_i_n files and thus  ccooookk  is  unable  to
       determine when they were modified.  For example, ccooookk is not
       useful for maintaining consistency of sets of records within
       a database.


       _3_._2  _H_o_w _d_o _I _t_e_l_l _C_o_o_k _w_h_a_t _t_o _d_o_?

       Sets  of recipes are gathered together into cookbooks.  When
       ccooookk is executed  it  looks  for  a  cookbook  of  the  name
       _H_o_w_t_o_._c_o_o_k  in the current directory.  If you did not name a
       file to be constructed on the command line, the first target
       in the cookbook will be constructed.

       The  best  way  to  understand  how  to  write recipes is an
       example.  In this example, a  program, _p_r_o_g, is composed  of
       three  files:  _f_o_o_._c,  _b_a_r_._c  and  _b_a_z_._c.  To inform ccooookk of
       this, the cookbook
            #include "c"

            prog: foo.o bar.o baz.o
            {
                    cc -o prog foo.o bar.o baz.o;
            }
       is sufficient for _p_r_o_g to be constructed.

       This cookbook has two parts.  The line
            #include "c"
       tells ccooookk to refer to a system  cookbook  which  tells  it,
       among other things, how to construct a _s_o_m_e_t_h_i_n_g..oo file from
       a _s_o_m_e_t_h_i_n_g..cc file.

       The second part is a recipe.  The first line of this recipe
            prog: foo.o bar.o baz.o
                ...
       names the target, _p_r_o_g, and the  ingredients,  _f_o_o_._o,  _b_a_r_._o
       and _b_a_z_._o.

       The next three lines
            ...
            {
                    cc -o prog foo.o bar.o baz.o;
            }
       are  the  recipe  body,  which  consists  of  a single _c_c(1)
       command to be executed.  Recipe bodies are always  within  {{
       curly  braces  }},  and  commands always end with a semicolon
       (;;).

       Thus, to update _p_r_o_g after any of the source files have been
       edited, it is only necessary to issue the command
            cook prog
       This could be simplified further, because ccooookk will cook the


       Peter Miller                                          Page 7





       Cook                                              User Guide



       targets of the first recipe by default; in this case,  _p_r_o_g.

       The  power  of cook becomes more apparent when include files
       are considered.  If the files _f_o_o_._c and  _b_a_z_._c  include  the
       file  _d_e_f_s_._h,  this would automatically be detected by ccooookk.
       If _d_e_f_s_._h were to be  edited,  and  ccooookk  re-executed,  this
       would  cause  ccooookk  to  recompile  both _f_o_o_._c and _b_a_z_._c, and
       relink _p_r_o_g_.  The information about how  to  turn  ..cc  files
       into  ..oo  files  came  from the ``#include "c"'' line, which
       read in the C recipes distributed with Cook.

       _3_._2_._1  _T_h_e _c_o_m_m_o_n _p_r_o_g_r_a_m _c_a_s_e
       The above example may be simplified even  further.   If  the
       four  files  _f_o_o_._c, _b_a_r_._c, _b_a_z_._c and _d_e_f_s_._h all resided in a
       directory  with  a  path  of  _/_s_o_m_e_/_w_h_e_r_e_/_p_r_o_g,   then   the
       _H_o_w_t_o_._c_o_o_k file in that directory need only contain
            #include "c"
            #include "program"
       for  _p_r_o_g  to  be  cooked.   This  is  because the "program"
       cookbook looks for all  of  the  _s_o_m_e_t_h_i_n_g..cc  files  in  the
       current  directory, compiles them all, and links them into a
       program named after the current directory.

       The default target in the "program" cookbook is called  _a_l_l.
       The ingredient of _a_l_l is the program named after the current
       directory.  Two other targets are supplied by this cookbook:

       clean removes  all of the _s_o_m_e_t_h_i_n_g..oo files from the current
            directory.

       clobber removes  the  program  named   after   the   current
            directory,  and  also  removes  all  of the _s_o_m_e_t_h_i_n_g..oo
            files from the current directory.

       _3_._3  _C_r_e_a_t_i_n_g _a _C_o_o_k_b_o_o_k

       To use ccooookk you will usually need to define a  cookbook,  by
       creating  a  file,  usually called _H_o_w_t_o_._c_o_o_k in the current
       directory, with your favorite text editor.

       This file has  a  specific  format.   The  format  has  been
       designed  to  be  easy  to  learn, even for the casual user.
       Much of the power of ccooookk is  contained  in  how  it  works,
       without complicating the format of the cookbook.












       Peter Miller                                          Page 8





       Cook                                              User Guide



       Example   of  what  a  cookbook  looks  like  are  scattered
       throughout this document.   The  following  example  is  the
       entire cookbook for many programs, some quite large:
            #include "c"
            #include "yacc"
            #include "usr.local"
            #include "program"
       As you can see, even for many complex programs, the cookbook
       is remarkably simple.















































       Peter Miller                                          Page 9





       Cook                                              User Guide



       _4_.  _C_o_o_k_i_n_g _i_n _P_a_r_a_l_l_e_l

       Cook is able  to  use  the  dependency  information  in  the
       cookbook  to  schedule  more  than  one recipe body at once,
       where they are  independent.   In  large  projects  this  is
       almost always possible.

       Parallel  processing  is  of  most  use  on  multi-processor
       systems.  There are cases, however, when running two jobs at
       once  on a workstation can take advantage of disk or network
       latencies.

       Parallel processing requires more resources than the  simple
       case.   Because  more  commands  are  running,  more  CPU is
       required, but also more virtual memory  and  more  temporary
       file space.  You need to be sure that cooking in parallel is
       a sensible thing to be doing.

       _4_._1  _C_o_m_m_a_n_d _L_i_n_e _O_p_t_i_o_n

       The -PARallel option is used to tell Cook to run the  recipe
       bodies  in  parallel.   By  default, 4 jobs run in parallel.
       You may specify the number of jobs after  the  option  (_e_._g_.
       --par=2) if you wish.

       _4_._2  _C_o_o_k_b_o_o_k _V_a_r_i_a_b_l_e

       It  is  also  possible to set the number of jobs from within
       the cookbook by using the parallel_jobs variable.  This  can
       be  used  to  automate  the selection of the number of jobs,
       based on the current host name:
            if [not [defined parallel_jobs]] then
            {
                    host = [os node];
                    if [in [host] cerberus] then
                            parallel_jobs = 3;
                    else if [in [host] zaphod] then
                            parallel_jobs = 2;
                    else if [in [host] hydra] then
                            parallel_jobs = 8;
            }
       In this way, the number of jobs will  be  set  appropriately
       for  each  machine,  provided  the  number  of  jobs was not
       already set by the command line option.

       _4_._3  _R_e_c_i_p_e _W_r_i_t_i_n_g

       Most recipes run in  parallel  without  difficulty,  however
       some  will  require  special  treatment.  The problems arise
       from conflict for resources - usually temporary files.






       Peter Miller                                         Page 10





       Cook                                              User Guide



       The  simplest  example  of  this  is  _y_a_c_c(1).   The  output
       filenames are hard-coded, even when you write a more general
       recipe:
            %.c: %.y
                    single-thread yy.tab.c
            {
                    [yacc] [yacc_flags] %.y;
                    sed "'s/[yY][yY]/%_/g'" yy.tab.c > [target];
                    rm yy.tab.c;
            }
       Replacing the YY is a common method for  getting  more  than
       one  yacc  grammar into a program.  We run into trouble with
       the yy.tab.c file because every one  of  the  yacc  grammars
       will need to use the same temporary file name.

       The  single-thread  clause tells cook to find something else
       to do if it discovers that it wants do two of these  at  the
       same time.

       The temporary file name may not be so evident as in the yacc
       case.  The GNU Autoconf utilities use a number of  temporary
       files  in  the current directory, but none of them appear in
       the text of the recipes.
            %: %.in: config.status
                    single-thread conftest.subs
            {
                    CONFIG_FILES\=[target] CONFIG_HEADERS\= config.status;
            }
       It is common, if your project uses GNU Autoconf, to generate
       several files in this way.  Once the config.status script is
       produced, all of these files will  then  be  candidates  for
       cook  to generate - but they can only be done one at a time.

       Other resources, such as tape drives, can also be  described
       in the single-thread clause.  You can do this by device name
       (_e_._g_. /dev/rmt/0) or by some descriptive string.  The single
       threading  is  performed  by mutually exclusive string sets,
       not by inode.

       _4_._3_._1  _C_o_n_c_u_r_r_e_n_t _E_x_e_c_u_t_i_o_n _T_h_r_e_a_d_s
       Each recipe, when its  actions  are  executed,  is  executed
       within  an execution thread.  Execution threads share almost
       everything in common; this includes all  of  the  variables,
       the state of the ``set'' statement, the stat cache, _e_t_c.

       If  you  need  to  create  variable names, or temporary file
       names, which are unique to a  thread,  use  the  [thread-id]
       variable.   This variable has a unique value for the life of
       a thread.  No other concurrent thread  will  have  the  same
       value.

       Note,  however,  that  the  [thread-id]  values of completed
       threads will be re-used; this ensures that when it  is  used
       to  construct variable names, the variables will be re-used.


       Peter Miller                                         Page 11





       Cook                                              User Guide



       This prevents memory bloat when cooking large projects.

       _4_._4  _F_i_l_e _L_o_c_k_i_n_g

       The above discussion applies to utilities which  perform  no
       file  locking,  and  thus cannot detect or sequence multiple
       accesses to a resource.  Other programs, such as those which
       access  databases,  may  have  quite  capable  file  locking
       mechanisms and are able to manage multiple parallel  updates
       on  their  own,  obviating  the  need  for the single-thread
       clause.

       _4_._5  _V_i_r_t_u_a_l _M_a_c_h_i_n_e

       It is possible to simulate a parallel machine if you are  on
       a network.  Cook is able to distribute tasks to computers on
       a network, if it is given sufficient information.

       The first information Cook requires is the list of machines.
       This  is  done using the parallel_hosts variable.  NNoottee:: The
       tasks will be distributed amongst these machines independent
       of  the setting of the parallel_jobs variable.  _i_._e_. even if
       you are not doing parallel processing.
            parallel_hosts = larry curly moe;
       If you want to give one  machine  more  wieghting  than  the
       others (say, because it is twice as fast) you simply name it
       more than once.  Cook will use these  names  in  round-robin
       fashion.

       _4_._5_._1  _R_e_m_o_t_e _S_h_e_l_l _C_o_m_m_a_n_d
       Cook  uses  the Berkeley _r_s_h(1) command to invoke the remote
       command.  You can set the command, or the command  and  some
       options, using the parallel_rsh variable.  The default value
       is
            parallel_rsh = rsh;
       In  order  to  work  in  a  useful  way,  Cook  makes   some
       assumptions about your environment and your account:

          +o That your system administrators allow _r_s_h(1) to be used
            on your network.

          +o That your account name is  the  same  on  _a_l_l  machines
            (otherwise  not  even the rsh -l _l_o_g_i_n_-_n_a_m_e option will
            help).

          +o That the /etc/hosts.equiv file, or your ~/.rhosts file,
            is set on _a_l_l machines so that you don't need to give a
            password.

          +o That all of the necessary  files  and  directories  are
            mounted  in  exactly  the  same  place  on  all  of the
            machines; and that they  are  _t_h_e  _s_a_m_e  _f_i_l_e_s  on  all
            machines,  via  NFS  or similar.  Automounters can make
            this especially messy.


       Peter Miller                                         Page 12





       Cook                                              User Guide



          +o That your account start-up scripts  set  the  necessary
            environment settings, _e_._g_. command search PATH, without
            any intervention required.

          +o That all of the machines are of the same  architecture,
            or that the architecture doesn't matter.

          +o That  the  system time is synchronised on all machines,
            using _r_d_a_t_e(1) from _c_r_o_n(8), or using NTP, or  similar.

       _4_._5_._2  _L_i_m_i_t_a_t_i_o_n_s
       There  are some inherent limitations in the _r_s_h(1) protocol.

          +o Your current  environment  variable  settings  are  not
            transferred  across.  Neither are _u_l_i_m_i_t settings, _e_t_c.
            If any are important, you need to write the cookbook to
            explicitly replicate them.

          +o The  exit  status of the remote command is not reported
            in the exit status of the _r_s_h(1) command1.   There  are
            internal  contortions  used  by Cook to obtain the exit
            status; error about mysteriously  named  files  usually
            indicate  that  one or more of the above assumptions is
            being broken.

       _4_._5_._3  _S_e_c_u_r_e _S_h_e_l_l
       It is possible to use the  Secure  Shell  (ssh)  instead  of
       Remote  Shell  (rsh).   This  gives you fully authenticated,
       fully encrypted sessions, both over your intranet  and  even
       over   the   Internet.   Once  you  have  it  installed  and
       configured correctly, you simply replace the _r_s_h command  in
       the above examples with the _s_s_h command.

       This is accomplised by setting
            parallel_rsh = "ssh";
       Somewhere near the top of your cookbook.

       _4_._5_._4  _H_o_s_t _B_i_n_d_i_n_g
       In  some  cases, such as licensing conditions, some commands
       will only run on  a  limited  set  of  hosts.   Rather  than
       perform  all commands on those hosts, it is possible to bind
       recipes to  specific  hosts.   This  binding  overrides  the
       parallel_hosts variable.
            %.c: %.esql
                    host-binding shylock
            {
                    esql %.esql > [target];
            }
       This example says that the embedded SQL preprocessor is only

       ____________________

       1. The Berkeley sources certainly don't contain code  to  do
          this.    Do   any   other  vendors  have  a  more  useful
          implementation?

       Peter Miller                                         Page 13





       Cook                                              User Guide



       to  be  run  on  the  database  server  called  ``shylock'',
       probably  due  to usurious licensing fees.  However, you may
       want to perform your other development  activities  on  more
       lightly  loaded  machines;  this clause only applies to this
       one recipe, other recipes behave as normal.

       The host-binding clause may have more than one  host  named,
       and  they  will  be  used in round-robin fashion.  This is a
       recipe-level variant of the parallel_hosts variable.

       The  host-binding  clause  will  apply  independent  of  the
       setting  of  the  settings  parallel_jobs and parallel_hosts
       variables.

       The recipe level host-binding overrides the  cookbook  level
       parallel_hosts when determining which remote hosts should be
       used.

       If the list of hosts given to  the  host-binding  clause  is
       empty,  the local host will be used (normal recipe execution
       will occur).

       If you need to include the local host in  the  round  robin,
       use localhost or [os node], however this will behave exactly
       the same as for a remote host.   You  should  also  consider
       hard  coding the name, that way you get the same behavior no
       mater which of the machines  in  the  rond  robin  the  Cook
       command is executed on.

       _4_._5_._5  _L_o_a_d _B_a_l_a_n_c_i_n_g
       It   is   possible  to  use  _h_o_s_t_-_b_i_n_d_i_n_g  to  perform  load
       balancing.  This is accomplished by using _r_u_p(1) to discover
       which  hosts are least busy, and then using this information
       to invoke the system's _r_s_h(1).

       This may be accomplished by using
            parallel_rsh = "cook_rsh";
       somewhere near the top of your cookbook (or _c_o_o_k___r_s_h _-_s  for
       secure  shell).  You then give classes of hosts to the _h_o_s_t_-
       _b_i_n_d_i_n_g clause of the recipes,  rather  than  specific  host
       names.   See  _c_o_o_k___r_s_h(1) for more information about setting
       up classes of hosts.

       If you still need  to  give  specific  host  names  to  some
       recipes, _c_o_o_k___r_s_h(1) will cope with this, too.

       _4_._6  _V_i_r_t_u_a_l _M_a_c_h_i_n_e_, _R_e_v_i_s_i_t_e_d

       It  is  also possible to have Cook run multiple processes in
       parallel without having to know what machines are available.
       This  method  puts  control  of the network resources in the
       hands of an  external  program,  one  example  of  which  is
       cook_rsh, distributed with Cook.



       Peter Miller                                         Page 14





       Cook                                              User Guide



       Once you have such a virtual network defined it becomes very
       easy  to  build   projects   for   multiple   platforms   or
       architectures  in  the  same  build.   It also allows easily
       adding new machines, or disabling machines for  maintenance.
       The  virtual  network  can  be  changed  at any time without
       disturbing ongoing development.

       The following examples will have the form allowing  multiple
       architecture builds, but of course they will work for single
       architecture as well.

       _4_._6_._1  _c_o_o_k___r_s_h
       The  cook_rsh  system  is  just  one  way  of  defining  the
       capabilities  of  a  given  network  in  a way that a single
       program can make the best choice of machine for a given job.
       It  does  so in a way that is reliable and does a decent job
       of balancing loads  across  available  machines,  even  with
       multiple developers doing builds at the same time.

       Each  job  that requested via cook_rsh picks the appropriate
       machine from those able to do the job  at  that  instant  in
       time.   In  contrast to parallel_hosts or host-binding hostA
       hostB etc, it does not work from a list which was current at
       the  time  a  cook  process  was  started.   Thus it is less
       vulnerable to machines going off line or becoming overloaded
       as time passes.

       Currently  cook_rsh uses rsh to actually execute the job, so
       requires the same network setup.  The next version  may  use
       multicast instead for even finer control and reliability.

       There  are  minor  differences  in the setup to use cook_rsh
       control.  The first is that Cook no longer requires  a  list
       of  machines.  It is not necessary to set the parallel_hosts
       variable.  The parallel_rsh variable is set as:
            parallel_rsh = cook_rsh -v;
       The -v option produces information as to  what  machine  was
       actually picked for each job.

       _4_._6_._2  _H_o_s_t _B_i_n_d_i_n_g
       All  recipe bodies which should run in parallel need a host-
       binding setting.  Rather than list the hosts to be  used  we
       form  a  name  which  is  used  by  cook_rsh  to  select  an
       appropriate machine.  This name may include an  architecture
       component and a operation component.
            %1/%.o: %.c
                    host-binding %1_C
            {
                    [%1_cc] -o [target] -c [resolve %.c];
            }

            %1/%2: [addprefix %1/ [%2_objs]]
                    host-binding %1_L
            {


       Peter Miller                                         Page 15





       Cook                                              User Guide



                    [%1_ld] -o [target] [resolve [need]];
            }
       This   example   says   that  the  compiles  for  a  certain
       architecture should take place on any machine designated  as
       a  compile  host  for  that  architecture.  And linking jobs
       should go to machines designated as a  link  host  for  that
       architecture.   Of course the same machine could probably do
       both jobs, but you get to define it  as  you  see  fit,  and
       change  the  designations  from  moment  to moment.  Current
       designations per architecture are:

       _C   Compile   (Compile source code)
       _L   Link      (link binary programs)
       _T   Test      (run automatic tests)
       _B   Build     (including cooking, or generic jobs)
       And others may be added if necessary by simple extension.

       _4_._6_._3  _A_d_m_i_n_i_s_t_r_a_t_i_o_n _o_f _c_o_o_k___r_s_h
       The definition of the virtual network used  by  cook_rsh  is
       contained in just a two configuration files.  One file lists
       designations,  and  lists   machines   belonging   to   each
       designation.   The  other  is  an  eexxcclluuddee file, which lists
       machines which should not be used for whatever reason.

       The designations file may be created by hand if desired  but
       a  utility  called  rate_hosts is provided that can generate
       the host_lists.pl file, possibly after being customized  for
       the particular requirements of a given environment.

       The  exclusion  file  lists  machines  that  should never be
       selected.  The exclusion file can be edited at any time  and
       adding  a  machine  will prevent any further jobs from going
       its way.  Removing the name will again  allow  selection  of
       that  machine.   How  soon a job actually goes there depends
       greatly on the network utilization.  The exclude_hosts  file
       contains  machine  names  and optional comments.  An example
       exclude_hosts file might contain:
            # list of hosts to exclude from arch_hosts lists
            # for whatever reason.
            monolith        # not a development machine - the ftp host
            namshub         # developer test station
            tiamat          # unreliable configuration
            locutus         # Being upgraded
       This is handy for maintenance on machines.  If a  particular
       machine  needs to be brought down you simply add its name to
       the exclusion file.  Checking its  process  list  will  tell
       when any currently running remove jobs are done.  After that
       it can safely be brought down without affecting  any  active
       builds.







       Peter Miller                                         Page 16





       Cook                                              User Guide



       _5_.  _I_n_c_l_u_d_e _F_i_l_e _D_e_p_e_n_d_e_n_c_i_e_s

       A significant factor in a cookbook accurately describing the
       dependencies in a program are the include file dependencies.
       There  are  three methods for doing this in Cook.  The first
       is easily understandable but is too slow  to  use  on  large
       projects,  the  second is a little harder to understand, but
       works well for large projects.  The third method  is  rather
       convoluted,  but works well for projects with many thousands
       of source  files  and  multiple  simultaneous  architectures
       built within the same source tree.

       The  recipes  here  are merely examples and starting points;
       you will almost certainly need to enhance them to  suit  the
       needs  of  your  projects.   Areas  you will need to address
       include (a) the existence of cc -I_p_a_t_h options, (b) the  use
       of  search_list variable and the [resolve] function, and (c)
       heterogeneous development.  The  techniques  also  apply  to
       other  languages, such as Fortran, Pascal and Roff, but each
       requires a language-specific include scanning program2.

       _5_._1  _T_h_e _M_a_n_u_a_l _M_e_t_h_o_d

       Well,   actually  there  are  four  methods,  if  you  count
       maintaining the dependencies manually.  This has the serious
       defect  that  humans  tend to _f_o_r_g_e_t to update the cookbook.
       On a large project not all developers are familiar with  the
       workings  of  Cook,  and  so they shy away from updating the
       cookbook.  By finding ways to  automate  include  dependency
       processing,  we reduce the risk that a developer will forget
       to update the cookbook, and we  reduce  the  risk  that  the
       cookbook's dependency information is out-of-date.

       Automatic  include  dependency  methods described below have
       flaws, and can never replace a  human  for  flexibility  and
       domain  knowledge.   On  the  other hand, humans have better
       things to do with their time than grope  files  for  include
       file dependencies (like write neat software).

       _5_._2  _D_e_b_u_g_g_i_n_g _C_o_o_k_b_o_o_k_s

       Before  we  proceed  further, it is worth spending some time
       covering some of the methods for  debugging  your  cookbook,
       because small mistakes in implementing the methods below can
       become quite difficult to locate.

       _5_._2_._1  _C_o_m_m_a_n_d _L_o_c_a_t_i_o_n_s
       Usually Cook will echo all the commands  it  executes,  just
       before executing them.  If you add the line
            set tell-position;

       ____________________

       2. The _c___i_n_c_l program understands Roff, you just need to use
          the -r option.

       Peter Miller                                         Page 17





       Cook                                              User Guide



       near  the  top  of your cookbook, Cook will add the filename
       and line number within  the  cookbook  to  each  command  it
       echoes.   This  can  be  useful in figuring out which recipe
       Cook actually chose to execute.

       _5_._2_._2  _P_r_i_n_t_i_n_g _S_t_u_f_f
       Often you will want to have Cook  print  various  pieces  of
       information.   The  wrong  way  to do it is with the shell's
       "echo" command
            echo variable "=" [variable];
       because  this  invokes  another  process  (which  can   make
       debugging  parallel  cookbooks  harder)  and  because of the
       optional _d_a_t_a _._._. _d_a_t_a_e_n_d which can follow commands (see the
       command  statement  in the language definition, below).  The
       correct method is to call the "print" function, like this
            function print [__FILE__]: [__LINE__]: variable "=" [variable];
       Note the use of the __FILE__ and  __LINE__  builtins,  which
       provide you with cookbook position information.

       _5_._2_._3  _T_r_i_g_g_e_r _I_n_g_r_e_d_i_e_n_t_s
       Another useful piece of information is the ingredients which
       caused  Cook  to  invoke  a  particular  recipe  body.   The
       following function
            function say-why =
            {
                    if [count [@1]] then
                            @1 = [@1];
                    if [count [@2]] then
                            @2 = [@2];
                    local tt = [target];
                    if [defined targets] then
                            tt = [targets];
                    local t = ;
                    if [in [count [younger]] 0 1 2 3] then
                    {
                            function print [@1] [@2]
                                    Building [target]
                                    because of [younger];
                    }
                    else
                    {
                            function print [@1] [@2]
                                    Building [target] because of
                                    [wordlist 1 3 [younger]] et al;
                    }
            }
       can be inserted at the beginning of a recipe
            %.o: %.c
            {
                    function say-why [__FILE__] [__LINE__];
                    cc -c %.c;
            }
       to  say  why the recipe was invoked.  This will even include
       dependencies automatically determined by all of the  methods


       Peter Miller                                         Page 18





       Cook                                              User Guide



       which follow, not just those named on the right-hand-side of
       the recipe itself.

       _5_._3  _T_o_o_l_s

       All  of  the  automated  include  file  dependency   methods
       described  below  use  the _c___i_n_c_l(1) program included in the
       Cook distribution.  It has a number of options tailored  for
       use  with  Cook.   For  exact  information  about the _c___i_n_c_l
       command, consult the on-line _m_a_n(1) system (it  should  have
       been installed) or the Cook Reference Manual.

       Other  tools  are  available.   The  commonest is to use the
       gcc-M option, which produces a list of include files on  the
       standard  output.   Because the gcc-M output is aimed at GNU
       Make, you will need an _a_w_k(1) or _s_e_d(1)  script  to  massage
       the output into a format suitable for Cook.

       _5_._4  _T_h_e _S_m_a_l_l _M_e_t_h_o_d

       The  easiest  way to determine a file's include dependencies
       is within the recipe's ingredients.
            %.o: %.c: [collect c_incl -api %.c]
            {
                    cc -c %.c;
            }

       Note the second colon - the _s_e_c_o_n_d set of  dependencies  are
       only  evaluated after Cook has chosen to activate the recipe
       (based on the first set).  This does not guarantee that  the
       file  exists  yet  (it  may  have  to be generated by _l_e_x or
       _y_a_c_c), which is why the  --Absent-Program-Ignore  option  is
       required.

       This  method  has  the  advantage  of simplicity.  It uses a
       single recipe which reads the way recipes usually read,  and
       does not contain any unusual constructs.

       There  are two problems with this method.  The first is that
       it doesn't scale well.  When there are  only  a  few  source
       files,  the processing burden of running _c___i_n_c_l for every ..cc
       file every time Cook is invoked is hardly  noticeable.   The
       _c___i_n_c_l  program  caches the results of its scans, so that is
       can minimize the length of time taken, and this does help  a
       little.   However  projects  with  hundreds  or thousands of
       files find  even  the  cached  performance  an  unreasonable
       burden;  it is constantly re-calculating something which has
       not changed from one run to the next.

       The second problem is that the _c___i_n_c_l program  is  run  when
       the  dependency  graph  is being built, not when it is being
       walked.  This means that the ..cc file (or  a  subordinate  ..hh
       file) may have been out-of-date at the time.  When the graph
       is walked, it will have been regenerated, and the  two  sets


       Peter Miller                                         Page 19





       Cook                                              User Guide



       of  include  files,  those  determined  by  _c___i_n_c_l  at graph
       building time, and those seen by _c_c at graph  walking  time,
       may not agree - which may result in compile-time errors.

       _5_._5  _T_h_e _L_a_r_g_e _M_e_t_h_o_d

       For  projects  with large numbers of files, hundreds or even
       thousands, it is necessary to re-calculate the include  file
       dependencies  only  when a ..cc file changes, or a subordinate
       ..hh file.   Ideally,  Cook  should  access  this  information
       directly,  rather  than running a program to determine it or
       to fetch it.

       The first task is  to  move  the  information  which  _c___i_n_c_l
       caches into a format that Cook can access directly; Cook can
       then read in this information as it scans the cookbook.   By
       making  a  separate ``dependency'' file for each ..cc file, we
       can use existing Cook mechanisms to  describe  how  to  keep
       this file up-to-date.

       The dependency file is generated and maintained as follows:
            %.c.d: %.c
            {
                    c_incl --no-cache %.c
                            "--prefix='%.o "[target]": %.c'"
                            "--suffix='set nodefault;'"
                            -o [target];
            }

       This  recipe generates a file which contains a mini-cookbook
       describing  the  ingredients  of  the  _o_b_j_e_c_t   file.    The
       dependencies  are in terms of the object file because if any
       of the ..hh files change, it is the object file which is  out-
       of-date,  not the ..cc file.  The mini-cookbook itself is also
       described, so that if any of the source  files  change,  the
       mini-cookbook can be brought up-to-date again.

       The  recipe  for the object file is less complicated than in
       the previous section, because the mini-cookbooks  supplement
       it:
            %.o: %.c
            {
                    cc -c %.c;
            }

       The  only thing missing is how to get the information in the
       mini-cookbooks into the main cookbook.  This is done with an
       include directive in the cookbook itself, but a special form
       of it.  The names of the mini-cookbooks  can  be  determined
       the  same  way  as  the  names of the object files, and this
       allows the cookbook fragments such as the  following  to  be
       written:
            object_files = [fromto %.c %.o [source_files]];
            dependency_files = [fromto %.c %.c.d [source_files]];


       Peter Miller                                         Page 20





       Cook                                              User Guide



            #include-cooked [dependency_files]

       The  #include-cooked  directive  says  to  include the named
       files (there may be more than one) if the file exist.   Once
       the cookbook (and its includes) have been read in, the files
       included with this directive are checked to see if they  are
       up-to-date.   If  they are not, then they are re-cooked, and
       then Cook starts  over  again;  this  time  with  up-to-date
       include dependencies.

       The  advantage  of  the  method  is that if the source files
       don't   change,   the   dependency   information   is    not
       recalculated, this can result in significant savings.  Also,
       no processes are invoked if nothing has changed, Cook  reads
       the   information   directly.    Because   file   opens  are
       significantly cheaper than process invocations, this results
       in a significant performance improvement.

       The  disadvantage  of  this  method  is that it is harder to
       describe and harder to implement.  To  the  uninitiated  the
       cookbook looks incomplete and overly complex.

       Another  problem is that if you delete an include file, Cook
       will complain that it is unable  to  derive  the  dependency
       file because the include file is not present.  Simply delete
       the dependency file and start again.  To avoid the  problem,
       remove  references  to  include  files, and re-build, before
       deleting the include files.  This problem is seen from  time
       to  time,  but  does  not  present  a huge problem in normal
       practice.

       _5_._6  _T_h_e _C_a_s_c_a_d_e _M_e_t_h_o_d

       When large numbers of files are involved, it  becomes  clear
       that  the  more  popular  include  files  are  being scanned
       repeatedly.  This can be un-necessarily time-consuming  when
       a  popular  include file is touched, as the dependency files
       of all .c files which reference it, even indirectly, must be
       re-calculated.

       There  is  also a problem when you are attempting to perform
       heterogenous builds for multiple architectures  out  of  the
       same  sources.   This  is  typically  done  by inserting the
       architecture name into the object file path as a  directory.
       This   presents  another  problem:  nominating  all  of  the
       architectures  on  the  left-hand-side  of  the  regenerated
       dependency recipes.  Especially if you add another one after
       the fact - now all the existing  dependency  files  must  be
       recalculated, merely to add the new architecture.

       An  alternative  is  to  scan  each  of the source files and
       include  files  once,  and  request  cook  to  combine  them
       together at build time, rather than at dependence scan time.
       This is done using cascade recipes.  These recipes  nominate


       Peter Miller                                         Page 21





       Cook                                              User Guide



       additional  ingredients (on their right-hand-size) if any of
       the files on their left-hand-size appears in an  ingredients
       list.
            cascade foo.c = bar.h;
       This  recipe  says  that  any  recipe which has _f_o_o_._c for an
       ingredient, also has _b_a_r_._h for an ingredient.

       This takes care of the heterogeneous case, because while the
       recipes remain specified in a simple manner, _v_i_z_:
            %1/%0%.o: %0%.c
            {
                    %1-gcc -o [target] -c %0%.c;
            }
       Any and all of them which compile _f_o_o_._c will depend on _b_a_r_._h
       from the cascade recipe.  (This example assumes that you are
       using  _g_c_c(1)  in  the usual way, and that your architecture
       names match the GNU target names.)

       The dependency files are generated and  maintained  in  much
       the same way as before, except that you need two: one for .c
       files and one for .h files:
            %0%.c.d: %0%.c
                    set no-cascade
            {
                    c_incl --no-cache --no-recurs %0%.c
                            "--prefix='cascade %0%.c ='"
                            "--suffix=';'"
                            -o [target];
            }
            %0%.h.d: %0%.h
                    set no-cascade
            {
                    c_incl --no-cache --no-recurs %0%.h
                            "--prefix='cascade %0%.h ='"
                            "--suffix=';'"
                            -o [target];
            }
       You will also need to add the .h.d files  to  the  #include-
       cooked  lines,  to  ensure they are generated.  If there are
       any generated .c or .h  files,  you  will  need  to  mention
       these, too.

       _5_._7  _D_e_p_e_n_d_e_n_c_i_e_s _o_n _D_e_r_i_v_e_d _F_i_l_e_s

       If   the   relationship  between  a  target  and  a  derived
       ingredient appears only in a derived cookbook, it is  likely
       that  a  clean build (solely from primary source files) will
       fail.  It is recommended that relationships such as this  be
       placed  in  a  primary source cookbook.  Cook looks for such
       dependencies, and will warn you about them.

       An example of this is commonly seen when using the -d option
       with  _y_a_c_c(1).  If you have a separate lexical analyzer (the
       usual reason for using -d)  it  will  need  to  include  the


       Peter Miller                                         Page 22





       Cook                                              User Guide



       generated token definition file.

       When you first add the _y_a_c_c(1) grammar definition, Cook will
       generate both the  .c  and  .h  file  from  the  usual  yacc
       recipes.   It  is  only later, when you have cleaned out all
       derived files (including the dependency files) that you  may
       have  problems.   Where  is  it  recorded that Cook needs to
       regenerate the token definition file before it can determine
       the  include  dependencies  of  the lexical analyzer?  (They
       were in a .d file which was ``cleaned'' away.)

       Cook will  detect  this  situation  at  the  first  possible
       moment,  and warn you.  But placing the dependency in a non-
       derived cookbook (_e_._g_.   Howto.cook)  the  warning  will  go
       away, and you will be able to do reliable clean builds.

       If you are convinced that Cook is _a_l_w_a_y_s wrong in your case,
       it is possible to suppress this warning.  Place the line
            set no-include-cooked-warning;
       in your main cookbook, and the warning will not be issued.

       Suppressing the warning could lead to problems.  It is often
       better to add the ingredients recipe given in the warning to
       the cookbook, even if  you  think  it  is  redundant.   This
       disables  a  single instance of the warning, rather than all
       of them - subsequent _v_a_l_i_d instances will still be reported.
       (Implicit  ingredients  recipes,  rather than explicit ones,
       are a useful alternative if you have a consistent  pattern.)

       _5_._8  _R_e_n_a_m_i_n_g _I_n_c_l_u_d_e _F_i_l_e_s

       A  consistent  problem when you have automatically generated
       include dependencies is that when you move an include  file,
       Cook complains that a required ingredient does not exist.

       The  easiest  way to avoid this is to do a few things before
       you build again after moving the include file.

          +o Move the include file to the new name.

          +o Where the include file was _f_r_o_m, put a file  containing
            the line
                 #error "I'm not here"
            to  make  Cook  happy  (the ingredient will exist), but
            also have the compiler generate an error if you miss  a
            reference to it.

          +o Edit all the references to the old include file name to
            reference the new name.  Don't worry if you miss one or
            two, the previous step will catch it.

          +o Rebuild  the  program.   Cook  will  automatically  re-
            calculate all  of  the  include  dependences  and  then
            recompile.


       Peter Miller                                         Page 23





       Cook                                              User Guide



          +o If  you missed one of the include file references, Cook
            will  not  complain,  but  the  compiler  will.   (This
            assumes   you   are   using  whole-project  builds,  as
            described in the _L_a_r_g_e _P_r_o_j_e_c_t_s chapter.)

          +o Once the program builds cleanly, remove  the  fake  old
            include  file,  because you know for certain that there
            are no longer any references.
















































       Peter Miller                                         Page 24





       Cook                                              User Guide



       _6_.  _B_u_i_l_d_i_n_g _L_a_r_g_e _P_r_o_j_e_c_t_s

       This chapter covers some of the  issues you may come  across
       in building large projects.  It gives a skeleton for how you
       could use Cook to build a medium-to-large projects, and even
       covers  some heterogenous build issues.  It is expected that
       you will use this  chapter  as  a  guide;  your  development
       environment,  and the shape of each individual project, mean
       that you will probably change this to suit your own needs.

       The material in this chapter uses  many,  many  features  of
       Cook.   If  you  are not familiar with Cook, you may want to
       read the rest of this User Guide  to  get  a  good  idea  of
       Cook's  features and capabilities.  Even if you are familiar
       with Cook, you may need to refer to the language  guide  and
       built-in function descriptions from time to time.

       _6_._1  _W_h_o_l_e _P_r_o_j_e_c_t _B_u_i_l_d

       The skeleton given here builds the whole project as a single
       Cook invocation, even when  the  project  consists  of  tens
       thousands of individual source files.  This is distinct from
       a build process which has Cook recursively  invoking  itself
       in  deeper  directories,  or  a  shell script doing much the
       same.  Some of the advantages of doing whole project  builds
       will  be  discussed  in  a  later  section.   For  now it is
       sufficient to say that experience has shown repeatedly  that
       this method does scale to significant projects.

       The first thing about a single build pass is that it happens
       relative to a single fixed place.  The logical place is  the
       top of the project source tree3.  This works well  with  the
       _s_e_a_r_c_h___l_i_s_t functionality, mentioned below, which simplifies
       the structure of private work areas.

       _6_._1_._1  _P_r_o_j_e_c_t _D_i_r_e_c_t_o_r_y _S_t_r_u_c_t_u_r_e
       In the examples use in this chapter, the following directory
       structure is assumed:













       ____________________

       3. If   you   ever  want  to  use  Aegis  for  configuration
          management, this is what Aegis expects.

       Peter Miller                                         Page 25





       Cook                                              User Guide



                             +++
                             ++_P+_r|_o_j_e_c_t
                              ++++Hloiwbtroa.rcyook
                              |++ +|_s_o_u_r_c_e_1.c
                              | + +|_s_o_u_r_c_e_2.c
                              | + +|_e_t_c_._._.
                              ++++i+nclude
                              | + +|_a_p_i_1.h
                              | + +|_a_p_i_2.h
                              | + +|_e_t_c_._._.
                              ++++_p+_r_o_g_r_a_m_1
                              | + +|_s_o_u_r_c_e_3.c
                              | + +|_s_o_u_r_c_e_4.c
                              |+++-_e_t_c_._._.
                              +++_p+_r|_o_g_r_a_m_2
                                + +|_s_o_u_r_c_e_5.c
                                + +|_s_e_o_t_u_c_r_._c_._e_._6.c
                                  -

       Below  the  project  directory is a library directory, which
       contains functions common  to  all  of  the  programs.   All
       source  files  in  this  directory  are  to be compiled, and
       linked into a library.  When the programs are  linked,  they
       will all reference this library.

       Next  to  the  library  directory  is the include directory.
       This describes interfaces and data shared  by  the  project.
       Information which is private to the internals of the library
       or a programs belongs  there,  not  in  the  shared  include
       space.

       The  rest of the directories below the project directory are
       programs to be built.  The sources files in each are  to  be
       compiled  and  linked,  together with the common library, to
       form the programs.  The name of the program  will  be  taken
       from the directory.

       This is a common enough picture, repeated for many projects.
       Your individual projects may vary in the  details;  you  may
       have  more  directory levels below the library directory, or
       all  of  your  programs  may  be  below  a  single   command
       directory.   With  simple  changes  to the examples given in
       this chapter, you will be able to cope with just  about  any
       project structure.

       _6_._1_._2  _F_i_l_e _M_a_n_i_f_e_s_t
       There  are many ways of discovering the source files you are
       working with.  Many  configuration  management  systems  are
       able  to  give you a list of them.  For example, if you were
       using Aegis, you would say
            change_files =
                    [collect aegis -l cf -terse -p [project] -c [change]];
            project_files =
                    [collect aegis -l pf -terse -p [project] -c [change]];


       Peter Miller                                         Page 26





       Cook                                              User Guide



            manifest =
                    [sort [change_files] [project_files]];

       If you were using RCS, you could find all of the RCS  files,
       and reconstruct the original filenames from them, _v_i_z_:
            manifest =
                    [fromto ./%0RCS/%,v %0%
                            [collect find .  -path "*/RCS/*,v" -print]
                    ];

       Or you could simply scan the directory tree:
            manifest =
                    [fromto ./%0% %0%
                            [collect find .  ! -type d -print]
                    ];
       This  is  will  find  too much, but what follows will not be
       altered by this.  If you want to get more advanced, however,
       it helps to have an accurate primary source file manifest.

       _6_._1_._3  _C_o_m_p_i_l_i_n_g _C _S_o_u_r_c_e_s
       Recalling that the build will take place from the top of the
       source tree, this means that there it is going to have to be
       directory   components  in  the  filenames  in  the  command
       executed by Cook, and in the recipes Cook is to use.

       This chapter uses C examples, but the same  techniques  work
       just  as will with Fortran or Groff, or anything else.  Most
       of it maps  directly;  you  may  need  to  adjust  for  your
       specific compiler behavior.

       This  chapter  starts  with  the  lowest level of building a
       project, the individual source  files,  and  works  its  way
       upwards,  building  on the examples until the whole project,
       including the library and  all  programs  are  linked  in  a
       single pass.

       So, when cooking C sources, you need recipes of the form
            cc = gcc;
            cc_flags = -g -Wall -O;

            %0%.o: %0%.c
            {
                    [cc] [cc_flags] -c %0%.c
                            -o [target];
            }
       The  ``%0''  part  of  the  patterns  matches  zero  or more
       directory parts.  If your compiler insists  on  putting  the
       output  (.o)  file into the current directory (the top level
       one) you will need to move it, after:
            %0%.o: %0%.c
            {
                    [cc] [cc_flags] -c %0%.c;
                    mv %.o [target];
            }


       Peter Miller                                         Page 27





       Cook                                              User Guide



       But, most existing sources will be  assuming  that  most  of
       their  include files are in the same directory as the source
       files.  We need include options to indicate this.   This  is
       most easily done by using more pattern elements
            %1/%0%.o: %1/%0%.c
            {
                    [cc] [cc_flags] -I%1 -c %0%.c
                            -o [target];
            }
       Or by using the dirname of the source file
            %0%.o: %0%.c
            {
                    [cc] [cc_flags] -I[dirname %0%.c] -c %0%.c
                            -o [target];
            }
       For  structures  more  than  2  directories  deep, these two
       produce  different  options.   Depending  on  your   project
       structure,  if  you have deep directories, one will probably
       be more suitable than the other.  One elegant use for deeper
       directory  structures  is  to  reflect  the  C++ inheritance
       hierarchy directly in the directory hierarchy.

       The simple [cc_flags]  variable  is  often  not  sufficient.
       Instead,  you  may want to replace it with [variable_by_path
       "cc_flags" %0%.c] which will look for several variables (all
       prefixed  with  "cc_flags")  based on the name of the source
       file.  See the _F_u_n_c_t_i_o_n_s _L_i_b_r_a_r_y chapter for  a  description
       of this function.

       The  common  include  file  will  also  need to be searched.
       Because of where the command is issued, it is rather  simple
       to add the include directory, _v_i_z_:
            %0%.o: %0%.c
            {
                    [cc] [cc_flags]
                            -I[dirname %0%.c] -Iinclude
                            -c %0%.c -o [target];
            }
       It  is  important to note that all of these recipes, and the
       commands they execute, are independent of  the  location  of
       the  source  file.  It is possible to customize the cc-flags
       used, based on  the  target  file,  or  even  the  directory
       containing  the file, without compromising the generality of
       the recipe4.

       _6_._1_._4  _T_r_a_c_k_i_n_g _I_n_c_l_u_d_e _D_e_p_e_n_d_e_n_c_i_e_s
       When it comes to tracking include dependencies using _c___i_n_c_l,
       you need to remember, again, that the Cook  happens  from  a
       single place.  All of the recipes that _c___i_n_c_l writes for you
       must be _r_e_l_a_t_i_v_e _t_o _t_h_a_t _p_l_a_c_e.


       ____________________

       4. Hint: use a function, and pass [target] as the  argument.

       Peter Miller                                         Page 28





       Cook                                              User Guide



       Continuing our  example,  and  assuming  we  are  using  the
       cascade include method described in the previous chapter, we
       need include dependency files which look similar to
            cascade _p_r_o_g_r_a_m_1/_s_o_u_r_c_e_3.c =
            include/_a_p_i_1.h
            ;
       Working backwards, we need to  create  the  dependency  file
       using the following recipe:
            %0%.c.d: %0%.c
                    set nocascade
            {
                    c_incl -nc -ns -nrec
                            -I[dirname %0%.c] -Iinclude
                            %0%.c
                            -prefix "'cascade %0%.c ='"
                            -suffix "';'"
                            -o [target];
            }
       For  other source languages, you will need to use the _c___i_n_c_l
       _-_-_l_a_n_g_u_a_g_e option.

       The dependency files need to be included in the magic way so
       that  Cook  will  build  them again if they are out of date.
       This method needs the source file  manifest  to  know  their
       names.
            dep-files =
                    [addsuffix .d
                            [match_mask %0%.c [manifest] ]
                            [match_mask %0%.h [manifest] ]
                    ];
            #include-cooked [dep-files]
       These  files  will  only be re-calculated if they are out of
       date; they are small  and  often  zero-length,  and  so  are
       usually  very  quick  to  read, adding little to the time it
       takes to read the cookbook.

       Notice that adding a  new  source  file  will  automatically
       cause  it  to  be  scanned for include dependencies, without
       modification to the cookbook.

       _6_._1_._5  _L_i_n_k_i_n_g _L_i_b_r_a_r_i_e_s
       To  link  libraries  with  a  generic  recipe,  you  need  a
       generalized  way  of  specifying  their  contents.  A little
       trickery with constructed variable names does the job:
            %/lib%.a: [[target]_obj]
                    set unlink
            {
                    ar cq [target] [[target]_obj];
            }
       The right-hand-side of recipes has late binding, and we  use
       the  name  of the target to tell us the name of the variable
       which  holds  all  of  the  object  files.   Assigning  this
       variable  looks  bizarre,  but  it looks more logical as you
       have more and more of them...


       Peter Miller                                         Page 29





       Cook                                              User Guide



            library/liblibrary.a_obj =
                    [fromto %0%.c %0%.o
                            [match_mask "library/%0%.c" [manifest] ]
                    ];
       The great thing about this construct is that you can build a
       loop,  using  Cook's loop statement, that assigns a variable
       for each of your libraries, if you have more than one.

       Notice  that  adding  a  new  library   source   file   will
       automatically  cause  it  to  be  compiled into the library,
       without modification to the cookbook.

       _6_._1_._6  _L_i_n_k_i_n_g _C_o_m_m_a_n_d_s
       We'll use a similar trick for each of the programs you  want
       to link...  First the link line
            bin/%: [[target]_obj]
                    set mkdir
            {
                    [cc] -o [target] [[target]_obj];
            }
       Then  the  objects  variable.   Note  how  we  add a library
       _f_i_l_e_n_a_m_e here, this will still only use the library portions
       actually  referenced,  not  the  whole  library, so it won't
       bloat your programs.
            bin/_p_r_o_g_r_a_m_obj =
                    [fromto %0%.c %0%.o
                            [match_mask _p_r_o_g_r_a_m/%0%.c [manifest] ]
                    ]
                    library/liblibrary.a
                    ;

       Notice  that  adding  a  new  program   source   file   will
       automatically  cause  it  to be compiled and linked into the
       program, without modification to the cookbook.

       The loop construct tends to obscure things, which is why the
       essential  assignment  was  given first.  This next fragment
       shows the whole loop.
            programs =
                    [fromto %/main.c %
                            [match_mask %/main.c [manifest] ]
                    ];
            program_list = [programs];
            loop
            {
                    program = [head [program_list]];
                    if [not [count [program]]] then
                            loopstop;
                    program_list = [tail [program_list]];

                    bin/[program]_obj =
                            [fromto %0%.c %0%.o
                                    [match_mask [program]/%0%.c
                                            [manifest]


       Peter Miller                                         Page 30





       Cook                                              User Guide



                                    ]
                            ]
                            library/liblibrary.a
                            ;
            }
       And now tell Cook you actually want it to do something, like
       build all of the programs...
            all: [addprefix bin/ [programs]];

       Notice  they  way the commands variable is constructed: just
       adding  a  new  command   (and   its   main.c   file)   will
       automatically  cause it to be built, without modification to
       the cookbook.


       _6_._2  _P_r_i_v_a_t_e _W_o_r_k _A_r_e_a_s

       This chapter is about large  projects,  but  large  projects
       usually  means  large  numbers of developers.  The directory
       structure and cookbook presented so far does not immediately
       lend itself to use by multiple developers.

       _6_._2_._1  _D_i_r_e_c_t_o_r_y _S_t_r_u_c_t_u_r_e
       The   method   suggested   here   uses   Cook's  _s_e_a_r_c_h___l_i_s_t
       functionality, which nominates a search list of  directories
       that  Cook  looks in to find the files named in the recipes.
       This can be used to overlay a private work area on top of  a
       master repository.
                       ++          +
                      ++ _R_e_p_o_s_i_t_o+_r+_y
                     +     main.+c+        _C_o_m_b_i_n_e_d _V_i_e_w
                   ++     part1+.c            main.c
                    _W_o_r_k _A_r_e_a      +         part1.c
                      main.c      ++         part2.c
                                ++
                     part2.c   +


       When  recipes are run, the results are written into the work
       area, which means that  the  repository  can  be  completely
       read-only.

       It  follows  from  this, that the directory structure of the
       work area exactly parallels the directory structure  of  the
       repository.   _E_x_c_e_p_t you only check out files into your work
       area that you actually need to change.

       _6_._2_._2  _F_i_n_d_i_n_g _t_h_e _C_o_o_k_b_o_o_k
       Setting the search list is done with  a  simple  assignment.
       In   your  work  area,  create  a  simple  Howto.cook  file,
       containing only 3 lines:
            set mkdir;
            search_list = . /project/repository ;
            #include /project/repository/Howto.cook

       
       Peter Miller                                         Page 31





       Cook                                              User Guide



       You only use this file if  you  don't  need  to  modify  the
       cookbook  itself.   You can make it work always, even if you
       are  modifying  the  cookbook,  by  giving  the  cookbook  a
       different  name  (main.cook),  and  changing  Howto.cook  to
       always read
            set mkdir;
            search_list = . /project/repository ;
            #include [resolve main.cook]
       The [resolve] function walks the search  list,  looking  for
       the file5.  This gives you access to Cook's internal  search
       mechanism.   However,  we  also  need  to modify each of the
       recipes to take the search list into account.

       The  unexplained  mkdir  flag  is  used  to   request   that
       directories  be  automatically  created before recipe bodies
       are run.  This is  common  for  large  projects,  where  the
       source  files  are  structured into several sub-directories,
       rather than all lumped together in the one place.  This  may
       be  necessary,  for  example, if a .c file in the repository
       needs to be recompiled because a .h file in  the  work  area
       has been changed.

       _6_._2_._3  _F_i_l_e _M_a_n_i_f_e_s_t
       The  files  could  be  in either of two places.  You need to
       merge them.  Most configuration management tools do this for
       you;  in  this example we'll scan the directory trees again.
       Fortunately, Cook comes with a tool to do this  efficiently.
            all_files_in_. = ;
            #include manifest.cook
            manifest = [all_files_in_.];

            /* This reduces re-scanning to a minimum. */
            set fingerprint;

            %0manifest.cook: ["if" [in "%0" ""] "then" "." "else" "%0"]
                    set mkdir
            {
                    cook_bom /* Bill Of Materials */
                            [addprefix '--dir=' [search_list]]
                            [need] [target] ;
            }
       At  the end of this fragment, the manifest variable contains
       a complete list of all files in the directory tree(s).  This
       variable  may  then  be  taken  apart  with  the  match_mask
       function to build ingredients lists.

       The if function is different to the _i_f statement.  It allows
       you  to  select one of two values (the then part or the else
       part) without creating a dummy variable.  In  this  example,
       it would be impossible to create a dummy variable.  Remember

       ____________________

       5. The  search  list  defaults  to  just  dot  (the  current
          directory) if not set.

       Peter Miller                                         Page 32





       Cook                                              User Guide



       to quote the if, then and else strings, otherwise Cook  will
       think  they  are  _i_f, _t_h_e_n and _e_l_s_e keywords, and give you a
       syntax error.

       The constructed _m_a_n_i_f_e_s_t_._c_o_o_k files work for both  the  top-
       level directory and individual sub-directories.

       _6_._2_._4  _C_o_m_p_i_l_i_n_g _C _S_o_u_r_c_e_s
       The C compilation recipe needs to be changed to read...
            %0%.o: %0%.c
            {
                    [cc] [cc_flags]
                            [prepost "-I" /[dirname %0%.c] [search_list]]
                            [prepost "-I" "/include" [search_list]]
                            -c [resolve %0%.c]
                            -o [target];
            }
       This ensures that the rights places are searched for include
       files.

       The prepost function is used to add a prefix and a suffix to
       each  of  the  remaining  strings.  This is very useful when
       constructing filenames, as are the addprefix  and  addsuffix
       functions.

       _6_._2_._5  _T_r_a_c_k_i_n_g _I_n_c_l_u_d_e _D_e_p_e_n_d_e_n_c_i_e_s
       A   similar   change   needs  to  be  made  to  the  include
       dependencies recipe...
            %0%.c.d: %0%.c
                    set nocascade
            {
                    c_incl -nc -ns -nrec
                            [prepost "-I" /[dirname %0%.c] [search_list]]
                            [prepost "-I" "/include" [search_list]]
                            [resolve %0%.c]
                            -prefix "'cascade %0%.c ='"
                            -suffix "';'"
                            [addsuffix "-rp=" [search_list]]
                            -o [target];
            }
       Note that the form of the output of  this  recipe  _d_o_e_s  _n_o_t
       change.   This means that the recipes it writes work even if
       you subsequently copy a file from the repository to the work
       area, or uncopy one.

       _6_._2_._6  _L_i_n_k_i_n_g _L_i_b_r_a_r_i_e_s
       The library recipe needs few modifications.
            %/lib%.a: [[target]_obj]
                    set unlink
            {
                    ar cq [target] [resolve [[target]_obj]];
            }
       The    variable   assignment   given   above   requires   no
       modifications.

       
       Peter Miller                                         Page 33





       Cook                                              User Guide



       _6_._2_._7  _L_i_n_k_i_n_g _C_o_m_m_a_n_d_s
       The command linking recipe requires few modifications.
            bin/%: [[target]_obj]
                    set mkdir
            {
                    [cc] -o [target] [resolve [[target]_obj]];
            }
       The variable assignment needs no modifications.

       _6_._3  _W_h_o_l_e _P_r_o_j_e_c_t _B_u_i_l_d _A_d_v_a_n_t_a_g_e_s

       The advantage of using a whole project  build  is  that  the
       dependency graph is complete, and the order of traversal may
       be freely determined  by  Cook.   Breaking  the  build  into
       fractured  segments  denies  Cook access to the whole graph,
       and dictates the order of traversal to  one  which,  in  the
       light of the entire graph, would be incorrect.

       It  greatly  simplifies  the  creating  of  work  areas  for
       developers, by using Cook's _s_e_a_r_c_h___l_i_s_t functionality.

       A whole project build also permits the _c_o_o_k _-_c_o_n_t_i_n_u_e option
       to work in the presence of a wider range of errors.

       The  whole  project  build  also  permits the _c_o_o_k _-_p_a_r_a_l_l_e_l
       option to parallelize more operations.

       _6_._4  _H_e_t_e_r_o_g_e_n_o_u_s _B_u_i_l_d

       Large   projects   frequently   involve   numerous    target
       architectures.   This  may  be in the form a multiple native
       compilations, performed in suitable hosts, or  it  may  take
       the form of cross-compilation.

       In  this example, we assume that the GNU C Compiler (GCC) is
       being used.  When GCC is installed as a cross compiler,  the
       command  names  (cc,  as,  ld,  _e_t_c)  are installed with the
       architecture name as a prefix.  For consistency, the  native
       compiler  is  installed with its own architecture names as a
       prefix, in addition to the more commonly used  gcc  command.
       This example will exploit this normal installation practice.

       _6_._4_._1  _C_r_o_s_s _C_o_m_p_i_l_i_n_g _C _S_o_u_r_c_e_s
       In order to  support  cross  compiling,  the  C  compilation
       recipe needs to be changed to read...
            %1/%0%.o: %0%.c
                    host-binding [defined-or-null %1-hosts]
            {
                    %1-gcc [cc_flags]
                            [prepost "-I" /[dirname %0%.c] [search_list]]
                            [prepost "-I" "/include" [search_list]]
                            -c [resolve %0%.c]
                            -o [target];
            }

       
       Peter Miller                                         Page 34





       Cook                                              User Guide



       This  uses  the  first directory element of the _t_a_r_g_e_t to be
       the architecture name.  This allows  multiple  architectures
       to be compiled in the same source tree, simultaneously.

       Because of the practice of installing a duplicate GCC in the
       same form as the cross compilers, this same recipe continues
       to work for native builds.

       The  _h_o_s_t_-_b_i_n_d_i_n_g  line tells Cook to run the command on one
       of  the  hosts  nominated  in  a  variable  named  for   the
       architecture  (or  as  a  native  cross-compiler  of no such
       variable  exists).    (The   defined-or-null   function   is
       available  in  the  ``functions''  library  distributed with
       Cook.)

       Remembering these architectures follow the  GNU  convention,
       these lines could read
            i386-linux-hosts = fast faster fastest ;
       This  will  do  two  things  for  you: first, it will always
       execute linux compiles on linux hosts even when Cook is  not
       executed  on  one; second, it will use more than one of them
       when you use the --parallel option.

       It is possible to use implicit ingredients  recipes  to  say
       that  all  object  of a given architecture depend on a magic
       include file, _e_._g_.
            i386-linux/%0%.o: include/linux-special.h;
       could be used to say that all Linux object files  depend  on
       this include file.  (This is a sledge-hammer approach, and a
       more subtle  method  is  preferable,  but  it  is  sometimes
       required.)

       _6_._4_._2  _T_r_a_c_k_i_n_g _I_n_c_l_u_d_e _D_e_p_e_n_d_e_n_c_i_e_s
       Because  of the cascade form of include dependency, there is
       no need to do anything different for  include  dependencies,
       even  if  you  add  another  architecture  some  time in the
       future.

       _6_._4_._3  _L_i_n_k_i_n_g _L_i_b_r_a_r_i_e_s
       The library recipe needs few modifications.
            %1/%/lib%.a: [%/lib%.a_obj]
                    set unlink
            {
                    %1-ar cq [target] [resolve [%/lib%.a_obj]];
            }
       The   variable   assignment   given   above   requires    no
       modifications.

       _6_._4_._4  _L_i_n_k_i_n_g _C_o_m_m_a_n_d_s
       The command linking recipe requires few modifications.
            %1/bin/%: [bin/%_obj]
                    set mkdir
            {
                    %1-gcc -o [target] [resolve [bin/%_obj]];

       
       Peter Miller                                         Page 35





       Cook                                              User Guide



            }
       The variable assignment needs no modifications.

       _6_._4_._5  _W_h_a_t _t_o _B_u_i_l_d
       The list of what to build becomes more interesting.  You can
       nominate any and all architectures for which you have  cross
       compilers, or native compilers and native hosts.
            all:
                    [addprefix i386-linux/bin/ [commands]]
                    [addprefix sparc-linux/bin/ [commands]]
                    [addprefix sparc-solaris2.0/bin/ [commands]]
                    [addprefix m68k-sunos4.1.3/bin/ [commands]]
                    ;

       All  of  these  architectures will be built in a single Cook
       invocation, on appropriate machines if necessary.   The  use
       of  --continue  and --parallel work over the entire scope of
       the build.

       _6_._5  _I_n_s_t_a_l_l_i_n_g _T_h_i_n_g_s

       The biggest hassle is that  the  _i_n_s_t_a_l_l(1)  command,  which
       should   know   how  to  do  most  installation  tasks,  has
       completely incompatible interfaces on the various platforms.
       This is why the GNU autoconf system comes with an _i_n_s_t_a_l_l_-_s_h
       script, which faithfully emulates the BSD options.  Once you
       have  a  reliable  command  line  interface to an _i_n_s_t_a_l_l(1)
       program (be it perl or shell) you can  then  write  sensible
       installation cookbooks.

       If we have a list of commands, we would install as follows:
            prefix = /usr/local;
            bindir = [prefix]/bin;
            install = install;

            install: [addprefix [bindir]/ [commands]];
            [bindir]/%0%: bin/%0% bin/%0.mkdir
            {
                    [install] -m 755 bin/%0% [bindir]/%0%;
            }
       That  magic  bin/%0.mkdir  file  is  used to record that the
       destination directory exists.  While you  can  often  assume
       this,  it  is  not  always true when you are building things
       like RPM packages.
            bin/%0.mkdir:
            {
                    [install] -d [bindir]/%0
                            set errok;
                    touch [target];
            }
       The alternative is to use
            set mkdir;
       at the top of your cookbook.  This creates  directories  for
       targets before rules are run.  The install recipe then reads

       
       Peter Miller                                         Page 36





       Cook                                              User Guide



            set mkdir;

            [bindir]/%0%: bin/%0%
            {
                    [install] -m 755 bin/%0% [bindir]/%0%;
            }
       because there is no need for the ``.mkdir''  recipe.   This,
       however   gives  you  less  crontrol  over  the  directories
       permission modes, and it  doesn't  help  when  you  want  to
       create  empty  directories  as part of the install.  Use the
       appropriate technique for your needs.

       _6_._6  _M_i_s_c_e_l_l_a_n_e_o_u_s

       This  section  contains  assorted  material  that  covers  a
       variety of topics.  (As the manual expands, it will probably
       be moved somewhere else.)

       _6_._6_._1  _L_o_t_s _o_f _D_e_p_e_n_d_e_n_c_i_e_s
       There are cases where you  may  want  to  nominate  a  whole
       category  of  files  as  depending  on  something else.  For
       example, you may want to say that  all  your  fubar-language
       sources   depend  on  your  fubar  compiler  You  could  say
       something such as
            cascade [match_mask %0%.fubar [manifest]] = fubarcompiler;
       but recall that _e_v_e_r_y_t_h_i_n_g which has a  .fubar  file  as  an
       ingredient  will  also  have fubarcomplier as an ingredient.
       This may not be what you wanted.

       Recall,  also,  that   compiler   recipes   carry   specific
       information.   You  could  more  specifically  nominate  the
       compiler by saying
            %0%.o: %0%.fubar: fubarcompiler
            {
                    fubarcompiler -c %0%.fubar -o [target];
            }
       which would be much  more  selective  about  which  uses  of
       .fubar files also depend on fubarcompiler.

       There  are times when writing cross-compilation recipes when
       you want to nominate  an  operating-system-specific  include
       file for all of the object files:
            %1/%0%.o: %0%.c
            {
                    /* general cross compiler recipe */
                    %1-gcc -c %0%.c -o [target];
            }
            /* All windows NT objects depend on this include file */
            i386-NT/%0%.o: winnt.h;

       You  can  also use _g_a_t_e_s to make you recipes more selective.
       The gating expression may be just  about  anything,  but  is
       often a pattern match or simple set membership.
            %.o: %.c

       
       Peter Miller                                         Page 37





       Cook                                              User Guide



                    if [in [target] foo.o bar.o]
            {
                    /* foo.o and bar.o are magic */
                    cc -DMAGIC [cc_flags] -c %.c;
            }
       The  gate  is  most easily read as ``if _(_t_h_i_s _c_o_n_d_i_t_i_o_n_) use
       this recipe''.

       _6_._6_._2  _E_r_r_o_r _P_r_o_c_e_s_s_i_n_g
       Cook stops processing a recipe at the first error.   If  the
       error occurs when constructing a command to be executed, the
       command is _n_o_t executed.  If a  recipe  body  contains  more
       than one command, and one of them gets an error (and doesn't
       have the _e_r_r_o_k flag set) the rest of the command will _n_o_t be
       executed.

       In  addition,  if  an  error occurs while executing a recipe
       body, the targets of the recipe  will  be  deleted  (on  the
       assumption  that they are probably only partially completed,
       or otherwise defective).  To override this behavior, use the
       _p_r_e_c_i_o_u_s flag.

       _6_._6_._3  _N_F_S
       A  perennial  problem for building projects over networks is
       that the clocks don't match.  If  you  use  the  _t_i_m_e_-_a_d_j_u_s_t
       flag,  this  problem is largely solved.  The simplest method
       is to put
            set time-adjust;
       at the top of your cookbook.

       File fingerprints, while not directly relevant to  NFS,  can
       offer  significant  performance  improvements,  as  they can
       eliminate many cases of unnecessary re-compilation.  To turn
       them on, use
            set fingerprint;
       at  the top of your cookbook.  See below for more discussion
       of fingerprints.

       _6_._6_._4  _S_y_m_b_o_l_i_c _L_i_n_k_s
       Symbolic  links  are  followed  to  the  actual  file,  when
       determining  file modification times.  The modification time
       of the symbolic link itself is not used.   This  means  that
       ``symlink  farms'' can be used when constructing work areas,
       particularly when you want functionality more  complex  than
       search_list can provide.

       _6_._7  _F_i_l_e _F_i_n_g_e_r_p_r_i_n_t_s

       Cook  has  the ability to supplement the last-modified time-
       stamps the operating system supplies for each  file  with  a
       ``fingerprint''.    This   is   a  cryptographically  strong
       checksum, with an mind-bogglingly low probability  that  two
       different files will have the same fingerprint.


       
       Peter Miller                                         Page 38





       Cook                                              User Guide



       When  Cook  needs to know if a file has changed, it looks at
       the last-modified time-stamp.  If it has changed  since  the
       last time the fingerprint was calculated, the fingerprint is
       re-calculated.  If the fingerprints match,  Cook  knows  the
       file  contents  are  unchanged, and uses the old time-stamp,
       and also suppress any recipe actions which  would  otherwise
       happen  if  the  file  contents had actually changed.  (Cook
       remembers the both the new and old time-stamps, so  that  it
       can  be  efficient  about re-calculating checksums and still
       use the old time stamp for out-of-date calculations.)

       When recipe bodies are run, Cook knows  that  the  target(s)
       have  been  modified,  so  it doesn't need to re-examine the
       operating system's idea of the last-modified time-stamp,  it
       simply re-fingerprints.

       It  is  tempting  to  try  to  achieve  something similar by
       writing recipe bodies which only over-write their targets if
       they actually changed.  _E_._g_.
            %.o: %.c
            {
                    if [exists [target]] then
                    {
                            [CC] -o %.tmp -c %.c;
                            if cmp %.tmp %.o\;
                            then mv %.tmp %.o\;
                            else rm %.tmp;
                    }
                    else
                            [CC] -o %.o -c %.c;
            }
       However,  this  will  not  work  (whether  or  not  you have
       fingerprints turned on).  Largely as a defense  against  NFS
       time  synchronization  problems and stupid systems with very
       coarse file time-stamps, Cook  ``knows''  that  because  the
       recipe body was run the target ``changed'', causing all down
       stream dependencies to be considered out-of-date.

       In addition, this recipe would leave the last-modified time-
       stamp out-of-date if the file was unchanged.  This means the
       recipe would trigger  again  in  the  next  Cook  execution,
       negating many of the intended savings.

       Fingerprints  are  intended  for  this purpose, but have the
       advantage of leaving the last-modified time-stamps  correct,
       and  they  need  to  do half the I/O that the _c_m_p(1) command
       does.  Also, all down stream dependent files are touched, to
       ensure  their last-modified time-stamps are also consistent.
       Naturally, if they needed to  be  re-built  for  some  other
       reason, then they would be re-built, not simply touched.

       While  there  is  some overhead in initially calculating the
       fingerprints for a new work area, they repay  that  overhead
       many times over.  This is especially true if your system has

       
       Peter Miller                                         Page 39





       Cook                                              User Guide



       generated code in it, particularly generated include  files,
       but there are also savings for simpler, smaller projects.

       _6_._7_._1  _T_u_r_n_i_n_g _F_i_n_g_e_r_p_r_i_n_t_s _O_n
       To turn fingerprints on, you need to add the lines
            set fingerprint;
            set time-adjust;
       to  your cookbook.  That second line is no essential, but it
       corrects   last-modified   time-stamps   when    NFS    time
       synchronization  problems would otherwise cause inconsistent
       behavior.

       While it is possible to turn fingerprints on for a subset of
       the  files  in your project, it is not as straightforward as
       it may seem.  There  is  no  way  to  bind  the  fingerprint
       request  to  a  single file, only to recipes, so you need to
       use the ``set  fingerprint''  recipe  flag  on  all  recipes
       between  the  relevant  source file and the ultimate target.
       This tends to be messy.

       _6_._7_._2  _V_a_n_i_s_h_i_n_g _D_e_p_e_n_d_e_n_c_i_e_s
       It is quite common that you need to re-build a file  if  one
       of the dependencies is removed.  Usually, this is quite hard
       to detect, because Cook has trouble  seeing  something  that
       isn't there, compared to the previous execution.  However an
       ingenious  method  has  been  described  by  Gilles  Lamiral
       <lamiral@mail.dotcom.fr> which ``remembers'' though a file:
            function contents-remember =
            {
                    /* @1 = name of contents file */
                    /* @2..N = the value of [need] */
                    [write [args]];
            }
            function contents-changed =
            {
                    /* @1 = name of contents file *
                    /* @2..N = the value of [need] */
                    if [not [exists [resolve [@1]]]] then
                            return 0;
                    local old-contents = [collect_lines cat [resolve [@1]]];
                    /* return 0 if nothing disappeared, >0 if did disappear */
                    return [count [stringset [old-contents] - [tail [arg]]]];
            }
            libfred.a libfred.contents: [fred_obj]
                    set ["if" [contents-changed libfred.contents [fred_obj]]
                            "then" forced]
                        unlink
            {
                    ar cq [target] [resolve [fred_obj]];
                    [contents-remember libfred.contents [fred_obj]];
            }

       Note: because the set clause is evaluated when the target is
       evaluated, the [need] variable is not  available.   In  this

       
       Peter Miller                                         Page 40





       Cook                                              User Guide



       example,  you  must  have  calculated  the  final  value  of
       [fred_obj] before the recipe appears in the  cookbook.   The
       evaluation  of the set clause also limits the application of
       this technique to explicit recipes; it  will  not  work  for
       implicit (pattern) recipes, because the value of the pattern
       elements is  not  known  at  the  time  the  set  clause  is
       evaluated.

       _6_._8  _C_o_p_i_n_g _w_i_t_h _L_i_n_k_s

       You  will  notice  that  the deafult operation of Cook copes
       with links (hard links and symbolic  links)  rather  poorly.
       For example, the recipe
            two: one
            {
                    ln one two;
            }
       will  always conclude that file _t_w_o is out-of-date.  This is
       bacause files _o_n_e and _t_w_o have exactly the same time  stamp.

       If  you  specify  a  weaker time constraint, Cook will allow
       this kind of recipe to be  written,  and  _n_o_t  conclude  the
       files is always out of date:
            two: one(weak)
            {
                    ln one two;
            }
       The  ``(weak)'' on the end of the ingredient name tells Cook
       to use the weak edge type, rather than the strict edge type.

       This technique is useful for symbolic links, too.

       One  other  thing  which  can  be  very useful for both link
       types, but particularly symbolic links  to  directories,  is
       the ``set unlink'' recipe flag.
            two: one(weak)
                    set unlink
            {
                    ln -s one two;
            }
       This  removes  the  target  (if necessary) before the recipe
       body is run.

       _6_._9  _C_o_p_i_n_g _w_i_t_h _V_e_r_s_i_o_n _S_t_a_m_p_s

       In some systems, the version stamp is regenerated for  every
       build,  but you don't want to relink zillions of executables
       just becaise the version stamp has changed, but nothing else
       has.

       By  using the ``(exists)'' edge type, you can tell Cook that
       an ingredient is needed for a  given  target,  but  that  it
       should  never  be considered to make the target out-of-date.
       For example:

       
       Peter Miller                                         Page 41





       Cook                                              User Guide



            #include "c"
            all: prog1 prog2;
            version.c:
                    set forced
            {
                    date "'+#define VERSION \"%C\"'" > [target];
            }
            prog1: prog1.o mylib.a version.o(exists)
            {
                    gcc -o [target] [need];
            }
            prog2: prog2.o mylib.a version.o(exists)
            {
                    gcc -o [target] [need];
            }
       This cookbook will generate a new _v_e_r_s_i_o_n_._c file every  time
       that  Cook  is run, and thus a new _v_e_r_s_i_o_n_._o file.  However,
       the _p_r_o_g_1 and _p_r_o_g_2  files  will  not  be  re-linked  unless
       something else changed as well.




































       
       Peter Miller                                         Page 42





       Cook                                              User Guide



       _7_.  _C_o_o_k_b_o_o_k _L_a_n_g_u_a_g_e _D_e_f_i_n_i_t_i_o_n

       This  chapter  defines  that  language  which  cookbooks are
       written in.  While some of its properties are similar to  C,
       do not be misled.

       A number of sections appear within this chapter.

         1.  The  _L_e_x_i_c_a_l _A_n_a_l_y_s_i_s section describes what the words
             of the cookbook language look like.

         2.  The  _P_r_e_p_r_o_c_e_s_s_o_r  section   describes   the   include
             mechanism and the conditional compilation mechanism.

         3.  The  _S_y_n_t_a_x  _a_n_d _S_e_m_a_n_t_i_c_s section describes how words
             in  the  cookbook  may  be  combined  to  form   valid
             constructs  (the  _s_y_n_t_a_x),  and  what these constructs
             mean (the _s_e_m_a_n_t_i_c_s).

       The sections are laid out in the recommended reading  order.

       _7_._1  _L_e_x_i_c_a_l _A_n_a_l_y_s_i_s

       The  cookbook  is  made of a number of recipes, which are in
       turn made of words.  This section describes what constitutes
       a word, and what does not.

       _7_._1_._1  _W_o_r_d_s _a_n_d _K_e_y_w_o_r_d_s
       Words are made of sequences of almost any character, and are
       separated by white  space  (including  end-of-line)  or  the
       special symbols.  CCooookk is always case sensitive when reading
       cookbooks.

       The characters ::;;=={{}}[[]] are  the  special  symbols,  and  are
       words in themselves, needing no delimiting.

       In  addition  to  the  special symbols, some words, known as
       _k_e_y_w_o_r_d_s, have special meaning to ccooookk.  The keywords are:

           else        host-binding      loopstop      single-thread
           fail             if            return           then
         function          loop             set          unsetenv
       You will meet the keywords in later sections.

       _7_._1_._2  _E_s_c_a_p_e _S_e_q_u_e_n_c_e_s
       The character \\ is the _e_s_c_a_p_e character.  If a character  is
       preceded  by  a  \\  any  specialness, if it had any, will be
       removed.  If it had no specialness it may have some added.

       This means that, if you want to use iiff  as  a  word,  rather
       than  a  keyword, at least one of its characters needs to be
       escaped, for example \\iiff.

       The escape sequences which are special are as follows.

       
       Peter Miller                                         Page 43





       Cook                                              User Guide



                  \\bb    The backspace character
                  \\ff    The form feed character
                  \\nn    The newline or linefeed character
                  \\rr    The carriage return character
                  \\tt    The horizontal tab character
                 \\_n_n_n   A character with a value of  _n_n_n,
                        where  _n_n_n  is an octal number of
                        at most 3 digits.
       An escaped end-of-line is totally  ignored.   It  should  be
       noted  that  a  cookbook may not have any non-printing ASCII
       characters in it other than space, tab and end-of-line.

       _7_._1_._3  _Q_u_o_t_i_n_g
       Words, and sections of words, may be quoted.  If any part of
       a word is quoted it cannot be a keyword.

       This  means  that,  if  you want to use iiff as a word, rather
       than a keyword, at least one of its characters needs  to  be
       quoted, for example ''iiff''.

       Both  single  ('')  and  double  ("") quotes are understood by
       ccooookk, and one may enclose the other.  If a quote is  escaped
       it does not open or close a quote as it usually would.

       CCooookk  does  not  like  newlines  within  quotes.   This is a
       generally good heuristic for catching unbalanced quotes.  If
       you  really  want  a  newline  within  a  string, use the \n
       escape.

       _7_._1_._4  _C_o_m_m_e_n_t_s
       Comments are delimited on the left by //**, and on  the  right
       by  **//.   If  the // character has been escaped or quoted, it
       doesn't  introduce  a  comment.   Comments  may  be  nested.
       Comments  may span multiple lines.  Comments are replaced by
       one logical space.

       _7_._2  _P_r_e_p_r_o_c_e_s_s_o_r

       The preprocessor may be thought of as doing  a  little  work
       before the _S_y_n_t_a_x _a_n_d _S_e_m_a_n_t_i_c_s section has its turn.

       The  preprocessor  is  driven by _p_r_e_p_r_o_c_e_s_s_o_r _d_i_r_e_c_t_i_v_e_s.  A
       preprocessor directive is a line which starts  with  a  hash
       (##)  character.   Each  of  the  preprocessor  directives is
       described below.

       _7_._2_._1  _i_n_c_l_u_d_e
       The most common preprocessor directive is
            #include "_f_i_l_e_n_a_m_e"

       This preprocessor directive is processed as if the  contents
       of  the named file had appeared in the cookbook, rather than
       the preprocessor include directive.


       
       Peter Miller                                         Page 44





       Cook                                              User Guide



       The most common use of the #include directive is to  include
       system  cookbooks.   For example, many small programs can be
       developed using the following simple cookbook:
            #include "c"
            #include "program"

       The standard places to search are first any  path  specified
       with  the --IInncclluuddee command line option, and then _$_H_O_M_E_/_._c_o_o_k
       and then _/_u_s_r_/_p_k_g_/_s_h_a_r_e_/_c_o_o_k in that order.

       _7_._2_._2  _i_n_c_l_u_d_e_-_c_o_o_k_e_d
       This directive looks similar to the one above, but do not be
       deceived.
            #include-cooked _f_i_l_e_n_a_m_e...
       You  may name several filenames on the line, and they may be
       expressions.

       The search path used for these files is  the  same  as  that
       used  for  other  cooked files, see the _s_e_a_r_c_h___l_i_s_t variable
       and the _r_e_s_o_l_v_e built-in function for more information.  The
       order  in  which  you  set the _s_e_a_r_c_h___l_i_s_t and the _#_i_n_c_l_u_d_e_-
       _c_o_o_k_e_d directives is important.  Always set the  _s_e_a_r_c_h___l_i_s_t
       variable first, if you are going to use it.

       Files included in this way are checked, after they have been
       read, to make sure they are up-to-date.  If  they  are  not,
       ccooookk  brings  them up-to-date and then re-reads the cookbook
       and starts over.

       You will only get a warning if  the  files  are  not  found.
       Usually,  ccooookk  will either succeed in constructing them, in
       which case they will be present the second time around, or a
       fatal  error  will result from attempting to construct them.
       Note that it is possible to go into an infinite loop, if the
       files are constantly out-of-date.

       The  commonest  use of this construct is maintaining include
       file dependency lists for source files.
            obj = [fromto %.c %.o [glob *.c]];

            %.o: %.c
            {
                    [cc] [cc_flags] -c %.c;
            }

            %.c.d: %.c
            {
                    c_incl -prefix "'%.o "[target]": %.c'" -suffix "';'"
                            -no-cache %.c > [target];
            }

            #include-cooked [fromto %.o %.c.d [obj]]
       This cookbook fragment shows how include  file  dependencies
       are  maintained.   Notice  how the _._d files have a recipe to

       
       Peter Miller                                         Page 45





       Cook                                              User Guide



       construct them, and that they are also included.  CCooookk  will
       bring  them  up-to-date  if  necessary, and then re-read the
       cookbook, so that it is  always  working  with  the  current
       include  dependencies.   (The  doubly  nested  quotes are to
       insulate the spaces and special characters  from  both  ccooookk
       and the shell.)

       You  could  use  _g_c_c  _-_M_M  if you prefer (you will need some
       extra shell script).  The _c___i_n_c_l program understands  absent
       files better but doesn't understand conditional compilation,
       and _g_c_c understands conditional compilation but gives  fatal
       errors  for absent include files.  Warning: If you are using
       _s_e_a_r_c_h___l_i_s_t you  mmuusstt  use  _c___i_n_c_l.   Gcc  returns  complete
       paths,  which  will result in ccooookk failing to notice when an
       include file is copied from later  in  the  search  list  to
       earlier, and then modified.

       There  are  times  when  you  don't want the #include-cooked
       directives to be acted upon.  You can over-ride it using the
       --no-include-cooked  command  line  option,  but it is often
       easier to use the  [command-line-goals]  variable,  and  say
       something like
            #if [not [match %1clean%2 [command-line-goals]]]
            #include-cooked [fromto %.o %.c.d [obj]]
            #endif
       This  construct  means  that  whenever an explicit ``clean''
       goal (or similar) is requested,  the  #include-cooked  lines
       will  not  be performed.  This is sensible, because cleaning
       actions usually remove dependency files; there is  no  point
       making sure they are up-to-date first.

       _7_._2_._3  _i_n_c_l_u_d_e_-_c_o_o_k_e_d_-_n_o_w_a_r_n
       This  directive is almost identical to the one above, but no
       warning is issued for absent files.
            #include-cooked-nowarn _f_i_l_e_n_a_m_e...
       You may name several filenames on the line, and they may  be
       expressions.

       _7_._2_._4  _i_f
       The  #if  directive may be used to conditionally pass tokens
       to the syntax and semantics processing.  Directives take the
       form
            #if _e_x_p_r_e_s_s_i_o_n_1
            _s_o_m_e_t_h_i_n_g_1
            #elif _e_x_p_r_e_s_s_i_o_n_2
            _s_o_m_e_t_h_i_n_g_2
            #else
            _s_o_m_e_t_h_i_n_g_3
            #endif
       There may be any number of elif clauses, and the else clause
       is optional.  Only one of  the  _s_o_m_e_t_h_i_n_g_s  will  be  passed
       through.



       
       Peter Miller                                         Page 46





       Cook                                              User Guide



       _7_._2_._5  _i_f_d_e_f
       This directive takes a similar form to the if directive, but
       with a different first line:
            #ifdef _v_a_r_i_a_b_l_e
       This is syntactic sugar for
            #if [defined _v_a_r_i_a_b_l_e]
       This is of most use in bracketing #include directives.

       _7_._2_._6  _i_f_n_d_e_f
       This directive takes a similar form to the if directive, but
       with a different first line:
            #ifndef _v_a_r_i_a_b_l_e
       This is syntactic sugar for
            #if [not [defined _v_a_r_i_a_b_l_e]]
       This is of most use in bracketing #include directives.

       _7_._2_._7  _p_r_a_g_m_a
       This is for the addition of extensions.

       _7_._2_._7_._1  _o_n_c_e
       This  directive  is to ensure that include files in which it
       appears are included exactly once.

       This directive has the form
            #pragma once

       _7_._2_._7_._2  _u_n_k_n_o_w_n _e_x_t_e_n_s_i_o_n_s
       Any pragma extensions not recognized will be ignored.



























       
       Peter Miller                                         Page 47





       Cook                                              User Guide



       _7_._3  _S_y_n_t_a_x _a_n_d _S_e_m_a_n_t_i_c_s

       The syntax is described using ``train track'' diagrams, with
       prose descriptions of the related semantics.

       _7_._3_._1  _O_v_e_r_a_l_l _S_t_r_u_c_t_u_r_e
       The general form of the cookbook is defined as
        -c-o-o-k-b-o-o-k--------------------------------------------------
                        |     -----------------------     |
                             +                       +
                         -------------+-----+-------------
                                  +   --s-t-m-t-    +
                                     +--------
                                   ---function+--
                                     ---------

       A  cookbook  is  defined  as a sequence of statements.  Each
       statement statement is executed.  For a definition  of  what
       it  means  when  a statement is executed, see the individual
       statement definitions.

       The nonterminal symbol _s_t_a_t_e_m_e_n_t  will  be  defined  in  the
       sections below.

       Please note that a statement is not always evaluated when is
       is read, but at specific, well defined times.

       _7_._3_._2  _T_h_e _C_o_m_p_o_u_n_d _S_t_a_t_e_m_e_n_t
       A nonterminal symbol which will be referred to below is  the
       _c_o_m_p_o_u_n_d___s_t_a_t_e_m_e_n_t symbol, defined as follows:
        cstmt            -                              -
        ---------------- -{ ---------------------------- -} --------
                              |      ---------      |
                                   +  |-----+  +
                                --------s-t-m-t---------

       The  compound statement may be used anywhere a statement may
       be, and in particular
        stmt                          +-----+
        --------------------------------c-s-t-m-t----------------------


       _7_._3_._3  _V_a_r_i_a_b_l_e_s _a_n_d _E_x_p_r_e_s_s_i_o_n_s
       CCooookk provides variables to the user to simplify things.

       _7_._3_._3_._1  _T_h_e _A_s_s_i_g_n_m_e_n_t _S_t_a_t_e_m_e_n_t
       It is possible to assign to  variables  with  the  following
       statement.
        stmt                  +----+  -   +-----+  +
        ------------------------e-x-p-r-- -= ---e-x-p-r-s-- +; --------------

       When this statement is executed, the variable whose name the
       left hand expression evaluates to will be assigned the value
       that the right hand expression list evaluates to.

       
       Peter Miller                                         Page 48





       Cook                                              User Guide



       For example:
            program_obj = foo.o bar.o baz.o;

       NNoottee::  It  is  possible  to  over-ride the value of built-in
       functions and variables with this statement.  This will  not
       produce   an  error  message,  however  it  is  usually  not
       desirable as it will change the meaning of the rest of  your
       cookbook.

       _7_._3_._3_._2  _T_h_e _A_s_s_i_g_n_-_A_p_p_e_n_d _S_t_a_t_e_m_e_n_t
       It  is possible to append to the value of variables with the
       following statement.
        stmt                  +----+  -   +-----+  +
        ------------------------e-x-p-r-- -+=---e-x-p-r-s-- +; --------------

       When this statement is executed, the variable whose name the
       left  hand  expression  evaluates  to  will  have  its value
       appended by the value that the right  hand  expression  list
       evaluates   to.   Expression  values  are  lists  of  words,
       appending means to append to the word list; it does _n_o_t mean
       appending to the last string of the value.

       For example:
            program_obj += [glob "deeper/*.o" ];

       NNoottee::  It  is  possible  to  over-ride the value of built-in
       functions and variables with this statement.  This will  not
       produce  an  error  message  (unless evaluating them with no
       arguments is an error), however it is usually not  desirable
       as  it will change the meaning of the rest of your cookbook.

       _7_._3_._3_._3  _T_h_e _S_e_t_e_n_v _S_t_a_t_e_m_e_n_t
       It is possible to assign to environment variables  with  the
       following statement.
         stmt             -----  +----+  -   +-----+  +
         ------- ---------s-e-t-e-n-v ---e-x-p-r-- -= ---e-x-p-r-s-- +; ----------

       When  this  statement  is executed, the environment variable
       whose name the left hand expression  evaluates  to  will  be
       assigned  the  value  that  the  right  hand expression list
       evaluates to.  It is an  error  if  the  variable  does  not
       already exist.

       For example:
            setenv PATH = [getenv PATH]":"[getenv HOME]/more-bin;

       _7_._3_._3_._4  _T_h_e _S_e_t_e_n_v_-_A_p_p_e_n_d _S_t_a_t_e_m_e_n_t
       It  is  possible  to  append  to the value of an environment
       variables with the following statement.
         stmt             -----  +----+  -   +-----+  +
         ------- ---------s-e-t-e-n-v ---e-x-p-r-- -+=---e-x-p-r-s-- +; ----------

       When this statement is executed,  the  environment  variable
       whose  name  the left hand expression evaluates to will have

       
       Peter Miller                                         Page 49





       Cook                                              User Guide



       its  value  appended  by  the  value  that  the  right  hand
       expression  list  evaluates  to.  Evaluation is analogous to
       the assign-append statement.

       For example:
            setenv FRED += nurk;

       _7_._3_._3_._5  _E_x_p_r_e_s_s_i_o_n_s
       Many definitions make reference to the _e_x_p_r_, _e_l_i_s_t and _e_x_p_r_s
       nonterminal symbols.  These are defined as follows.

       The _e_l_i_s_t is a list of at least one expression,
                                     ---------
                                   +  |------  +
        -e-l-i-s-t--------------------------expr +---------------------
                                      ------

       whereas the _e_x_p_r_s is a list of zero or more expressions.
        -e-x-p-r-s-----------------------------------------------------
                                   +           +
                                     --+----+--
                                       -e-l-i-s-t-


       An  expression  is  composed  of words, variable references,
       function invocations, or concatenation of expressions.   The
       concatenation  is  implied  by abutting the two parts of the
       expression together, _e_._g_._: "[fred]>thing" is an  indirection
       on _f_r_e_d concatenated with the literal word ">thing".
        expr                           ------
        ------------------------------ -_w-_o-_r-_d-----------------------
                             +     -   +----- -       +
                             |-----[ --elist+ ] ----- |
                             |     -   ------ -       |
                              --+----+- -----+----+--
                                --e-x-p-r-  -_c-_a-_t  --e-x-p-r-


       When  an  [[_e_l_i_s_t]]  expression  is  evaluated,  the  _e_l_i_s_t is
       evaluated first.  If the result is a  single  word,  then  a
       variable  of  that name is searched for.  If found the value
       of an expression of this form is the value of the  variable.

       If  there  is  no  variable  of the given name, or the _e_l_i_s_t
       evaluated to more than one word, the first word is taken  to
       be  a  built-in  function  name.  If there is no function of
       this name it is an error.

       The _c_a_t operator works as one would expect, joining the last
       word  of the left expression and the first word of the right
       expression together, and otherwise leaving the order of  the
       expressions  alone.   One  usually  uses the trivial case of
       single word expressions.  For more  complex  concatenations,
       see the [catenate] and [join] built-in functions.

       
       Peter Miller                                         Page 50





       Cook                                              User Guide



       _7_._3_._4  _R_e_c_i_p_e_s
       A  number  of  forms of _s_t_a_t_e_m_e_n_t are concerned with telling
       ccooookk how  to  cook  things.   There  are  three  forms,  the
       _e_x_p_l_i_c_i_t  recipe,  the  _i_m_p_l_i_c_i_t recipe, and the _i_n_g_r_e_d_i_e_n_t_s
       recipe.

       _7_._3_._5  _T_h_e _E_x_p_l_i_c_i_t _R_e_c_i_p_e _S_t_a_t_e_m_e_n_t
       The explicit recipe has the form
        stmt       +----+  +  +-----++-----+ +----++-----+-+---+
        ------------e-l-i-s-t--:+---e-x-p-r-s----f-l-a-g-s---g-a-t-e----c-s-t-m-t---u-s-e----

       The target(s) of the recipe are to the left  of  the  colon,
       and  the  ingredients,  if  any,  are  to  the  right.   The
       statements, usually commands, which are to be  performed  to
       (re)construct  the  target(s)  are contained in the compound
       statement.  The expressions are only  evaluated  into  words
       when  the  recipe is executed.  Recipe bodies may have local
       variables.

       For example:
            program: [program_obj]
            {
                    /* use [need] rather than [program_obj] in case
                       there are additional ingredients recipes
                       (see below).  */
                    cc -o program [need];
            }

       The target expressions and recipe flags are  evaluated  when
       the recipe is instantiated.  The ingredients expressions and
       the recipe gate are evaluated at graph building  time.   The
       body  and use statements are executed at graph walking time.

       The recipes also take a ``_h_o_s_t_-_b_i_n_d_i_n_g'' attribute.  See the
       chapter  on Cooking in Parallel for how this is attribute is
       written and used.  If the host binding flag is given, it  is
       always  used,  even  when not cooking in parallel.  If it is
       not given _a_n_d you are cooking in parallel, it  will  default
       to the contents of the [parallel_hosts] variable.

       _7_._3_._5_._1  _R_e_c_i_p_e _F_l_a_g_s
       The _f_l_a_g_s are defined as follows.
        -f-l-a-g-s-----------------------------------------------------
                                +                 +
                                  ----- -+-----+--
                                    -s-e-t  -e-x-p-r-s--

       Recipe  flags  are  evaluated  when  the  recipe targets are
       evaluated.  At this time, _n_o_n_e of the  [target],  [targets],
       [need]  or  [younger] variables are set, and neither are any
       of the pattern matches (%, %1, _e_t_c) available.

       A number of flags may be used


       
       Peter Miller                                         Page 51





       Cook                                              User Guide



       clearstat The last-modified  time  of  the  files  named  in
                 executed  commands  will be removed from the last-
                 modified  time  cache.   This  is  essential   for
                 commands such as _r_m(1) and _m_v(1).

       noclearstat Do not clear entries from the last-modified time
                 cache.  This is usually the default.

       default   If no targets are specified on the  command  line,
                 the  first  recipe  with  the _d_e_f_a_u_l_t flag will be
                 used.  Not meaningful for implicit recipes.

       nodefault If no targets are specified on the  command  line,
                 and  there  are  no  recipes with the _d_e_f_a_u_l_t flag
                 set, the first recipe wwiitthhoouutt the  _n_o_d_e_f_a_u_l_t  flag
                 will   be   used.   Not  meaningful  for  implicit
                 recipes.

       errok     Exit status from commands will be ignored.

       noerrok   If the _n_o_e_r_r_o_k flag  is  specified,  the  commands
                 within the actions bound to the recipe must always
                 be successful.  This is usually the default.

       fingerprint File fingerprints are used to  supplement  last-
                 modified  time  information  about files, which is
                 how _c_o_o_k determines if a file is  out-of-date  and
                 needs  to  be  cooked.   If a file appears to have
                 changed,  from  the  last-modified  time,  it   is
                 fingerprinted,  and  the fingerprint compared with
                 what it was in the past.  The file has changed  if
                 and  only  if the fingerprint has also changed.  A
                 cryptographically strong  hash  is  used,  so  the
                 chance  of  a  file  edit  producing  an identical
                 fingerprint   is   less   than   1   in    2**200.
                 Fingerprinting is disabled by default.

       nofingerprint Do  not  use  file  fingerprinting.   This  is
                 usually the default.

       forced    If the _f_o_r_c_e_d flag is specified, the actions bound
                 to the recipe will always be evaluated.

       noforced  If  the  _n_o_f_o_r_c_e_d  flag  is specified, the actions
                 bound to the recipe will  be  evaluated  when  the
                 recipe  is logically out-of-date.  This is usually
                 the default.

       gate-after-ingredients This flags causes the recipe gate  to
                 be  evaluated  after  the  ingredients  have  been
                 evaluated and determined to be cookable.  This  is
                 usually the default.



       
       Peter Miller                                         Page 52





       Cook                                              User Guide



       gate-before-ingredients This  flag causes the recipe gate to
                 be applied before the  ingredients  are  evaluated
                 and  determined to be cookable.  This is useful if
                 the ingredients  evaluation  itself  needs  to  be
                 conditional.

       implicit-ingredients
                 This  flag  may be used to specify that a recipe's
                 ingredients may be satisfied by implicit  recipes.
                 This is usually the default.

       no-implicit-ingredients
                 This  flag  may be used to specify that a recipe's
                 ingredients  may  not  be  satisfied  by  implicit
                 recipes;  this  is of most use with utilities such
                 as RCS where the  recipe  writer  knows  that  the
                 ingredients cannot be constructed.

       include-cooked-warning This  flag  may  be  used  to  enable
                 warnings when the relationship  between  a  target
                 and a derived ingredient appears only in a derived
                 cookbook.  This is usually the default.  This flag
                 is  only  meaningful  at the cookbook level, it is
                 not meaningful for individial recipes or commands.

       no-include-cooked-warning This  flag  may be used to disable
                 warnings when the relationship  between  a  target
                 and a derived ingredient appears only in a derived
                 cookbook.  This flag is  only  meaningful  at  the
                 cookbook   level,   it   is   not  meaningful  for
                 individial recipes or commands.

       ingredients-fingerprint This  flag  may  be  used  to  cause
                 recipes  to re-trigger when their ingredients list
                 chages in any way.  This is especially useful, for
                 example, in causing libraries to be rebuilt when a
                 content source file is removed.

       no-ingredients-fingerprint Cancel  any  active  _i_n_g_r_e_d_i_e_n_t_s_-
                 _f_i_n_g_e_r_p_r_i_n_t setting.

       match-mode-cook Use native Cook pattern matching.

       match-mode-regex Use   POSIX   regular   expression  pattern
                 matching.

       meter     If the _m_e_t_e_r flag is specified, a summary  of  the
                 CPU  usage by the commands within this recipe will
                 be printed after each command.  The silent options
                 override this option.

       nometer   Do  not  meter  commands.   This  is  usually  the
                 default.


       
       Peter Miller                                         Page 53





       Cook                                              User Guide



       mkdir     If the _m_k_d_i_r flag is specified, the directories of
                 any  targets  will  be  created before the actions
                 bound to the recipe are evaluated.

       nomkdir   If the _n_o_m_k_d_i_r flag is specified, the  directories
                 of  any  targets  will  need  to be created by the
                 actions bound to the recipe.  This is usually  the
                 default.

       precious  If  the _p_r_e_c_i_o_u_s flag is specified, if the actions
                 bound to the  recipe  fail,  the  targets  of  the
                 recipe will not be deleted.

       noprecious If  the  _n_o_p_r_e_c_i_o_u_s  flag  is  specified,  if the
                 actions bound to the recipe fail, the  targets  of
                 the  recipe  will be deleted.  This is usually the
                 default, so that erroneous  targets  will  be  re-
                 cooked.

       recurse   If  this  flag  is specified, recipes will recurse
                 upon  themselves  if  one  of  their   ingredients
                 matches  one  of  their  targets.   This can cause
                 problems, and so it is not the default.

       norecurse If this flag is specified,  the  recipe  will  not
                 recurse  if  one of its ingredients matches one of
                 its targets.  This is the default.

       silent    If the _s_i_l_e_n_t  flag  is  specified,  the  commands
                 within the actions bound to the recipe will not be
                 echoed.

       nosilent  Commands will be  echoed.   This  is  usually  the
                 default.

       stripdot  This  option  causes  ccooookk  to remove leading "./"
                 prefixes from  filenames.   This  is  usually  the
                 default.

       nostripdot This  option  causes  ccooookk  to leave leading "./"
                 prefixes on filenames.

       tell-position This  option  causes  the  filename  and  line
                 number  to  be  printed when echoing commands just
                 before they  are  executed,  in  addition  to  the
                 command itself.

       no-tell-position This  option  supresses the printing of the
                 filename and line  number  when  echoing  commands
                 just  before  they  are executed.  This is usually
                 the default.

       time-adjust This option  causes  ccooookk  to  check  the  last-
                 modified  time  of  the  targets  of  recipes, and

       
       Peter Miller                                         Page 54





       Cook                                              User Guide



                 adjust them if necessary, to make  sure  they  are
                 consistent  with  (younger than) the last-modified
                 times of the ingredients.   This  usually  adjusts
                 the  file  time into the (near) future.  A warning
                 message will be  printed,  telling  you  how  many
                 seconds  the  file  was adjusted.  This results in
                 more system calls, and can  slow  things  down  on
                 some systems6.

       no-time-adjust Do  not  adjust  the file last-modified times
                 after performing the body of a  recipe.   This  is
                 usually the default.

       time-adjust-back  This option causes ccooookk to force the last-
                 modified time of the  targets  of  recipes  to  be
                 exactly one (1) second younger than their youngest
                 ingredient.  This usually adjusts  the  file  time
                 into the (recent) past.  A warning message will be
                 printed, telling you how many seconds the file was
                 adjusted.   This results in more system calls, and
                 can slow things down on  some  systems.   This  is
                 primarily  useful when some later process is going
                 to compress file modification times; this provides
                 smarter compression.

       unlink    If  the  _u_n_l_i_n_k  flag is specified, of any targets
                 will be unlinked before the actions bound  to  the
                 recipe are performed.

       nounlink  If  the  _n_o_u_n_l_i_n_k  flag  is  specified, the recipe
                 targets are not removed before the  actions  bound
                 to  the recipe are performed.  This is usually the
                 default.

       Each flag may also be specified in the negative, by adding a
       "no"  prefix,  to  override  any  existing  positive default
       setting.  There is  a  strict  precedence  defined  for  the
       various levels of flag setting, see the end of the "How Cook
       Works" chapter for details.

       _7_._3_._5_._2  _R_e_c_i_p_e _G_a_t_e
       Each recipe  may  have  a  _g_a_t_e.   The  gate  is  a  way  of
       specifying  a  conditional  recipe;  if the condition is not
       true, the recipe is not used.  The condition is in  addition
       to the condition that the ingredients are cookable.





       ____________________

       6. This  flag  was once named the ``update'' flag.  The name
          was changed to more closely reflect  its  function.   The
          old name continues to work.

       Peter Miller                                         Page 55





       Cook                                              User Guide



        -g-a-t-e------------------------------------------------------
                                 +                +
                                  ---- --+----+--
                                     -if  --e-x-p-r-


       For example:
            program: [program_obj]
                    if [not [in horrible.o [program_obj]]]
            {
                    cc -o program [program_obj];
            }

       _7_._3_._5_._3  _T_h_e_n _C_l_a_u_s_e
       There  are  times when it is necessary to know that a recipe
       has been applied, but because the recipe was up-to-date, the
       recipe body was not run.
        -u-s-e-------------------------------------------------------
                               +                   +
                                 --------+-----+--
                                   t-h-e-n- --c-s-t-m-t-

       The  then-clause  is  run  every time the recipe is applied,
       even if the recipe is up-to-date.  It will be run after  the
       recipe  body,  if  the recipe body is run.  All of the usual
       percent  (%)  substitutions  and  automatic  variables  will
       apply.  Recipe then-clauses may have local variables.

       For example:
            program: [program_obj]
            {
                    cc -o program [program_obj];
            }
            then
            {
                    install-set += program;
            }

       _7_._3_._5_._4  _D_o_u_b_l_e _C_o_l_o_n
       Most  cookbooks  are  constructed  so  that  if ccooookk finds a
       suitable recipe for the target it is currently constructing,
       it  will  apply  the  recipe  and  then conclude that it has
       finished constructing the target.  In some  rare  cases  you
       will  want  ccooookk  to keep going after applying a recipe.  To
       specify this use a ``double colon'' construction:
        stmt       +----+  +  +-----++-----+ +----++-----+-+---+
        ------------e-l-i-s-t--:+:---e-x-p-r-s----f-l-a-g-s---g-a-t-e----c-s-t-m-t---u-s-e----

       This operates like a normal explicit recipe, but  ccooookk  will
       continue on looking for recipes after applying this one.  As
       soon as an applicable ``single colon'' recipe is  found  and
       applied,   ccooookk   will   conclude   that   it  has  finished
       constructing the target.


       
       Peter Miller                                         Page 56





       Cook                                              User Guide



       For example:
            all:: programs
            {
                    [print "all programs done"];
            }
            all:: libraries
            {
                    [print "all libraries done"];
            }

       _7_._3_._6  _T_h_e _I_m_p_l_i_c_i_t _R_e_c_i_p_e _S_t_a_t_e_m_e_n_t
       Implicit recipes are distinguished from explicit recipes  in
       that  and  implicit recipe has a target with a '%%' character
       in it.

       _7_._3_._6_._1  _S_i_m_p_l_e _F_o_r_m


       In general the user will rarely need  to  use  the  implicit
       recipe  form,  as there are a huge range of implicit recipes
       already defined in the system default recipes.

       An example of this recipe form is
            %: %.gz
            {
                    gzcat %.gz > %;
            }
       This recipe tells ccooookk how to use the _g_z_c_a_t(1) program.

       _7_._3_._6_._2  _C_o_m_p_l_e_x _F_o_r_m
       The implicit recipe recipe has a second form where there are
       two  sets  of  ingredients,  separated by another colon.  In
       this  form,  the  ingredients   specified   in   the   first
       ingredients  list are used to determine the applicability of
       the recipe; if these are all constructible then  the  recipe
       will  be  applied,  if  any  are  not constructible then the
       recipe will not be applied.  If the recipe is  applied,  the
       ingredients  specified  in  the  second ingredients list are
       required to be constructible.  The  the  second  ingredients
       list section is known as the _f_o_r_c_e_d _i_n_g_r_e_d_i_e_n_t_s section.

       NNoottee:: if you want the first ingredients list to be empty you
       _m_u_s_t separate the two colons with a  space,  otherwise  ccooookk
       will think this is a ``double colon'' recipe.

       An example of this is the C recipe
            %.o: %.c: [collect c_incl -api %.c]
            {
                    cc -c %.c;
            }
       This  recipe  is applied if the _%_._c file can be constructed,
       and is not applied if it cannot be constructed.  The include
       dependencies are only expressed if the recipe is going to be
       applied;  but  if  they  are   expressed,   they   _m_u_s_t   be

       
       Peter Miller                                         Page 57





       Cook                                              User Guide



       constructible.    This   means  that  absent  include  files
       generate an error7.

       The naive form of this recipe
            %.o: %.c [collect c_incl -api %.c]
            {
                    cc -c %.c;
            }
       will attempt to apply the _c___i_n_c_l command before the _%_._c file
       is  guaranteed  to  exist.   This  is  because the _e_x_p_r_s_2 is
       performed after the  _e_x_p_r_s_1  all  exist  (because  they  are
       constructible,  they  have been constructed).  In this naive
       form, absent include files result in the  recipe  not  being
       applied.

       _7_._3_._6_._3  _D_o_u_b_l_e _C_o_l_o_n
       Just as explicit recipes have a ``double colon'' form, so do
       both  types  of  implicit  recipes.    The   semantics   are
       identical,  with  ccooookk  looking for more than one applicable
       implicit recipe, but stopping  if  it  finds  an  applicable
       ``single colon'' implicit recipe.

       As  stated  earlier  in  this  manual,  ccooookk first scans for
       explicit recipes before scanning for implicit  recipes.   If
       an explicit recipe has been applied, ccooookk will not also look
       for applicable implicit recipes, even if all the  applicable
       explicit recipes were double colon recipes.

       _7_._3_._7  _T_h_e _I_n_g_r_e_d_i_e_n_t_s _R_e_c_i_p_e _S_t_a_t_e_m_e_n_t
       The ingredients recipe has the form
        stmt            +----+  +  +-----++-----++----+   +
        -----------------e-l-i-s-t- +: ---e-x-p-r-s---f-l-a-g-s----g-a-t-e-- +; -------

       The  target(s)  of  the recipe are to the left of the colon,
       and the prerequisites  are  to  the  right.   There  are  no
       statements to perform to cook the targets of this recipe, it
       is simply supplementary to  any  other  recipe,  usually  an
       implicit recipe.

       For example:
            program: batman.o robin.o;

       The  right-hand-side  expressions  are  only  evaluated into
       words when the recipe is instantiated.

       Ingredients recipes are usually explicit,  but  it  is  also
       valid to use implicit ingredients recipes.

       For example:

       ____________________

       7. This is not the recommended way of determining C  include
          dependencies,  see  the  ``Include Dependencies'' chapter
          for more information.

       Peter Miller                                         Page 58





       Cook                                              User Guide



            some-%-program: %.o;

       _7_._3_._8  _T_h_e _C_a_s_c_a_d_e _R_e_c_i_p_e _S_t_a_t_e_m_e_n_t
       The cascade recipe statement has the form
          stmt            ------  +----+  -   +----+  +
         ------- ---------c-a-s-c-a-d-e --e-l-i-s-t-- -= ---e-l-i-s-t- +; ----------

       This  recipe  specifies  on  its  right-hand-side additional
       ingredients for any recipe which has  ingredients  mentioned
       on the left-hand-side of this cascade recipe.

       Unlike  all  other recipe forms, both the left-hand-side _a_n_d
       the  right-hand-side  are  evaluated  when  the  recipe   is
       instantiated.

       For example:
            cascade batman.c = robin.h;
            cascade somelib.a = some-deeper-lib.a;

       _7_._3_._9  _C_o_m_m_a_n_d_s
       Commands  may take several forms in ccooookk.  They all have one
       thing in common; they execute a command.

       _7_._3_._1_0  _T_h_e _S_i_m_p_l_e _C_o_m_m_a_n_d _S_t_a_t_e_m_e_n_t
       The simplest command form is
        stmt                     +----++-----+  +
        --------------------------e-l-i-s-t---f-l-a-g-s-- +;-----------------

       When executed, the _e_l_i_s_t is evaluated into a word  list  and
       used  as a command to be passed to the operating system.  On
       UNIX this usually means that a shell is invoked to  run  the
       command,   unless   the   string  contains  no  shell  meta-
       characters.

       The _f_l_a_g_s are those which may be specified in  the  explicit
       recipe statement.  They have a higher precedence than either
       the _s_e_t statement or the recipe flags.

       Some characters in commands are special both  to  the  shell
       and  to  cook.   You  will  need  to  quote  or escape these
       characters.  Each command is executed in a separate process,
       so the cd command will not work, you will need to combine it
       with the relevant commands, not  forgetting  to  escape  the
       semicolon (;) characters.

       When  Cook  needs to invoke a shell to execute a command, it
       uses the shell named in the SHELL environment variable.   If
       the  cookbook is to be used by a variety of users, each with
       a different shell setting, it may be useful to add a
            setenv SHELL = /bin/sh;
       line at the top of your cookbook.

       It is also important to note that unless the _e_r_r_o_k flag  has
       been specified, the shell will be given the -e option, which

       
       Peter Miller                                         Page 59





       Cook                                              User Guide



       will cause it to exit immediately after  the  first  command
       which returns a non-zero exit status.  This can be important
       when commands in the _._p_r_o_f_i_l_e or _._b_a_s_h_r_c (or  similar)  file
       fails.

       _7_._3_._1_1  _T_h_e _D_a_t_a _C_o_m_m_a_n_d _S_t_a_t_e_m_e_n_t
       For  programs  which require _s_t_d_i_n to be supplied by ccooookk to
       perform their functions, the data command statement has been
       provided.
        stmt         |-----++-----+  +   ---- |-----+  ------
        ---------------e-l-i-s-t--f-l-a-g-s-- +; --d-a-t-a----e-x-p-r--d-a-t-a-e-n-d -----

       In this form, the _e_x_p_r is evaluated and used as input to the
       command.   Between  the  ddaattaa  and  ddaattaaeenndd   keywords   the
       definition  of  the  special  symbols and whitespace change.
       There are only two  special  symbols,  [[  and  ]],  to  allow
       functions   and   variable   references  to  appear  in  the
       expression.  In addition,  whitespace  ceases  to  have  its
       usual specialness; it is handed to the command, instead.

       For  those  of you familiar with writing shell scripts, this
       is analogous to _h_e_r_e documents.  It allows you to create  an
       input  file without creating an explicit temporary file.  It
       also allows you to create files that you  could  not  create
       using _e_c_h_o redirected into the file8.

       The ddaattaa keyword must be the  last  on  a  line,  whitespace
       after the ddaattaa keyword up to and including end-of-line, will
       _n_o_t be given to the command.

       The ddaattaaeenndd keyword must appear alone on a line,  optionally
       surrounded  by  whitespace;  if it is not alone, it is not a
       ddaattaaeenndd keyword and will not terminate the expression.

       An example of this may be useful.
            /usr/fred/%: %
            {
                    newgrp fred;
            data
            cp % /usr/fred/%
            dataend
            }
       The _n_e_w_g_r_p(1) command is used to change the default group of
       a process, and then throw a shell; so the ``cp'' is executed
       by this sub-shell when it reads its standard input.  If  the
       directory  _/_u_s_r_/_f_r_e_d  has  read-only permissions for others,
       and group write permissions, and belonged to group _f_r_e_d, and
       you  were  a member of group _f_r_e_d, the above implicit recipe
       could be used to copy the file.


       ____________________

       8. For  example,  Windows NT has a ludicrously small command
          line length limit.

       Peter Miller                                         Page 60





       Cook                                              User Guide



       Here is an example of how to cope  with  stupidly  short  NT
       command lines:
            %.LIB: [%_obj]
            {
                    cat > %.contents;
            data
            [unsplit "\n" [unix-to-dos [need]]]
            dataend
                    link -lib "/out:"[unix-to-dos [target]] @%.contents;
                    rm %.contents;
            }
       The  ``@_s_o_m_e_t_h_i_n_g''  means the linker should read file names
       from the _s_o_m_e_t_h_i_n_g file.

       This technique will also work with Unix  if  you  have  more
       then  5MB  of  command  line  arguments  _a_n_d  the program is
       written to have an option something like this (many  have  a
       --ff option).

       _7_._3_._1_2  _T_h_e _S_e_t _S_t_a_t_e_m_e_n_t
       It is possible to override the defaults used by ccooookk or even
       those specified by the _C_O_O_K environment variable,  by  using
       the _s_e_t statement.
       stmt                       ---  +-----+  +
       ----------------------------s-e-t---e-x-p-r-s-- +; ------------------

       The  flag  values are those mentioned in the _f_l_a_g_s clause of
       the explicit recipe statement.   Many  command-line  options
       have  equivalent  flag  settings.   There  is  no  ``unset''
       statement, to  restore  the  default  settings,  but  it  is
       possible  to  set flags the other way, by adding or removing
       the ``no'' prefix.

       To set flags for individual recipes, use the _f_l_a_g_s clause of
       the recipe statements.

       To  set  flags for individual commands, use the _f_l_a_g_s clause
       of the command statements.

       _7_._3_._1_2_._1  _E_x_a_m_p_l_e_s
       Fingerprinting is not used by default, because it can  cause
       a  few  surprises,  and  takes a little more CPU.  To enable
       fingerprinting for you project, place the statement
            set fingerprint;
       somewhere near the  start  of  your  _H_o_w_t_o_._c_o_o_k  file.   The
       --NNoo__FFiinnggeerrPPrriinntt command line option can still override this,
       but the default behavior will be to use fingerprints.

       To prevent echoing of commands as they are executed, place
            set silent;
       somewhere in your _H_o_w_t_o_._c_o_o_k file.   The  --NNooSSiilleenntt  command
       line  option  can  still  override  this,  but  the  default
       behavior will be not to echo commands.


       
       Peter Miller                                         Page 61





       Cook                                              User Guide



       _7_._3_._1_3  _T_h_e _F_a_i_l _S_t_a_t_e_m_e_n_t
       CCooookk can be forced to think that a recipe has failed by  the
       uses of the ffaaiill statement.
        stmt                      ---  +-----+  +
        -------------------------f-a-i-l --e-x-p-r-s---;+-----------------

       This  is  hugely useful when programs do not return a useful
       exit status, but _d_o fail.  If they  have  printed  an  error
       message, but not produced the output file, you could use the
       Fail statement without arguments:
            fred: other stuff
                    set unlink
            {
                    brain-dead [need] -o [target];
                    if [not [exists [target]]] then
                            fail;
            }

       If you give the Fail statement any arguments, they  will  be
       printed as an error message before the recipe fails:
            fred: other stuff
                    set unlink
            {
                    brain-dead [need] -o [target];
                    if [not [exists [target]]] then
                            fail Did not produce [target] file.;
            }

       _7_._3_._1_4  _T_h_e _I_f _S_t_a_t_e_m_e_n_t
       The if statement has one of two forms.
         stmt    -   +----+  ---- |-----+
         -------i-f ---e-x-p-r---t-h-e-n----s-t-m-t-------------------------
                                           +   ----  +----+  +
                                            ----e-l-s-e---s-t-m-t----


       In  nested  if statements, the eellssee will bind to the closest
       _e_l_s_e-less _i_f.  An expression is false if and only if all  of
       its words are null or it has no words.

       Note  that  one or both of the subordinate statements may be
       compound statements, should you need to say  something  more
       complex than a single statement.

       _7_._3_._1_5  _T_h_e _L_o_o_p _a_n_d _L_o_o_p_e_n_d _S_t_a_t_e_m_e_n_t_s
       Looping is provided for in ccooookk by the generic infinite loop
       construct defined below.
        stmt                        ----  +----+
        -----------------------------l-o-o-p----s-t-m-t-------------------

       A facility is provided to break out of a loop at any  point.
        stmt                        -------   +
        ---------------------------l-o-o-p-s-t-o-p--;+-------------------


       
       Peter Miller                                         Page 62





       Cook                                              User Guide



       The  statement  following  the  lloooopp  directive  is executed
       repeatedly  forever.   The  llooooppssttoopp   statement   is   only
       semantically valid within the scope of a lloooopp statement.

       Here is an example of how to use the loop statement:
            dirs = a b c d;
            src = ;

            tmp = [dirs];
            loop
            {
                    tmp_dir = [head [tmp]];
                    if [not [tmp_dir]] then
                            loopstop;
                    tmp = [tail [tmp]];
                    src = [src] [glob [tmp_dir]"/*.c"];
            }

       There  is  also a ``for each'' loop variant, allowing a more
       terse expression of exactly the same thing
            dirs = a b c d;
            src = ;

            loop tmp_dir = [dirs]
            {
                    src = [src] [glob [tmp_dir]"/*.c"];
            }
       You can use loopstop within such a loop.  Note that the loop
       body _m_u_s_t be a compound statement.

       _7_._3_._1_6  _F_u_n_c_t_i_o_n_s
       It is possible to define your own functions.

       _7_._3_._1_6_._1  _F_u_n_c_t_i_o_n _D_e_f_i_n_i_t_i_o_n
       User-defined functions are specified using something similar
       to an assignment.
        function           -------   ------   -   +-----+
        -------------------fu-n-c-t-i-o-n-- --_w-_o-_r-_d--- -= ---c-s-t-m-t-----------

       Functions must be defined before they are used.

       You need to make sure  you  do  not  re-define  a  built-in-
       function as this may have dire consequences.

       _7_._3_._1_6_._2  _T_h_e _R_e_t_u_r_n _S_t_a_t_e_m_e_n_t


       You  return  values  from  a  function  by  using the return
       statement:
        stmt                     -----  +-----+  +
        -------------------------r-e-t-u-r-n---e-x-p-r-s---;+----------------

       Note that return statements are  not  meaningful  outside  a
       function definition.

       
       Peter Miller                                         Page 63





       Cook                                              User Guide



       _7_._3_._1_6_._3  _F_u_n_c_t_i_o_n _A_r_g_u_m_e_n_t_s
       The  arguments  to  the  function  are passed in the ``arg''
       variable.  Each argument is also separately defined  in  the
       ``@1'' to ``@9'' variables for direct access.  (If there are
       more than 9, you will need to use  ``[word  _n  [arg]]''  for
       argument 10 and later).  These variables are unique for each
       function invocation, even if they are nested.

       You  can  use  the  ``@1''  to  ``@9''  variables  as  local
       variables if you have no need of their values.

       All  of  these  special  names are thread safe and recursion
       safe.  Every function invokation receives  its  own  set  of
       them.

       _7_._3_._1_6_._4  _E_x_a_m_p_l_e
       An  example  of  a  function  definition is a ``capitalize''
       function:
            function capitalize =
            {
                    @1 = ;
                    loop @2 = [downcase [arg]]
                    {
                            @1 += [upcase [substr 1 1 [@2]]][substr 2 99 [@2]];
                    }
                    return [@1];
            }
       This function capitalizes the first letter of  each  of  its
       arguments.

       User-defined  functions are invoked in the same way a built-
       in functions.
            host = [os node];
            Host = [capitalize [host]];

       See the ``Function Library'' section for additional function
       examples which are distributed with Cook.

       _7_._3_._1_6_._5  _F_u_n_c_t_i_o_n _C_a_l_l _S_t_a_t_e_m_e_n_t
       User  defined  functions  may  be invoked in the same way as
       built-in functions, but they may also be invoked in the same
       way as commands, providing a form of subroutine.
        stmt                 --------  +-----+  +
        -------------------- -f-u-n-c-t-i-o-n --e-l-i-s-t---;+-----------------


       If  the  function return value is not zero, it is considered
       to fail, just as a command would fail.  The commonest use of
       this  is to invoke the built-in print function for debugging
       cookbooks.
            function print [__FILE__] [__LINE__] hello [getenv USER];

       These function calls may be used in recipe bodies, or in the
       general cookbook.

       
       Peter Miller                                         Page 64





       Cook                                              User Guide



       _7_._3_._1_6_._6  _L_o_c_a_l _V_a_r_i_a_b_l_e_s
       Functions  can have local variables simply by using the word
       local on the left-hand-side of the assignment.   Care  needs
       to  be  taken with the loop statement and the += assignment,
       as the variable needs to be established as a local  variable
       _f_i_r_s_t.
            function capitalize =
            {
                    local result = ;
                    local tmp = ;
                    loop tmp = [downcase [arg]]
                    {
                            result += [upcase [substr 1 1 [tmp]]][substr 2 99 [tmp]];
                    }
                    return [result];
            }
       Functions may have as many local variables as they like.

       Local  variables  are  reentrant.   You  can write recursive
       functions, and  each  invocation  of  the  function  has  an
       independent set of local variables.

       Local variables are thread-safe.  You can use the same user-
       defined function in two parallel threads,  and  their  local
       variables are completely independent.

       The  ``arg''  and  ``@1'' to ``@9'' variables are implicitly
       local.



























       
       Peter Miller                                         Page 65





       Cook                                              User Guide



       _8_.  _B_u_i_l_t_-_I_n _F_u_n_c_t_i_o_n_s

       This chapter defines each of the built-in functions of _c_o_o_k.

       A built-in function is invoked by using an expression of the
       form
            [[_f_u_n_c_-_n_a_m_e _a_r_g _a_r_g ...]]
       in most places where a literal word is valid.

       _8_._1  _a_d_d_p_r_e_f_i_x

       The _a_d_d_p_r_e_f_i_x function is used to add a prefix to a list  or
       words.   This  function requires at least one argument.  The
       first argument is a prefix to be added  to  the  second  and
       subsequent arguments.

       _8_._1_._1  _S_e_e _A_l_s_o
       addsuffix, patsubst, prepost, subst

       _8_._2  _a_d_d_s_u_f_f_i_x

       The  _a_d_d_s_u_f_f_i_x function is used to add a suffix to a list or
       words.  This function requires at least one  argument.   The
       first  argument  is  a  suffix to be added to the second and
       subsequent arguments.

       _8_._2_._1  _S_e_e _A_l_s_o
       addprefix, patsubst, prepost, subst

       _8_._3  _a_n_d

       This function requires at least two arguments, upon which it
       forms  a  logical  conjunction.   The  value returned is "1"
       (true) if none of the arguments are "" (false), otherwise ""
       (false) is returned.

       _8_._3_._1  _E_x_a_m_p_l_e
       The  following  cookbook fragment shows how to use the [and]
       function in conditional recipes.
            #if [and [defined change] [defined baseline]]
            _._._._d_o _s_o_m_e_t_h_i_n_g_._._.
            #endif
       This fragment will only _d_o _s_o_m_e_t_h_i_n_g if both the _c_h_a_n_g_e  and
       _b_a_s_e_l_i_n_e variables are defined.

       _8_._3_._2  _C_a_v_e_a_t
       This  function  is  rather  clumsy, and probably needs to be
       replaced by a  better  syntax  within  the  cokbook  grammar
       itself.

       This function does not short-circuit evaluation.




       
       Peter Miller                                         Page 66





       Cook                                              User Guide



       _8_._3_._3  _S_e_e _A_l_s_o
       or, not

       _8_._4  _b_a_s_e_n_a_m_e

       The _b_a_s_e_n_a_m_e treats each argument as filenames, and extracts
       all but the  suffix  of  each  filename.   If  the  filename
       contains a period, the basename is everything up to (but not
       including) the  period.   Otherwise,  the  basename  is  the
       entire filename.

       Please  note:  this  is  not  the  same behavior as the Unix
       _b_a_s_e_n_a_m_e(1) utility.  For this, [basename [notdir _a_r_g_s]]  or
       [fromto %0%.c %0% _a_r_g_s] may be more appropriate.

       _8_._4_._1  _E_x_a_m_p_l_e


                   Expression               Result
                   -------------------------------------
                   [basename foo.c]         foo
                   [basename foo/bar.c]     foo/bar
                   [basename baz]           baz
                   [basename foo/bar/baz]   foo/bar/baz

       _8_._4_._2  _S_e_e _A_l_s_o
       addsuffix, dirname, entryname, fromto, notdir, suffix

       _8_._4_._3  _C_a_v_e_a_t
       This function is almost nothing like the Unix command of the
       same name.  It operates in  this  manner  for  compatibility
       with other packages.

       _8_._5  _c_a_n_d_o

       This function is used to test whether Cook knows how to cook
       the given targets.  It returns  all  of  the  arguments  for
       which derivations can be found, or nothing if none can.

       _8_._5_._1  _C_a_v_e_a_t
       This will use as much of the cookbook as has been read in up
       to the point where this function is  used.   This  can  mean
       that crucial recipes have yet to be parsed and instantiated.

       _8_._5_._2  _S_e_e _A_l_s_o
       cook, uptodate









       
       Peter Miller                                         Page 67





       Cook                                              User Guide



       _8_._6  _c_a_t_e_n_a_t_e

       This function  requires  zero  or  more  arguments.   If  no
       arguments  are  supplied,  the result is an empty word list.
       If one or more arguments are supplied, the result is a  word
       list  of  one  word  being  the  catenation  of  all  of the
       arguments.

       _8_._6_._1  _E_x_a_m_p_l_e


                       Expression           Result
                       ----------------------------
                       [catenate a]         a
                       [catenate a b]       ab
                       [catenate a " " b]   "a b"
       Quotes used in the results for clarity.

       _8_._6_._2  _S_e_e _A_l_s_o
       split, unsplit, prepost, join

       _8_._7  _c_o_l_l_e_c_t___l_i_n_e_s

       The arguments are interpreted as a command to be  passed  to
       the  operating  system.   The  result is one "word" for each
       line of the output of the command.

       _8_._7_._1  _E_x_a_m_p_l_e
       To read each line of a file into a variable:
            files = [collect_lines cat file];
       Spaces and tabs in the input lines will be preserved in  the
       "words" of the result.

       _8_._7_._2  _S_e_e _A_l_s_o
       collect, execute, glob, read, read_lines, write

       _8_._7_._3  _C_a_v_e_a_t
       You will probably get better performance using the #include-
       cooked directive, and a recipe to create the included  file.
















       
       Peter Miller                                         Page 68





       Cook                                              User Guide



       _8_._8  _c_o_l_l_e_c_t

       The  arguments  are interpreted as a command to be passed to
       the operating system.  The  result  is  one  word  for  each
       white-space separated word of the output of the command.

       The  command will not be echoed unless the -No_Silent option
       is specified on the command line.

       _8_._8_._1  _E_x_a_m_p_l_e
       Read the date and time and assign it to a variable:
            now = [collect date];
       Do not  use  the  collect  function  to  expand  a  filename
       wildcard, used the [glob] function instead.

       _8_._8_._2  _S_e_e _A_l_s_o
       collect_lines, execute, glob, read, read_lines, write

       _8_._8_._3  _A_l_s_o _K_n_o_w_n _A_s
       shell

       _8_._9  _c_o_o_k

       This  function  requires one or more arguments, filenames to
       be tested to see if they are up-to-date, and be brought  up-
       to-date  if  they are not.  The result are true ("1") if the
       files are (now) up-to-date, or false ("") if they could  not
       be built.

       _8_._9_._1  _C_a_v_e_a_t
       This will use as much of the cookbook as has been read in up
       to the point where this function is  used.   This  can  mean
       that crucial recipes have yet to be parsed and instantiated.

       This function works one argument at a time.  This is  slower
       than  the  main  cookbook,  which  will  pursue  all targets
       simultaneously.

       _8_._9_._2  _S_e_e _A_l_s_o
       cando, uptodate















       
       Peter Miller                                         Page 69





       Cook                                              User Guide



       _8_._1_0  _c_o_u_n_t

       This function requires zero or more arguments.   The  result
       is  a  word list of one word containing the (decimal) length
       of the argument word list.

       _8_._1_0_._1  _E_x_a_m_p_l_e
       This cookbook fragment echoes the number of files, and  then
       the name of the last file:
            echo There are [count [files]] files.;
            echo The last file is [word [count [files]] [files]].;

       _8_._1_0_._2  _S_e_e _A_l_s_o
       head, tail, word

       _8_._1_0_._3  _A_l_s_o _K_n_o_w_n _A_s
       words

       _8_._1_1  _d_e_f_i_n_e_d

       This  function  requires  a  single  argument, the name of a
       variable to be tested for existence.  It returns "1"  (true)
       if  the  named  variable  is defined and "" (false) if it is
       not.

       _8_._1_1_._1  _E_x_a_m_p_l_e
       This function is most often seen in conditional portions  of
       cookbooks:
            if [defined baseline] then
                    cc_flags = [cc_flags] -I[baseline];

       _8_._1_2  _d_i_r_n_a_m_e

       This  function  requires one or more arguments, the names of
       files which will have their directory parts extracted.

       _8_._1_2_._1  _E_x_a_m_p_l_e


                         Expression        Result
                         -------------------------
                         [dirname a]       _`_p_w_d_`
                         [dirname a/b]     a
                         [dirname a/b/c]   a/b
       When the answer would be ``.'' (the current directory),  the
       result   is   instead  the  absolute  path  of  the  current
       directory.  This allows repeated [dirname]  applications  to
       climb  the  directory  tree, no matter where you start.  See
       _r_e_l_a_t_i_v_e___d_i_r_n_a_m_e for one which returns ``.'' instead.

       _8_._1_2_._2  _S_e_e _A_l_s_o
       basename,  entryname,  notdir,  pathname,  relative_dirname,
       suffix


       
       Peter Miller                                         Page 70





       Cook                                              User Guide



       _8_._1_2_._3  _A_l_s_o _K_n_o_w_n _A_s
       dir

       _8_._1_3  _d_i_r

       This  function  requires one or more arguments, the names of
       files which will have their directory parts extracted.

       _8_._1_3_._1  _E_x_a_m_p_l_e


                           Expression    Result
                           ---------------------
                           [dir a]       .
                           [dir a/b]     a
                           [dir a/b/c]   a/b

       _8_._1_3_._2  _S_e_e _A_l_s_o
       basename,  entryname,  notdir,  pathname,  relative_dirname,
       suffix

       _8_._1_3_._3  _A_l_s_o _K_n_o_w_n _A_s
       dirname

       _8_._1_4  _d_o_s_-_p_a_t_h

       This  function requires one or more arguments, which will be
       converted from a UNIX path into a DOS path.  This is of most
       use  under  Windows-NT, to convert Cook's internal pathnames
       into DOS pathnames.  (The UNIX porting layer  usually  hides
       this from Cook.)

       _8_._1_4_._1  _E_x_a_m_p_l_e


                Expression                  Result
                -------------------------------------------
                [dos-path a/b/c]            a\b\c
                [dos-path //c/temp]         c:\temp
                [dos-path //server/stuff]   \\server\stuff

       _8_._1_4_._2  _S_e_e _A_l_s_o
       un-dos-path












       
       Peter Miller                                         Page 71





       Cook                                              User Guide



       _8_._1_5  _d_o_w_n_c_a_s_e

       This  function  requires  one or more arguments, words to be
       forced into lower case.

       _8_._1_5_._1  _E_x_a_m_p_l_e


                         Expression       Result
                         ------------------------
                         [downcase FOO]   foo
                         [downcase Bar]   bar
                         [downcase baz]   baz

       _8_._1_5_._2  _S_e_e _A_l_s_o
       upcase

       _8_._1_6  _e_n_t_r_y_n_a_m_e

       This function requires one or more arguments, the  names  of
       files which will have their entry name parts extracted.

       _8_._1_6_._1  _E_x_a_m_p_l_e


                      Expression              Result
                      -------------------------------
                      [entryname foo.c]       foo.c
                      [entryname foo/bar.c]   bar.c
                      [entryname baz]         baz

       _8_._1_6_._2  _S_e_e _A_l_s_o
       basename, dir, suffix

       _8_._1_6_._3  _A_l_s_o _K_n_o_w_n _A_s
       notdir

       _8_._1_7  _e_x_e_c_u_t_e

       This  function  requires at least one argument, and executes
       the command given by the arguments.  If the executed command
       returns  non-zero  exit  status  the  resulting  value is ""
       (false), otherwise it is "1" (true).

       The command will not be echoed unless the -No_Silent  option
       is specified on the command line.

       _8_._1_7_._1  _C_a_v_e_a_t
       This  function is not often required as its functionality is
       available in a more useful form as recipe bodies.

       _8_._1_7_._2  _E_x_a_m_p_l_e
       To get access to a wide  range  of  Unix  command,  such  as
       _t_e_s_t(1), you can use this function in conditionals

       
       Peter Miller                                         Page 72





       Cook                                              User Guide



            if [not [test -d fubar]] then
            {
                    rm -f fubar;
                    mkdir fubar;
            }

       _8_._1_7_._3  _S_e_e _A_l_s_o
       collect

       _8_._1_8  _e_x_i_s_t_s

       This  function  requires  one  argument, being the name of a
       file to test for existence.  The resulting word list  is  ""
       (false)  if  the  file does not exist, and "1" (true) if the
       file does exist.

       _8_._1_8_._1  _E_x_a_m_p_l_e
       To remove the target of a recipe before building it again:
            %.a: [%_obj]
            {
                    if [exists [target]] then
                            rm [target]
                                    set clearstat;
                    [ar] qc [target] [%_obj];
            }
       Note: you _m_u_s_t use the clearstat, because  otherwise  cook's
       "stat cache" will be incorrect.

       This  is  only  an  example.   It  is better to perform this
       particular activity using  the  ``unlink''  flag.   See  the
       [find_command] function, below, for an example.

       _8_._1_8_._2  _S_e_e _A_l_s_o
       cando, find_command, uptodate

       _8_._1_9  _e_x_i_s_t_s_-_s_y_m_l_i_n_k

       This  function  requires  one  argument, being the name of a
       file to test  for  existence.   The  test  will  _n_o_t  follow
       symbolic  links, so it may be used to test for the existence
       of symbolic links themselves.  The resulting word list is ""
       (false)  if  the  file does not exist, and "1" (true) if the
       file does exist.

       _8_._1_9_._1  _S_e_e _A_l_s_o
       exists, readlink









       
       Peter Miller                                         Page 73





       Cook                                              User Guide



       _8_._2_0  _e_x_p_r

       This function  may  be  used  to  calculate  simple  integer
       arithmetic  expressions.   The numbers and the operators are
       expected to each be a separate argument.  The  result  is  a
       string containing the value of the evaluated expression.

       _8_._2_0_._1  _O_p_e_r_a_t_o_r_s
       The  following operators are understood.  They have the same
       precedence as the equivalent C operators.

                        Operator    Associativity
                        --------------------------
                        ( )              ->
                        ! ~ -            <-
                        * / %            ->
                        + -              ->
                        << >>            ->
                        < <= > >=        ->
                        == !=            ->
                        &                ->
                        ^                ->
                        |                ->
                        &&               ->
                        ||               ->
                        ?:               <-
       Please note that there is no short-circuit evaluation of the
       ?: or && or || operators.

       You  may  need  to  quote some of the operators, to insulate
       them from their usual Cook interpretation (colon and  equals
       characters in particular).

       Numbers may be given in decimal, octal (with a 0 prefix), or
       hexadecimal (with  a  0x  prefix).   The  result  is  always
       decimal.

       _8_._2_0_._2  _S_e_e _A_l_s_o
       count
















       
       Peter Miller                                         Page 74





       Cook                                              User Guide



       _8_._2_1  _f_i_l_t_e_r___o_u_t

       This  function  requires  one  or more arguments.  The first
       argument is a pattern, the second and  later  arguments  are
       strings  to  match  against  this  pattern.   The  resulting
       wordlist contains those arguments which did  not  match  the
       pattern given as the first argument.

       _8_._2_1_._1  _E_x_a_m_p_l_e


                   Expression                  Result
                   ------------------------------------
                   [filter_out %.c a.c a.o]    a.o
                   [filter_out %.cc a.c a.o]   a.c a.o

       _8_._2_1_._2  _M_a_t_c_h _M_o_d_e
       This  function  is affected by the selected match mode.  See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.

       _8_._2_1_._3  _S_e_e _A_l_s_o
       filter, stringset

       _8_._2_2  _f_i_l_t_e_r

       This function requires one or  more  arguments.   The  first
       argument  is  a  pattern, the second and later arguments are
       strings  to  match  against  this  pattern.   The  resulting
       wordlist  contains those arguments which matched the pattern
       given as the first argument.

       _8_._2_2_._1  _E_x_a_m_p_l_e


                      Expression              Result
                      -------------------------------
                      [filter %.c a.c a.o]    a.c
                      [filter %.cc a.c a.o]

       _8_._2_2_._2  _M_a_t_c_h _M_o_d_e
       This function is affected by the selected match  mode.   See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.

       _8_._2_2_._3  _S_e_e _A_l_s_o
       filter_out, stringset

       _8_._2_2_._4  _A_l_s_o _K_n_o_w_n _A_s
       match_mask







       
       Peter Miller                                         Page 75





       Cook                                              User Guide



       _8_._2_3  _f_i_n_d___c_o_m_m_a_n_d

       This  function  requires  at  least  one argument, being the
       names of commands to search for  in  $PATH.   The  resulting
       word  list  contains  either "" (false) or a fully qualified
       path name for each command given.

       _8_._2_3_._1  _E_x_a_m_p_l_e
       Some systems require _r_a_n_l_i_b(1) to be run  on  archives,  and
       some do not.  Here is a simple way to test:
            ranlib = [find_command ranlib];

            %.a: [%_obj]
                    set unlink
            {
                    ar qc [target] [%_obj];
                    if [ranlib] then
                            [ranlib] [target];
            }

       _8_._2_3_._2  _S_e_e _A_l_s_o
       cando, exists, uptodate

       _8_._2_4  _f_i_n_d_s_t_r_i_n_g

       The  findstring  function  is  used  to match a fixed string
       against a set of strings.  This function takes at least  one
       argument.   The  first  argument  is  the  fixed string, the
       second and subsequent  arguments  are  matched  against  the
       first.   The result contains one word for each of the second
       and subsequent arguments, each  will  either  be  the  empty
       string  (false)  or the string to be matched, if a match was
       found.

       _8_._2_4_._1  _E_x_a_m_p_l_e


                      Expression             Result
                      -------------------------------
                      [findstring a a b c]   a "" ""
                      [findstring a b c]     "" ""
       Quotes are for clarity,  to  emphasize  the  empty  strings.
       Because  the empty string is "false", this can be used in an
       _i_f statement:
            if [findstring fish [sources]] then
                    sources = [sources] hook.c;

       _8_._2_4_._2  _S_e_e _A_l_s_o
       filter-out, match, match_mask, patsubst, stringset, subst






       
       Peter Miller                                         Page 76





       Cook                                              User Guide



       _8_._2_5  _f_i_r_s_t_w_o_r_d

       This function requires zero or more arguments.  The wordlist
       returned  is  empty if there were no arguments, or the first
       argument if there were arguments.

       _8_._2_5_._1  _E_x_a_m_p_l_e
       You can iterate  along  a  list  using  the  _l_o_o_p  statement
       combined with the _f_i_r_s_t_w_o_r_d and _t_a_i_l functions:
            dirs = a b c d;
            src = ;

            tmp = [dirs];
            loop
            {
                    tmp_dir = [firstword [tmp]];
                    if [not [tmp_dir]] then
                            loopstop;
                    tmp = [tail [tmp]];
                    src = [src] [glob [tmp_dir]"/*.c"];
            }
       More  efficient ways exist to do this, this an example only.

       _8_._2_5_._2  _S_e_e _A_l_s_o
       count, glob, fromto, prepost, tail, word

       _8_._2_5_._3  _A_l_s_o _K_n_o_w_n _A_s
       head

       _8_._2_6  _f_r_o_m_t_o

       This function requires at least two arguments.  Fromto gives
       the  user access to the pattern transformations available to
       ccooookk.  The first argument is the  "from"  form,  the  second
       argument  is  the "to" form.  All other arguments are mapped
       from one to the other.

       _8_._2_6_._1  _E_x_a_m_p_l_e
       Given a list of C source files, generate a  list  of  object
       files as follows:
            obj = [fromto %.c %.o [src]];

       _8_._2_6_._2  _S_e_e _A_l_s_o
       filter, filter_out, subst

       See  the pattern matching chapter for more information about
       patterns.

       _8_._2_6_._3  _M_a_t_c_h _M_o_d_e
       This function is affected by the selected match  mode.   See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.




       
       Peter Miller                                         Page 77





       Cook                                              User Guide



       _8_._2_6_._4  _A_l_s_o _K_n_o_w_n _A_s
       patsubst

       _8_._2_7  _g_e_t_e_n_v

       Each  argument  is  treated  as  the  name of an environment
       variable.   The  result  is  the  value  of  each   argument
       variable,  or  ""  if  it  does  not  exist (consistent with
       command shell behaviour).

       _8_._2_7_._1  _E_x_a_m_p_l_e
       To read the value of the TERM environment variable:
            term = [getenv TERM];

       Values of variables  are  not  automagically  set  from  the
       environment, you must set each one explicitly:
            cc = [getenv CC];
            if [not [cc]] then
                    cc = gcc;

       _8_._2_7_._2  _S_e_e _A_l_s_o
       find_command, home

       _8_._2_8  _g_l_o_b

       Each  argument  is treated as a _s_h(1) file name pattern, and
       expanded accordingly.  The resulting list  of  filenames  is
       sorted lexicographically.

       You  may  need  to  quote  the  pattern,  to  protect square
       brackets from the meaning cook attaches to them.

       NNoottee:: The character sequence /* is a comment introducer, and
       is a frequent source of problems when combined with the _g_l_o_b
       function.  Remember to quote _g_l_o_b arguments which need  this
       character  sequence.  See the [head] function, below, for an
       example of this.

       _8_._2_8_._1  _E_x_a_m_p_l_e
       To find the sources in the current directory:
            src = [glob *.c];
            obj = [fromto %.c %.o [src]];

       _8_._2_8_._2  _S_e_e _A_l_s_o
       filter, filter_out, shell

       _8_._2_8_._3  _A_l_s_o _K_n_o_w_n _A_s
       wildcard







       
       Peter Miller                                         Page 78





       Cook                                              User Guide



       _8_._2_9  _h_e_a_d

       This function requires zero or more arguments.  The wordlist
       returned  is  empty if there were no arguments, or the first
       argument if there were arguments.

       _8_._2_9_._1  _E_x_a_m_p_l_e
       You can iterate  along  a  list  using  the  _l_o_o_p  statement
       combined with the _h_e_a_d and _t_a_i_l functions:
            dirs = a b c d;
            src = ;

            tmp = [dirs];
            loop
            {
                    tmp_dir = [head [tmp]];
                    if [not [tmp_dir]] then
                            loopstop;
                    tmp = [tail [tmp]];
                    src = [src] [glob [tmp_dir]"/*.c"];
            }
       More  efficient ways exist to do this, this an example only.

       _8_._2_9_._2  _S_e_e _A_l_s_o
       count, glob, fromto, prepost, tail, word

       _8_._2_9_._3  _A_l_s_o _K_n_o_w_n _A_s
       firstword

       _8_._3_0  _h_o_m_e

       The _h_o_m_e function is used to find the home directory of  the
       named  users.  You may name more than one user.  If no users
       are named, it returns the  home  directory  of  the  current
       user.

       _8_._3_1  _i_f

       This  function requires one or more arguments, the arguments
       before the "then" word are used  as  a  condition.   If  the
       condition  is true the words between the "then" word and the
       "else" word are the result, otherwise the  words  after  the
       "else"  word  are the value.  The "else" clause is optional.
       There is no way to escape the "then" and "else" words.

       _8_._3_1_._1  _E_x_a_m_p_l_e
       Here is an example of the ``if'' function.  Please note that
       ``if'',  ``then''  and  ``else''  are reserved words, so you
       need to quote them before they will  be  recognised  on  the
       function context.
            %: %_obj
                    set ["if" [defined all_shallow] "then" shallow]
            {
                    [cc] -o [target] [%_obj];

       
       Peter Miller                                         Page 79





       Cook                                              User Guide



            }

       _8_._3_1_._2  _C_a_v_e_a_t
       It  is  often  clearer  to  use  the  _i_f _s_t_a_t_e_m_e_n_t than this
       function.

       The recipe flags are evaluated  at  the  same  time  as  the
       recipe  targets.   None  of the [target], [targets], [need],
       [younger] variables or pattern matches (%, %1, _e_t_c) are  set
       at this time.

       _8_._3_2  _i_n

       This  function requires one or more arguments.  The wordlist
       returned is a single word: the index of the matching word (1
       based)  if  the  first argument is equal to any of the later
       ones; or "" (false) if not.

       This function can also be used for  equality  testing,  just
       use a single element in the set.

       Because  it  returns the index, the return valus can be used
       with the _[_w_o_r_d_] or _[_w_o_r_d_s_] functions.

       _8_._3_2_._1  _E_x_a_m_p_l_e
       Frequently seen in conditional parts of recipes:
            %: [%_obj]
            {
                    [cc] -o [target] [%_obj];
                    if [in [target] [private]] then
                            chmod og-rwx [target];
            }

       _8_._3_2_._2  _S_e_e _A_l_s_o
       stringset, word, words

       _8_._3_3  _i_n_t_e_r_i_o_r___f_i_l_e_s

       This function requires zero arguments.  The  result  is  the
       list  of  files  which are interior to the dependency graph.
       (Files which are constructed by a recipe.)  This function is
       only meaningful within a recipe body.

       _8_._3_3_._1  _S_e_e _A_l_s_o
       leaf_files     function,    graph_interior_file    variable,
       graph_interior_pattern variable









       
       Peter Miller                                         Page 80





       Cook                                              User Guide



       _8_._3_4  _j_o_i_n

       The _j_o_i_n function is  used  to  join  two  sets  of  strings
       together,  element  by  element.   The  argument  list  must
       contain an even number of arguments,  with  the  first  half
       joined  pair-wise with the last half.  There is no marker of
       any kind between the lists, so the user needs to ensure they
       are both the same length.

       _8_._3_4_._1  _E_x_a_m_p_l_e


                         Expression       Result
                         ------------------------
                         [join a b c d]   ac bd
                         [join a b]       ab

       _8_._3_4_._2  _S_e_e _A_l_s_o
       basename, catenate, suffix

       _8_._3_5  _l_e_a_f___f_i_l_e_s

       This  function  requires  zero arguments.  The result is the
       list of files which are  leaves  of  the  dependency  graph.
       (Files  which  are  not  constructed  by  a  recipe.)   This
       function is only meaningful within a recipe body.

       _8_._3_5_._1  _S_e_e _A_l_s_o
       interior_files    function,    graph_leaf_file     variable,
       graph_leaf_pattern variable

       _8_._3_6  _m_a_t_c_h_e_s

       This  function  requires  one  or more arguments.  The first
       argument is a pattern, the second and  later  arguments  are
       strings   to  match  against  the  pattern.   The  resulting
       wordlist contains ""  (false)  if  did  not  match  and  the
       1-based list index (true) if it did.

       The  returned list index may be used in combination with the
       [words] function.

       _8_._3_6_._1  _E_x_a_m_p_l_e
       This function may be used to test for strings which  have  a
       particular form:
            if [matches %1C%2 [version]] then
                    cc_flags = [cc_flags] -DDEBUG
       If  the version contains a Capital-C character, then turn on
       debugging.

       _8_._3_6_._2  _M_a_t_c_h _M_o_d_e
       This function is affected by the selected match  mode.   See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.


       
       Peter Miller                                         Page 81





       Cook                                              User Guide



       _8_._3_6_._3  _S_e_e _A_l_s_o
       filter, filter-out, words

       _8_._3_7  _m_a_t_c_h___m_a_s_k

       This  function  requires  one  or more arguments.  The first
       argument is a pattern, the second and  later  arguments  are
       strings  to  match  against  this  pattern.   The  resulting
       wordlist contains those arguments which matched the  pattern
       given as the first argument.

       _8_._3_7_._1  _E_x_a_m_p_l_e


                    Expression                  Result
                    -----------------------------------
                    [match_mask %.c a.c a.o]    a.c
                    [match_mask %.cc a.c a.o]

       _8_._3_7_._2  _M_a_t_c_h _M_o_d_e
       This  function  is affected by the selected match mode.  See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.

       _8_._3_7_._3  _S_e_e _A_l_s_o
       filter-out, findstring, stringset

       _8_._3_7_._4  _A_l_s_o _K_n_o_w_n _A_s
       filter

       _8_._3_8  _m_t_i_m_e

       This function requires one argument, the name of a  file  to
       fetch  the last-modified time of.  The resulting wordlist is
       ""  (false)  is  the  file  does  not  exist,  or  a  string
       containing  a (sortable) representation of the date and time
       the files were last modified.

       _8_._3_8_._1  _S_e_e _A_l_s_o
       exists, mtime-seconds, sort_newest

       _8_._3_9  _m_t_i_m_e_-_s_e_c_o_n_d_s

       This function requires one argument, the name of a  file  to
       fetch  the last-modified time of.  The resulting wordlist is
       ""  (false)  is  the  file  does  not  exist,  or  a  string
       containing  number of seconds since the epoch that the files
       were last modified.  This is more useful  than  [mtime]  for
       doing arithmetic on.

       _8_._3_9_._1  _S_e_e _A_l_s_o
       exists, expr, mtime, sort_newest




       
       Peter Miller                                         Page 82





       Cook                                              User Guide



       _8_._4_0  _n_o_t_d_i_r

       This  function  requires one or more arguments, the names of
       files which will have their entry name parts extracted.

       _8_._4_0_._1  _E_x_a_m_p_l_e


                       Expression           Result
                       ----------------------------
                       [notdir foo.c]       foo.c
                       [notdir foo/bar.c]   bar.c
                       [notdir baz]         baz

       _8_._4_0_._2  _S_e_e _A_l_s_o
       basename, dirname, relative_dirname, suffix

       _8_._4_0_._3  _A_l_s_o _K_n_o_w_n _A_s
       entryname

       _8_._4_1  _n_o_t

       This function requires zero or more arguments, the value  to
       be  logically  negated.  It returns "1" (true) if all of the
       arguments are "" (false), or there  are  no  arguments;  and
       returns  ""  (false)  otherwise.  This is symmetric with the
       definition of true and false for iiff.

       _8_._4_1_._1  _E_x_a_m_p_l_e
       This is often seen in recipes:
            %1/%0%2.o: %1/%0%2.c
                    single-thread %2.o
            {
                    if [not [exists [dirname [target]]]] then
                            mkdir -p [dirname [target]]
                                    set clearstat;
                    [cc] [cc_flags] -I%1 %1/%0%2.c;
                    mv %2.o [target];
            }
       Note that "%0" matches zero or more whole filename portions,
       including  the  trailing  slash.  See the chapter on pattern
       matching for more information.

       This is an example only.  The ``mkdir'' recipe flag  creates
       the directory more efficiently.

       _8_._4_1_._2  _S_e_e _A_l_s_o
       and, or







       
       Peter Miller                                         Page 83





       Cook                                              User Guide



       _8_._4_2  _o_p_e_r_a_t_i_n_g___s_y_s_t_e_m

       This   function   requires  zero  or  more  arguments.   The
       resulting wordlist contains the values of various attributes
       of  the  operating system, as named in the arguments.  If no
       attributes are named, "system" is assumed.  Below is a  list
       of attributes:

       node      The name of the computer ccooookk is presently running
                 on.

       system    The name of the operating system ccooookk is presently
                 being run under.  For example: if you were running
                 on SunOS 4.1.3, this would return "SunOS".

       release   The specific release of operating  system,  within
                 name,  ccooookk  is  presently  being  run under.  For
                 example: if you were running on SunOS 4.1.3,  this
                 would return "4.1.3".

       version   Version  information.  For SunOS 4.1.3, this would
                 return the kernel build number, for other  systems
                 it is often the kernel patch release number.

       machine   The name of the hardware ccooookk is presently running
                 on.  For example: If you  were  running  on  SunOS
                 4.1.3 this would return "sun4" or similar.

       This function may be abbreviated to "os".

       _8_._4_2_._1  _E_x_a_m_p_l_e
       This  function is usually used to determine the architecture
       (either system or machine):
            arch=[os system]-[os release]-[os machine];
            if [matches SunOS-4.1%1-sun4%2 [arch]] then
                    arch = sun4;
            else if [matches SunOS-5.%1-sun4%2 [arch]] then
                    arch = sun5;
            else if [matches SunOS-5.%1-i86pc [arch]] then
                    arch = sun5pc;
            else if [matches ConvexOS-%1-%2 [arch]] then
                    arch = convex;
            else
                    arch = unknown;

       _8_._4_2_._2  _C_a_v_e_a_t
       This function is implemented using the _u_n_a_m_e(2) system call.
       Some  systems do not implement this correctly, and therefore
       this function is less useful than it should  be,  and  needs
       the pattern match appropach used above.

       _8_._4_2_._3  _S_e_e _A_l_s_o
       collect


       
       Peter Miller                                         Page 84





       Cook                                              User Guide



       _8_._4_2_._4  _A_l_s_o _K_n_o_w_n _A_s
       os

       _8_._4_3  _o_p_t_i_o_n_s

       This  functions  takes  no  arguments.   The  results  is  a
       complete  list  of  _c_o_o_k  options,  exactly  describing  the
       current   options   settings.   This  intended  for  use  in
       constructing recursive _c_o_o_k invocations.

       The option  setting  generated  are  a  combination  of  the
       command  line  options  used to invoke _c_o_o_k_, the contents of
       the COOK environment variable, the results  of  the  ``set''
       command and the various ``set'' clauses.

       _8_._4_3_._1  _E_x_a_m_p_l_e
       The top level cookbook for a recursive project structure can
       be as follows:
            %:
            {
                    dirlist = [dirname [glob '*/Howto.cook' ]];
                    loop
                    {
                            dir = [head [dirlist]];
                            if [not [dir]] then
                                    loopstop;
                            dirlist = [tail [dirlist]];

                            cd [dir]\; cook [options] %;
                    }
            }

            /*
             * This recipe sets the default.
             * It doesn't actually do anything.
             */
            all:;
       Please note the % hiding on  the  end  of  the  nested  _c_o_o_k
       command,  this  is  how  the  target  is communicated to the
       nested ccooookk invocation.

       _8_._4_3_._2  _C_a_v_e_a_t
       Recursive Cook is not recommended, because it  segments  the
       dependency  graph  and  forces  Cook  to  walk  the graph in
       (potentially) the wrong order.  This introduces a number  of
       significant   problems.   A  single  top-level  cookbook  is
       recommended.

       _8_._4_3_._3  _S_e_e _A_l_s_o
       The supplied ``recursive'' cookbook does exactly  this.   In
       order  to  use it, you need a _H_o_w_t_o_._c_o_o_k file containing the
       single line
            #include "recursive"


       
       Peter Miller                                         Page 85





       Cook                                              User Guide



       _8_._4_4  _o_r

       This function requires at least two arguments, upon which it
       forms  a  logical  disjunction.   The  value returned is "1"
       (true) if any one  of  the  arguments  is  not  ""  (false),
       otherwise "" (false) is returned.

       _8_._4_4_._1  _S_e_e _A_l_s_o
       and, not

       _8_._4_5  _p_a_t_h_n_a_m_e

       The  function  requires  one  or more arguments, being files
       names to be replaced with their full path names.

       _8_._4_5_._1  _E_x_a_m_p_l_e
       Relative names are made absolute, and redundant slashes  and
       dots are removed:
            pwd = [pathname .];

       _8_._4_5_._2  _S_e_e _A_l_s_o
       basename, dirname, entryname

       _8_._4_6  _p_a_t_s_u_b_s_t

       This  function  requires  at  least two arguments.  Patsubst
       gives  the  user  access  to  the  pattern   transformations
       available  to  ccooookk.  The first argument is the "from" form,
       the second argument is the "to" form.  All  other  arguments
       are mapped from one to the other.

       _8_._4_6_._1  _E_x_a_m_p_l_e
       Given  a  list  of C source files, generate a list of object
       files as follows:
            obj = [patsubst %.c %.o [src]];

       _8_._4_6_._2  _M_a_t_c_h _M_o_d_e
       This function is affected by the selected match  mode.   See
       the _F_i_l_e _N_a_m_e _P_a_t_t_e_r_n_s chapter for details.

       _8_._4_6_._3  _S_e_e _A_l_s_o
       filter, filter_out, subst

       _8_._4_6_._4  _A_l_s_o _K_n_o_w_n _A_s
       fromto










       
       Peter Miller                                         Page 86





       Cook                                              User Guide



       _8_._4_7  _p_r_e_p_o_s_t

       This  function  must have at least two arguments.  The first
       argument is a prefix and the second argument  is  a  suffix.
       The  resulting  word  list  is the third and later arguments
       each given the prefix and suffix as defined by the first and
       second arguments.

       _8_._4_7_._1  _E_x_a_m_p_l_e


                Expression               Result
                -------------------------------------------
                [prepost sun4/ .o a b]   sun4/a.o sun4/b.o
                [prepost -I "" . bl]     -I. -Ibl

       _8_._4_7_._2  _S_e_e _A_l_s_o
       addprefix, addsuffix, patsubst, subst

       _8_._4_8  _p_r_i_n_t

       The  arguments  are  printed as an informative message.  The
       usual output wrapping is performed.   The  function  returns
       the empty list as a result.

       This function is frequently use to debug cookbooks.

       _8_._4_9  _q_u_o_t_e

       Each  argument  is  quoted  by  double  quotes,  with shell9
       special characters escaped as necessary.

       _8_._4_9_._1  _S_e_e _A_l_s_o
       collect, execute


















       ____________________

       9. See _s_h (1) and _c_s_h(1) for more information.

       Peter Miller                                         Page 87





       Cook                                              User Guide



       _8_._5_0  _r_e_a_d___l_i_n_e_s

       The argument is interpreted as the name of a text file to be
       read.  The result is one word for each line of the file.

       _8_._5_0_._1  _E_x_a_m_p_l_e
       Read a the _e_x_a_m_p_l_e file and assign it to a variable:
            example = [read_lines example];

       _8_._5_0_._2  _S_e_e _A_l_s_o
       collect, collect_lines, read, write

       _8_._5_1  _r_e_a_d_l_i_n_k

       The  arguments  are  assumed to be symbolic links, and their
       values are read.  It is a fatal error if the files named are
       not symbolic links.

       _8_._5_1_._1  _S_e_e _A_l_s_o
       collect, exists-symlink

       _8_._5_2  _r_e_a_d

       The argument is interpreted as the name of a text file to be
       read.  The result is one word for each white-space separated
       word of the file.

       _8_._5_2_._1  _E_x_a_m_p_l_e
       Read a the _e_x_a_m_p_l_e file and assign it to a variable:
            example = [read example];

       _8_._5_2_._2  _S_e_e _A_l_s_o
       collect, collect_lines, read_lines, write

       _8_._5_3  _r_e_l_a_t_i_v_e___d_i_r_n_a_m_e

       This  function  requires one or more arguments, the names of
       files which will have their directory parts extracted.

       _8_._5_3_._1  _E_x_a_m_p_l_e


                    Expression                 Result
                    ----------------------------------
                    [relative_dirname a]       .
                    [relative_dirname a/b]     a
                    [relative_dirname a/b/c]   a/b
       See _d_i_r_n_a_m_e if you want to climb  the  directory  tree  with
       repeated  applications,  _r_e_l_a_t_i_v_e___d_i_r_n_a_m_e  will  continue to
       return ``.'' once the current directory is reached.

       _8_._5_3_._2  _S_e_e _A_l_s_o
       basename, dirname, entryname, notdir, pathname, suffix


       
       Peter Miller                                         Page 88





       Cook                                              User Guide



       _8_._5_3_._3  _A_l_s_o _K_n_o_w_n _A_s
       reldir

       _8_._5_4  _r_e_s_o_l_v_e

       This builtin function is used to  resolve  file  names  when
       using  the  _s_e_a_r_c_h___l_i_s_t  variable  to  locate  files.   This
       builtin function produces resolved  file  names  as  output.
       This  is  useful  when  taking partial copies of a source to
       perform controlled updates.   The  targets  of  recipes  are
       always cooked into the current directory.

       _8_._5_4_._1  _E_x_a_m_p_l_e
       This   function   is   used  in  cookbooks  which   use  the
       _s_e_a_r_c_h___l_i_s_t functionality:
            search_list = . baseline;

            %.o: %.c
            {
                    [cc] [cc_flags] [addprefix -I [search_list]] [resolve %.c];
            }

       The cookbooks distributed with Cook contain full support for
       the  search_list  functionality.   They are a good source of
       examples of how  to  write  recipes  which  take  this  into
       account.

       _8_._5_5  _s_h_e_l_l

       The  arguments  are interpreted as a command to be passed to
       the operating system.  The  result  is  one  word  for  each
       white-space separated word of the output of the command.

       The  command will not be echoed unless the -No_Silent option
       is specified on the command line.

       _8_._5_5_._1  _E_x_a_m_p_l_e
       Read the date and time and assign it to a variable:
            now = [shell date];
       Do not use the shell function to expand a filename wildcard,
       used the [wildcard] function instead.

       _8_._5_5_._2  _S_e_e _A_l_s_o
       collect_lines, execute, wildcard

       _8_._5_5_._3  _A_l_s_o _K_n_o_w_n _A_s
       collect








       
       Peter Miller                                         Page 89





       Cook                                              User Guide



       _8_._5_6  _s_o_r_t___n_e_w_e_s_t

       The   arguments  are  sorted  by  file  last-modified  time,
       youngest to oldest.  File names are resolved first (see  the
       resolve  function,  below).   Absent files will be sorted to
       the start of the list.

       _8_._5_6_._1  _E_x_a_m_p_l_e
       This function is often  used  to  "shorten  the  wait"  when
       building  large  project,  so  that the file you edited most
       recently is recompiled almost immediately:
            src = [glob *.c];
            obj = [sort_newest [fromto %.c %.o [src]]];

       This trick does not always work as expected,  and  can  take
       significant time for little result.

       _8_._5_6_._2  _S_e_e _A_l_s_o
       fromto, glob, sort

       _8_._5_7  _s_o_r_t

       The arguments are sorted lexicographically.

       NNoottee::   Duplicates  are  _n_o_t  removed.   Use  the  _s_t_r_i_n_g_s_e_t
       function if you want to do this.

       _8_._5_7_._1  _S_e_e _A_l_s_o
       sort_newest, stringset

       _8_._5_8  _s_p_l_i_t

       The _s_p_l_i_t function is used to split  strings  into  multiple
       strings,  given  the  separator.   This function requires at
       least one argument.  The first  argument  is  the  separator
       character,  the  second  and  subsequent arguments are to be
       separated.  The result is the separated strings, each  as  a
       separate word.

       _8_._5_8_._1  _E_x_a_m_p_l_e


                 Expression                  Result
                 ----------------------------------------
                 [split ":" "foo:bar:baz"]   foo bar baz
                 [split " " "New York"]      New York
       Each of the words in the result is a separate string.

       This can be useful in splitting an environment variable into
       separate words.  For example:
            path = [split ":" [getenv PATH]];




       
       Peter Miller                                         Page 90





       Cook                                              User Guide



       _8_._5_8_._2  _S_e_e _A_l_s_o
       unsplit, join, catenate, strip

       _8_._5_9  _s_t_r_i_n_g_s_e_t

       Logical operations are performed on sets of strings.   These
       include  conjunction  (++)  or  implicit, disjunction (**) and
       difference (--).

       _8_._5_9_._1  _E_x_a_m_p_l_e


                     Expression                Result
                     ---------------------------------
                     [stringset a b a]         a b
                     [stringset a b c * a]     a
                     [stringset a b c - a]     b c
                     [stringset a b - c + d]   a b d

       The can be very  useful  in  constructing  lists  of  source
       files:
            src = [stringset [glob "*.[cyl]" ] - y.tab.c lex.yy.c];

       _8_._5_9_._2  _S_e_e _A_l_s_o
       filter, filter_out, glob, in, patsubst, subst

       _8_._6_0  _s_t_r_i_p_d_o_t

       The  _s_t_r_i_p_d_o_t  function  is  used  to  remove leading ``.\''
       directories from each of the path name arguments.

       _8_._6_0_._1  _E_x_a_m_p_l_e


                       Expression           Result
                       ----------------------------
                       [stripdot ./foo.c]    foo.c
                       [stripdot bar.o]     bar.o
                       [stripdot /fubar]    /fubar

       _8_._6_0_._2  _S_e_e _A_l_s_o
       set stripdot













       
       Peter Miller                                         Page 91





       Cook                                              User Guide



       _8_._6_1  _s_t_r_i_p

       The _s_t_r_i_p function is used to remove  leading  and  trailing
       white  space  from words.  Internal sequences of white space
       are replaced by a single space.

       _8_._6_1_._1  _E_x_a_m_p_l_e


                 Expression                  Result
                 -----------------------------------------
                 [strip " " "foo " " bar"]   "" foo bar
                 [strip " really   big  "]   "really big"
       Quotes are used here for clarity, and are not present in the
       internal representation of strings.

       _8_._6_1_._2  _S_e_e _A_l_s_o
       split

       _8_._6_2  _s_u_b_s_t_r

       The  _s_u_b_s_t_r function is used to perform substring extracton.
       The first argument is the starting position in  the  string,
       starting  from  one.   The  second argument is the number of
       characters to extract.  Thirst and subsequent arguments will
       be processed to extract sub-strings.

       _8_._6_2_._1  _E_x_a_m_p_l_e


                      Expression             Result
                      ------------------------------
                      [substr 1 1 Peter]     P
                      [substr 3 99 Miller]   ller

       _8_._6_2_._2  _S_e_e _A_l_s_o
       subst, patsubst


















       
       Peter Miller                                         Page 92





       Cook                                              User Guide



       _8_._6_3  _s_u_b_s_t

       The  _s_u_b_s_t  function is used to perform string substitutions
       on its arguments.   This  function  requires  at  least  two
       arguments.   The  first  argument  is the "from" string, the
       second argument is the "to"  string.   All  occurreneces  of
       "from"  are  replaced  with "to" in the third and subsequent
       arguments.

       _8_._6_3_._1  _E_x_a_m_p_l_e
       This is a litteral replacement, not a pattern replacement:

            Expression                            Result
            ---------------------------------------------------
            [subst buffalo cress water.buffalo]   water.cress
            [subst .c .o test.c]                  test.o
            [subst .c .o stat.cache.c]            stat.oache.o
       Note that last case: it is not selective.

       _8_._6_3_._2  _S_e_e _A_l_s_o
       filter, filter_out, patsubst

       _8_._6_4  _s_u_f_f_i_x

       The _s_u_f_f_i_x function treats each argument as a filename,  and
       extracts  the  suffix from each.  If the filename contains a
       period, the suffix is  everything  starting  with  the  last
       period.   Otherwise,  the  suffix  is  the  empty string (as
       opposed to nothing at all).

       _8_._6_4_._1  _E_x_a_m_p_l_e


                     Expression              Result
                     ---------------------------------
                     [suffix a.c foo b.y]    .c "" .y
                     [suffix stat.cache.c]   .c
                     [suffix .eric]          ""
       Quotes are used here for clarity, and are not present in the
       internal representation of strings.

       The  _s_u_f_f_i_x  functions in this way to allow sensible results
       when  using  the  _j_o_i_n  function   to   re-unite   filenames
       dismembered by the _b_a_s_e_n_a_m_e and _s_u_f_f_i_x functions.

       _8_._6_4_._2  _S_e_e _A_l_s_o
       basename, dirname, entryname, join, patsubst








       
       Peter Miller                                         Page 93





       Cook                                              User Guide



       _8_._6_5  _t_a_i_l

       This  function  requires  zero  or more arguments.  The word
       list returned will be  empty  if  there  is  less  than  two
       arguments, otherwise it will consist of the second and later
       arguments.

       _8_._6_5_._1  _S_e_e _A_l_s_o
       count, head, word

       _8_._6_6  _u_n_-_d_o_s_-_p_a_t_h

       This function requires one or more arguments, which will  be
       converted from a DOS path into a UNIX path.  This is of most
       use under Windows-NT, to convert DOS pathnames  into  Cook's
       internal  pathnames.   (The UNIX porting layer usually hides
       this from Cook.)

       _8_._6_6_._1  _E_x_a_m_p_l_e


              Expression                     Result
              ----------------------------------------------
              [un-dos-path a\b\c]            a/b/c
              [un-dos-path c:\temp]          //c/temp
              [un-dos-path \\server\stuff]   //server/stuff

       _8_._6_6_._2  _S_e_e _A_l_s_o
       dos-path

       _8_._6_7  _u_n_s_p_l_i_t

       The _u_n_s_p_l_i_t function is used to glue strings together, using
       the  specified  glue.   The first argument is the text to go
       between each of the second and subsequent arguments.

       _8_._6_7_._1  _E_x_a_m_p_l_e


              Expression                    Result
              ----------------------------------------------
              [unsplit ":" one two three]   "one:two:three"
              [unsplit " " four five six]   "four five six"
       The quotes are necessary to isolate characters such as colon
       and space which cook would normally treat differently.

       _8_._6_7_._2  _S_e_e _A_l_s_o
       catenate, prepost, split







       
       Peter Miller                                         Page 94





       Cook                                              User Guide



       _8_._6_8  _u_p_c_a_s_e

       This  function  requires  one or more arguments, words to be
       forced into upper case.

       _8_._6_8_._1  _E_x_a_m_p_l_e


                          Expression     Result
                          ----------------------
                          [upcase FOO]   FOO
                          [upcase Bar]   BAR
                          [upcase baz]   BAZ

       _8_._6_8_._2  _S_e_e _A_l_s_o
       downcase

       _8_._6_9  _u_p_t_o_d_a_t_e

       This function may be used to determine if files  are  up-to-
       date.   It  returns  a word list containing the names of the
       up-to-date files, or empty if none of them  are  up-to-date.
       They  are  _n_o_t  brought  up to date if they are not already.
       This function requires one or more arguments.

       _8_._6_9_._1  _C_a_v_e_a_t
       This will use as much of the cookbook as has been read in up
       to  the  point  where  this function is used.  This can mean
       that crucial recipes have yet to be parsed and instanciated.

       _8_._6_9_._2  _S_e_e _A_l_s_o
       cando, cook

       _8_._7_0  _w_i_l_d_c_a_r_d

       Each  argument  is treated as a _s_h(1) file name pattern, and
       expanded accordingly.  The resulting list  of  filenames  is
       sorted lexicographically.

       You  may  need  to  quote  the  pattern,  to  protect square
       brackets from the meaning cook attaches to them.

       NNoottee:: The character sequence /* is a comment introducer, and
       is  a  frequent  source  of  problems when combined with the
       _w_i_l_d_c_a_r_d function.  Remember  to  quote  _w_i_l_d_c_a_r_d  arguments
       which need this character sequence.

       _8_._7_0_._1  _E_x_a_m_p_l_e
       To find the sources in the current directory:
            src = [wildcard *.c];
            obj = [patsubst %.c %.o [src]];




       
       Peter Miller                                         Page 95





       Cook                                              User Guide



       _8_._7_0_._2  _S_e_e _A_l_s_o
       filter, filter_out, patsubst

       _8_._7_0_._3  _A_l_s_o _K_n_o_w_n _A_s
       glob

       _8_._7_0_._4  _W_o_r_d_l_i_s_t
       This  function may be used to extract a list of words from a
       larger list.  The first argument is the  starting  position,
       and  the  second argument is the ending poistion, inclusive.
       The third and  subsequent  arguments  are  the  list  to  be
       extracted from.  Positions are numbered starting from 1.  If
       the start is bigger than  the  end,  they  will  be  quietly
       swapped.   If  the start is bigger than the list, the result
       will be empty.

       _8_._7_0_._4_._1  _E_x_a_m_p_l_e


                   Expression                   Result
                   -------------------------------------
                   [wordlist 2 3 foo bar baz]   bar baz
                   [wordlist 1 1 foo bar baz]   foo
                   [wordlist 7 3 foo bar baz]   baz

       There are a number of functions which are similar

                  Expression               Similar to
                  ---------------------------------------
                  [wordlist 1 1 _l_i_s_t]      [head _l_i_s_t]
                  [wordlist 2 9999 _l_i_s_t]   [tail _l_i_s_t]
                  [wordlist _N _N _l_i_s_t]      [word _N _l_i_s_t]

       _8_._7_0_._4_._2  _S_e_e _A_l_s_o
       firstword head, tail, word, words

       _8_._7_1  _w_o_r_d

       The _w_o_r_d function is used to extract a specific word from  a
       list of words.  The function requires at least one argument.
       The first argument is the number of the word to extract from
       the  wordlist.   The  wordlist  is the second and subsequent
       arguments.  An empty list will be returned if you ask for an
       element off the end of the list.

       _8_._7_1_._1  _E_x_a_m_p_l_e


                     Expression               Result
                     --------------------------------
                     [word 1 one two three]   one
                     [word 2 one two three]   two
                     [word 3 one two three]   three


       
       Peter Miller                                         Page 96





       Cook                                              User Guide



                     [word 5 one two three]

       The last element of a list of words may be extracted as:
            last = [word [count [list]] [list]];

       _8_._7_1_._2  _S_e_e _A_l_s_o
       count, head

       _8_._7_2  _w_o_r_d_s

       This  function  requires zero or more arguments.  The result
       is a word list of one word containing the  (decimal)  length
       of the argument word list.

       _8_._7_2_._1  _E_x_a_m_p_l_e
       This  cookbook fragment echoes the number of files, and then
       the name of the last file:
            echo There are [words [files]] files.;
            echo The last file is [word [words [files]] [files]].;

       _8_._7_2_._2  _S_e_e _A_l_s_o
       head, tail, word

       _8_._7_2_._3  _A_l_s_o _K_n_o_w_n _A_s
       count

       _8_._7_3  _w_r_i_t_e

       This function requires one or  more  arguments.   The  first
       argument  is  the  name  of the file to write, the second an
       later arguments are lines to be written to the file.   (This
       is  specifically  a text file.)  The result is an empty word
       list.

       This function is very useful in writing  command  line  file
       for  Windows-NT,  due  to  its  absurdly  short command line
       interface.

       _8_._7_3_._1  _S_e_e _A_l_s_o
       read, read_lines















       
       Peter Miller                                         Page 97





       Cook                                              User Guide



       _9_.  _P_r_e_d_e_f_i_n_e_d _V_a_r_i_a_b_l_e_s

       A number of variables are defined by ccooookk at run-time.

       _9_._1  _a_r_g

       This is  the  arguments  list  for  user-defined  functions.
       Individual  arguments  are  split out into ``@1'' to ``@9''.
       These can also be used at automatic variables.  Caution: _a_r_g
       and   the   automatic  variables  are  _s_h_a_r_e_d  for  parallel
       execution, causing  weird  interactions  if  you  execute  a
       command within the function.

       _9_._2  _c_o_m_m_a_n_d_-_l_i_n_e_-_g_o_a_l_s

       The  value  of  this  variable is the goals specified on the
       command line, if any.   If  none  were  specified,  and  the
       default goal is in effect, the value will be empty.

       _9_._3  _____F_I_L_E____

       The  value  of this variable is the logical name of the file
       which contains it.  In the case  of  #include-cooked  files,
       the  physical  name  may  be  obtained  using  the [resolve]
       function.  The logical name  may  be  set  using  the  #line
       directive.

       _9_._4  _____F_U_N_C_T_I_O_N____

       The value of this variable is the name of the function which
       executes it.  It is not set for the global cookbook scope or
       the recipe body scope.

       _9_._5  _g_r_a_p_h___l_e_a_f___f_i_l_e

       File  names  which are listed in this variable could be leaf
       files of the dependency graph.   (See  also  the  _l_e_a_f___f_i_l_e_s
       function, for Cook's idea of the leaf files.)

       _9_._6  _g_r_a_p_h___e_x_t_e_r_i_o_r___f_i_l_e

       File  names  which  are  listed  in  this variable cannot be
       present in any way in the dependency graph.

       _9_._7  _g_r_a_p_h___i_n_t_e_r_i_o_r___f_i_l_e

       File names which  are  listed  in  this  variable  could  be
       interior  files  of  the  dependency  graph.   (See also the
       _i_n_t_e_r_i_o_r___f_i_l_e_s function, for Cook's  idea  of  the  interior
       files.)

       _9_._8  _g_r_a_p_h___l_e_a_f___p_a_t_t_e_r_n

       File  names  which match the patterns in this variable could

       
       Peter Miller                                         Page 98





       Cook                                              User Guide



       be leaf files  of  the  dependency  graph.   (See  also  the
       _l_e_a_f___f_i_l_e_s function, for Cook's idea of the leaf files.)

       _9_._9  _g_r_a_p_h___e_x_t_e_r_i_o_r___p_a_t_t_e_r_n

       File  names which match the patterns in this variable cannot
       be present in any way in the dependency graph.

       _9_._1_0  _g_r_a_p_h___i_n_t_e_r_i_o_r___p_a_t_t_e_r_n

       File names which match the patterns in this  variable  could
       be  interior  files  of the dependency graph.  (See also the
       _i_n_t_e_r_i_o_r___f_i_l_e_s function, for Cook's  idea  of  the  interior
       files.)

       _9_._1_1  _____L_I_N_E____

       The  value of this variable is the line number within of the
       file which contains it.  The line number may  be  set  using
       the #line directive.

       _9_._1_2  _n_e_e_d

       The ingredients of the recipe currently being cooked.

       _9_._1_3  _p_a_r_a_l_l_e_l___h_o_s_t_s

       This  variable may be set to indicate a list of hosts to use
       to distribute the execution of recipe bodies.

       _9_._1_4  _p_a_r_a_l_l_e_l___j_o_b_s

       This variable may be set to the number of parallel execution
       threads  to  perform  simultaneously.   Defaults to 1 if not
       set.

       _9_._1_5  _p_a_r_a_l_l_e_l___r_s_h

       This variable may be set to  the  command  used  to  execute
       commands  on  remote  machines.  Assumes to take argument in
       the same form  as  the  BSD  _r_s_h(1)  command.   Defaults  to
       ``_r_s_h'' if not set.

       _9_._1_6  _s_e_a_r_c_h___l_i_s_t

       This  variable  may  be  set  to a list of directories to be
       searched  for  targets  and  ingredients.   This   list   is
       initially  the  current  directory (.)  and will always have
       the current directory prepended if it is not present.   This
       is  useful when taking partial copies of a source to perform
       controlled updates.  Use the _r_e_s_o_l_v_e  built-in  function  to
       determine  what  file name cook actually found.  The targets
       of recipes are always cooked into the current directory.


       
       Peter Miller                                         Page 99





       Cook                                              User Guide



       The cookbooks distributed with Cook contain full support for
       the  search_list  functionality.   They are a good source of
       examples of how  to  write  recipes  which  take  this  into
       account.

       _9_._1_7  _s_e_l_f

       The  name  ccooookk  was invoked as, usually "cook".  Be careful
       what you call cook, because anything with the string  "cook"
       in  it  will be changed, including (but not limited to) file
       suffixes and environment variable names.

       _9_._1_8  _t_a_r_g_e_t

       The target of the recipe  currently  being  cooked,  or  the
       first target if there is more than one.

       _9_._1_9  _t_a_r_g_e_t_s

       The  targets  of  the  recipe  currently being cooked.  This
       includes all targets of the recipe,  should  there  be  more
       than one.

       _9_._2_0  _t_h_r_e_a_d_-_i_d

       This  variable has a unique value for each execution thread,
       for the lifetime of that thread.  This value may be used  to
       construct   thread-unique   variable   names,  thread-unique
       temporary file names, or anything  else  that  needs  to  be
       unique  to  each  execution  thread.  The thread IDs are re-
       used, and so several threads in sequence may have  the  same
       thread  ID; it is only guaranteed that no other simultaneous
       thread will have the same thread  ID.   By  re-using  thread
       IDs,  generated  variable  names  are also re-used, avoiding
       memory bloat.

       _9_._2_1  _y_o_u_n_g_e_r

       The subset of the ingredients of the recipe currently  being
       cooked which are younger than the target.

       _9_._2_2  _v_e_r_s_i_o_n

       The version of ccooookk currently executing.











       
       Peter Miller                                        Page 100





       Cook                                              User Guide



       _1_0_.  _F_u_n_c_t_i_o_n_s _L_i_b_r_a_r_y

       There is a file of functions available to you by using a
            #include "functions"
       line  in your cookbook.  The file defines a number of useful
       functions.

       The functions in the file also serve as examples of how  you
       can write your own functions.

       _1_0_._1  _c_a_p_i_t_a_l_i_z_e

       The _c_a_p_i_t_a_l_i_z_e function maps all of its arguments into lower
       case, and then the first letter of each argument  is  mapped
       to upper case.  Zero, one or more arguments may be given.

       _1_0_._2  _d_e_f_i_n_e_d_-_o_r_-_n_u_l_l

       The  _d_e_f_i_n_e_d_-_o_r_-_n_u_l_l  function may be used to determine if a
       variable has been set (on the command line, for example) and
       return its value if so, otherwise return the empty list.

       This  function  should only be given one argument - the name
       of the variable to look for.  Additional arguments  will  be
       ignored.   Too  few arguments will produce a complaint about
       the "" variable being undefined.

       _1_0_._3  _d_e_f_i_n_e_d_-_o_r_-_d_e_f_a_u_l_t

       The _d_e_f_i_n_e_d_-_o_r_-_d_e_f_a_u_l_t function may be used to determine  if
       a  variable  has been set (on the command line, for example)
       and return its value  if  so,  otherwise  return  the  given
       default value.

       The  first argument is the name of the variable to look for.

       The second and later arguments (if present) are the  default
       value  to  be  used  if  the  named variable is not defined.
       Optional.

       _1_0_._4  _r_e_p_e_a_t

       The _r_e_p_e_a_t function  is  used  to  repeatedly  call  another
       function, once for each of the specified arguments.  The can
       be  useful  when  dealing  with  functions  which   do   not
       automaticly accept argument lists in the form you require.

       There  are  many instances where the repeat function call be
       used to elegantly avoid used to the ``loop  {  loopstop  }''
       construct.

       The  first  argument  is  the  name of the function you want
       called.  This function must accept a single argument.


       
       Peter Miller                                        Page 101





       Cook                                              User Guide



       The second and subsequent arguments are argument  values  to
       be passed to the named function, one at a time.

       The   results   of  the  invocations  of  the  function  are
       accumulated in the order in which they were calculated.  The
       accumulated results are returned.

       _1_0_._5  _v_a_r_i_a_b_l_e___b_y___p_a_t_h

       The  _v_a_r_i_a_b_l_e___b_y___p_a_t_h  function is used to extract the union
       of option settings relevant to a particular  compilation  or
       link.  By using a variable prefix, this function may be used
       to obtain the setting of  a  wide  variety  of  options  and
       commands.

       Global  variables  are searched in a no particular order for
       the necessary information.  All are searched, all found  are
       used.

       For  example,  the  function call [variable_by_path cc_flags
       foo/bar/baz.c] will hunt for variables  with  the  following
       names:   cc_flags_foo/bar/baz.c   and  cc_flags_foo/bar  and
       cc_flags_foo and cc_flags.  It is  expected  that  the  vast
       majority of these variables will not be set.  Duplicates are
       removed.






























       
       Peter Miller                                        Page 102





       Cook                                              User Guide



       _1_1_.  _A_c_t_i_o_n_s _w_h_e_n _C_o_o_k_i_n_g

       This section describes what ccooookk does when  you  ask  it  to
       cook something.

       CCooookk performs the following actions in the order stated.

       _1_1_._1  _S_c_a_n _t_h_e _C_O_O_K _E_n_v_i_r_o_n_m_e_n_t _V_a_r_i_a_b_l_e

       The  CCOOOOKK  environment  variable  is  looked  for.  If it is
       found, it is treated as if it consisted of ccooookk command line
       arguments.   Only  the  --HHeellpp option is illegal.  This could
       result is very strange behavior if used incorrectly.

       This feature is supplied to  override  ccooookk's  default  with
       your own preferences.

       _1_1_._2  _S_c_a_n _t_h_e _C_o_m_m_a_n_d _L_i_n_e

       The command line is scanned as defined in chapter 3.

       _1_1_._3  _L_o_c_a_t_e _t_h_e _C_o_o_k_b_o_o_k

       The  current  directory  is scanned for the cookbook.  Names
       which a cookbook may have include

                 howto.cook    Howto.cook    .howto.cook
                 how.to.cook   How.to.cook   .how.to.cook
                  cookfile      Cookfile       .cookrc
                  cook.file     Cook.file      .cook.rc
       The first so named file found in the current directory  will
       be  used.   The  order  of  search  is not defined.  You are
       strongly advised to have just _o_n_e of these name forms in any
       directory.  The name _H_o_w_t_o_._c_o_o_k is the preferred form.

       _1_1_._4  _F_o_r_m _t_h_e _L_i_s_t_i_n_g _F_i_l_e_n_a_m_e

       The listing file, if not explicitly named in the environment
       variable or on the command line, will be  the  name  of  the
       cookbook, with any suffix removed and '.list' appended.

       _1_1_._5  _C_r_e_a_t_e _t_h_e _L_i_s_t_i_n_g _f_i_l_e

       The  listing  file  is created.  If ccooookk is executing in the
       background, or the --NNooTTTTyy option has been specified,  _s_t_d_o_u_t
       and  _s_t_d_e_r_r  will  be  redirected into the listing file.  If
       ccooookk is executing in the foreground, and the  --NNooTTTTyy  option
       has not been specified, _s_t_d_o_u_t and _s_t_d_e_r_r will be redirected
       into a pipe to a _t_e_e(1) command; which will, in  turn,  copy
       the output into the named file.

       A  heading  line  with the name of the file and the date, is
       generated.


       
       Peter Miller                                        Page 103





       Cook                                              User Guide



       _1_1_._6  _S_c_a_n _t_h_e _C_o_o_k_b_o_o_k

       When ccooookk  reads  the  cookbook  it  evaluates  all  of  the
       statements   it  finds  in  it.   Usually  these  statements
       instantiate recipes, although other things are possible.

       Recipes  contain   statements   that   are   not   evaluated
       immediately,  but  which  are remembered for later execution
       when cooking a target.  The meaning of a cookbook is defined
       in chapter X.

       _1_1_._7  _D_e_t_e_r_m_i_n_e _t_a_r_g_e_t_s _t_o _c_o_o_k

       If  no  target  files  are  named  on  the command line, the
       targets of the first defined explicit or ingredients recipe.
       It is an error if this is none.

       _1_1_._8  _C_o_o_k_i_n_g _a _T_a_r_g_e_t

       A derivation graph is formed using all of the targets given.
       Once the derivation graph is  formed,  it  will  be  walked,
       looking for files which are out of date.

       To  build  the  derivation  graph  for  a  target,  each the
       following steps is performed in the order given:

         1.  CCooookk exploits knowledge of the derivation  graph  that
             the user may provide to it:

                +o If  the  _g_r_a_p_h___e_x_t_e_r_i_o_r___f_i_l_e variable is set, and
                  the file name is listed in it, the file is not  a
                  leaf,  and  the derivation will backtrack and try
                  another alternative.

                +o If the _g_r_a_p_h___e_x_t_e_r_i_o_r___p_a_t_t_e_r_n  variable  is  set,
                  and  the  file  name  matches one of the patterns
                  listed in it, the file is not  a  leaf,  and  the
                  derivation   will   backtrack   and  try  another
                  alternative.

                +o If the _g_r_a_p_h___l_e_a_f___f_i_l_e variable is set,  and  the
                  file  name  is  listed  in it, the file is a leaf
                  file of the derivation.   There  is  no  need  to
                  attempt  to  apply  any  recipes.   It will be an
                  error if the file does not exist.

                +o If the _g_r_a_p_h___l_e_a_f___p_a_t_t_e_r_n variable  is  set,  and
                  the  file name matches one of the patterns listed
                  in it, the file is a leaf file of the derivation.
                  There is no need to attempt to apply any recipes.
                  It will be an error if the file does not exist.
             These optimizations require an  accurate  source  file
             manifest,  but  can  result is substantial performance
             improvements.

       
       Peter Miller                                        Page 104





       Cook                                              User Guide



         2.  CCooookk  scans  through  the   instantiated   ingredients
             recipes   in   the   order  they  were  defined.   All
             ingredients recipes with the target  in  their  target
             list are used.

             If  a  recipe  is used, then any ingredients also have
             their derivation graph constructed.  When walking  the
             graph,  if any of the ingredients are younger than the
             target, all other explicit or  implicit  recipes  with
             the same target will be deemed to be out of date.10

         3.  CCooookk  then  scans  through  the  instantiated explicit
             recipes in the order they were defined.  All  explicit
             recipes with the target in their target list are used.

             If a recipe is a used, the ingredients also have their
             derivation graph constructed.  When walking the graph,
             if any ingredients are out of date or the target  does
             not  yet  exist  (or  the  "forced" flag is set in the
             recipe's  _s_e_t  clause)  the  recipe   body   will   be
             performed.   If  a  recipe has no ingredients, it will
             not be performed,  unless  the  target  does  not  yet
             exist, or it is forced.

         4.  If  the  target  was  not  in  the  target list of any
             explicit recipe,  ccooookk  then  scans  the  instantiated
             implicit  recipes  in  the order they were defined, in
             two passes.   Implicit  recipes  which  not  not  have
             pattern  elements  in  the basename of the targets are
             scanned before implicit recipes which do have patterns
             in  the  basename.   Usually  this  has no significant
             effect, however in heavily heterogeneous  builds  this
             method  is  often  used in constructing the dependency
             files, so that  all  architectures  may  use  the  one
             implicit  dependency recipe, rather than stating every
             architecture explicitly.  Within each pass, the  order
             of scan is the order of definition.

             Implicit  recipe targets and ingredients may contain a
             wildcard  character  (%%),  which  is  why   they   are
             implicit.   When  expressions  are evaluated into word
             lists in an implicit recipe, any word  containing  the
             wildcard  character  (%%)  will  be expanded out by the
             current wildcard expansion.

             If the target matches a pattern in the targets  of  an
             implicit  recipe,  it is a candidate.  Each ingredient
             of a candidate recipe is recursively cooked.   If  any
             ingredient  cannot be cooked, then the implicit recipe

       ____________________

       10.A  target  which  does  not exist yet is considered to be
          infinitely ancient, and thus everything is  younger  than
          it.

       Peter Miller                                        Page 105





       Cook                                              User Guide



             is not used.  If all ingredients can be  cooked,  then
             the implicit recipe is used.

             If   an   implicit   recipe  is  a  used,  the  forced
             ingredients   also   have   their   derivation   graph
             constructed.   It  is  an error if a forced ingredient
             cannot be constructed.

             Only the first implicit recipe to get to this point is
             used.  The scan stops at this point.

         5.  If the target is not the subject of any ingredients or
             explicit  recipe,  and  no  implicit  recipes  can  be
             applied,  then  several  derivations are attempted, in
             the order specified:

                +o If the _g_r_a_p_h___i_n_t_e_r_i_o_r___f_i_l_e variable is  set,  and
                  the  file name is listed in it, the file is a not
                  leaf file of the derivation.  Cook will backtrack
                  and try another alternative.

                +o If  the  _g_r_a_p_h___i_n_t_e_r_i_o_r___p_a_t_t_e_r_n  variable is set,
                  and the file name matches  one  of  the  patterns
                  listed  in it, the file is a not leaf file of the
                  derivation.  Cook will backtrack and try  another
                  alternative.

                +o If  the  _g_r_a_p_h___l_e_a_f___f_i_l_e variable is set, and the
                  file name is listed in it, the  file  is  a  leaf
                  file  of  the derivation.  It will be an error if
                  the file does not exist.

                +o If the _g_r_a_p_h___l_e_a_f___p_a_t_t_e_r_n variable  is  set,  and
                  the  file name matches one of the patterns listed
                  in it, the file is a leaf file of the derivation.
                  It will be an error if the file does not exist.

                +o If    either    of    the    _g_r_a_p_h___l_e_a_f___f_i_l_e   or
                  _g_r_a_p_h___l_e_a_f___p_a_t_t_e_r_n variables are  set,  then  the
                  file  is  not  a  leaf,  and  the derivation will
                  backtrack and try another alternative.

                +o If the file exists, then it is up  to  date,  and
                  the file is a leaf file of the derivation.

                +o If the file does not exist then CCooookk doesn't know
                  how, and the derivation will  backtrack  and  try
                  another alternative.

       If  a  command in the body of any recipe fail, ccooookk will not
       that body any further, and will not perform the body of  any
       recipe  for  which  the  target of the failed actions was an
       ingredient, directly or indirectly.


       
       Peter Miller                                        Page 106





       Cook                                              User Guide



       CCooookk will trap recursive looping of targets.

          +o If the file exists, the it is up to date, or

          +o If the file does not exist then ccooookk doesn't know  how.

       _1_1_._9  _T_h_e _D_e_p_e_n_d_e_n_c_y _G_r_a_p_h

       The  above section describes how Cook derives the dependency
       graph.  Once the dependency graph has been  derived,  it  is
       then  walked.  The next section describes a little about how
       Cook walks the dependency graph.

       Cook is a simple kind of expert system.  You give it  a  set
       of  of  recipes for how to construct things, and a target to
       be constructed.  The recipes can be  decomposed  into  pair-
       wise ordered dependencies between files.

       Cook  determines  how  to build the target by constructing a
       _d_i_r_e_c_t_e_d _a_c_y_c_l_i_c _g_r_a_p_h.  The vertexes of this graph are  the
       files  in the system, the edges in this graph are the inter-
       file dependencies.  The edges  of  the  graph  are  directed
       because  the pair-wise dependencies are ordered resulting in
       a _a_c_y_c_l_i_c graph - things which look like loops are  resolved
       by the direction of the edges.

       For  example, if you have a simple cookbook (with the recipe
       bodies omitted for simplicity) like this:
            program: one.o two.o;
            one.o: one.c one.h;
            two.o: two.c two.h one.h;
       here is the corresponding directed acyclic graph.
       

                                 program



                        one.o               two.o




                   one.c     one.h     two.c     two.h



       There are several things that can be  done  with  the  graph
       once it has been derived:
       *  It can be walked to verify and regenerate the referential
       integrity of the files (the usual case), or
       * it can walked to print  the  pair-wise  dependencies  (the
       -pairs option), or
       *  it  can be walked to generate a shell script (the -script

       
       Peter Miller                                        Page 107





       Cook                                              User Guide



       option) which does  something  very  similar  to  the  first
       option.

       _1_1_._9_._1  _E_d_g_e _T_y_p_e_s
       Each  of the arrows in the above graph have a specific type.

       _s_t_r_i_c_t edges mean that Cook will decide  that  a  target  is
             out-of-date  if its time stamp is not strictly younger
             than all of the ingredients.  This  is  almost  always
             what you want.

       _w_e_a_k  edges mean that Cook will decide that a target is out-
             of-date if its time stamp is older  than  any  of  the
             ingredients.   This means that the times stamps of the
             target and ingredients may be equal - this  is  useful
             for  hard links and symbolic links.  You specify edges
             of this type by appending the ``(weak)'' string to the
             name of the ingredient.

       _e_x_i_s_t_s edges  mean that Cook will arrange for the ingredient
             to be cooked before the recipe is run,  but  the  time
             stamp  _i_s  _n_o_t  _c_o_n_s_u_l_t_e_d.  The ingredient cannot ever
             make the target  out-of-date.   This  is  useful  form
             coping with version stamps which change often, but you
             don't want to re-link unless something  else  changes.
             You  specify  edges  of  this  type  by  appending the
             ``(exists)'' string to the name of the ingredient.
       The default edge type is ``_s_t_r_i_c_t''.  You can use the "time-
       adjust" setting (see the "set" command) to make this simpler
       on very fast machines.

       _1_1_._1_0  _F_i_l_e _S_t_a_t_u_s

       CCooookk determines the time a file was last modified by  asking
       the  operating  system.   Because this operation tends to be
       performed  frequently,  ccooookk  maintains  a  cache  of   this
       information,   rather  than  make  redundant  calls  to  the
       operating system.  Because this information is cached, it is
       possible for ccooookk's memory of a file's last-modified time to
       become inconsistent with  the  file's  actual  last-modified
       time.   In particular, ccooookk doe _n_o_t ask the operating system
       for the "new" last-modified time of a recipe target  once  a
       recipe  body is completed.  Careful use of the set clearstat
       clause  will  generally  prevent  this.   For  example,  the
       following  recipe  needs  to create a directory when writing
       its output:
            bin/%: [%_obj]
            {
                    if [not [exists bin]] then
                            mkdir bin;
                    [cc] -o [target] [need];
            }
       If there were several programs being  cooked,  e.g.  _b_i_n_/_f_o_o
       and  _b_i_n_/_b_a_r,  the second time ccooookk performed the recipe, it

       
       Peter Miller                                        Page 108





       Cook                                              User Guide



       would erroneously attempt to make the _b_i_n directory a second
       time  -  contrary to the test.  This is because _[_e_x_i_s_t_s _b_i_n_]
       used the cache, and nothing tells ccooookk that the cache is now
       wrong.  The recipe should have been written
            bin/%: [%_obj]
            {
                    if [not [exists bin]] then
                            mkdir bin
                                    set clearstat;
                    [cc] -o [target] [need];
            }
       which  tells  ccooookk  that it should remove any files named in
       the _m_k_d_i_r command from the cache.

       An alternative way of performing the above example is to set
       the _m_k_d_i_r recipe flag:
            bin/%: [%_obj]
                    set mkdir
            {
                    [cc] -o [target] [need];
            }
       This  flag  instructs  ccooookk  to create the directory for the
       target before running the recipe body.  There is  a  similar
       _u_n_l_i_n_k  flag, which unlinks the targets of the recipe before
       running the recipe body.  These two flags take care of most,
       but not all, uses of the _c_l_e_a_r_s_t_a_t flag.

       A  second  mechanism  used  by  ccooookk  to determine the last-
       modified times of files is a file _f_i_n_g_e_r_p_r_i_n_t.   This  is  a
       cryptographically  strong  hash  of  the contents of a file.
       The  chances  of  two  different  files  having   the   same
       fingerprint  is less than 1 in 2**200.  If ccooookk notices that
       a file has  changed,  because  its  last-modified  time  has
       changed,  a  fingerprint  is  taken of the file and compared
       with  the  remembered  fingerprint.   If  the   fingerprints
       differ,  the  file  is  considered  to be different.  If the
       fingerprints match, the  file  is  considered  not  to  have
       changed.

       This description of fingerprints is somewhat simplified, the
       actual mechanics depends on remembering two different  last-
       modified times, as well as the fingerprint, in a file called
       _._c_o_o_k_._f_p in the current directory.

       Fingerprinting can cause some surprises.  For example,  when
       you  use  the  _t_o_u_c_h(1)  command, ccooookk will often fail to do
       anything, and report instead that everything is  up-to-date.
       This  is  because  the fingerprint has not changed.  In this
       situation, either remove  the  _._c_o_o_k_._f_p  file,  or  use  the
       --NNoo__FFiinnggeerrPPrriinntt command line option.





       
       Peter Miller                                        Page 109





       Cook                                              User Guide



       _1_2_.  _O_p_t_i_o_n _P_r_e_c_e_d_e_n_c_e

       At  various  points in the description there are a number of
       flags and options with the same, or similar,  names.   These
       are in fact different levels of the same option.

       The different levels, from highest precedence to lowest, are
       as follows.

       Error     This level is used  to  disable  undesirable  side
                 effects when an error occurs.

       Command Line Options  specified on the command line override
                 almost everything.  There are some isolated  cases
                 where  there is no equivalent command line option.
                 They are in scope for the entire ccooookk session.

       Execute   When a command attached to a recipe  is  executed,
                 the  flags  in  the  'sseett'  clause  are given this
                 precedence.  They are in scope for the duration of
                 the execution of the command they are bound to.

       Recipe    When  a recipe is considered for use, the flags in
                 the 'sseett' clause are given the  precedence.   They
                 are in scope for the evaluation of the ingredients
                 names and the execution of the recipe  body;  they
                 are not in scope while cooking the ingredients.

       Cookbook  When  a  'sseett'  statement  is  encountered  in the
                 cookbook, the  option  are  given  this  priority.
                 They  are  in  scope  until  the  end  of the ccooookk
                 session.

       Environment Variable
                 When the  options in the CCOOOOKK environment variable
                 are set, they are given this precedence.  They are
                 in scope for the entire ccooookk session.

       Default   All options have a default setting.  The  defaults
                 noted  in  chapter  3  are  given this precedence.
                 They are in scope for the entire ccooookk session.














       
       Peter Miller                                        Page 110





       Cook                                              User Guide



       _1_3_.  _F_i_l_e _n_a_m_e _p_a_t_t_e_r_n_s

       There are two pattern matchers to choose from.

       The  tough  part  about  designing  a  pattern  matcher  for
       something  like  Cook  is  that _i_d_e_a_l_l_y the patterns must be
       reversible.  That is, it must be possible to  use  the  same
       string  both  as  a  pattern  to be matched against and as a
       template for building a string once a pattern  has  matched.
       Rather  like the difference between the left and right sides
       of an editor search-and-replace command in an  editor  using
       the  same  description  for  both the search pattern and the
       replace template.  This is why classic  regular  expressions
       are not the default.

       The  choice  of  which pattern matcher to use is dictated by
       flag settings:

       set match-mode-cook
            This causes patterns to be matched using Cook's  native
            patterns.  This is the default.

       set match-mode-regex
            This  causes  patterns  to  be  matched  using  regular
            expressions.

       The match mode to use may be set at the cookbook level
            set match-mode-cook;
       or at the recipe level
            %.o: %.c
                    set match-mode-cook
            {
                    [cc] -o %.o -c %.c;
            }
       if you want to change your mind temporarily.

       The match mode also affects match functions, such as _f_i_l_t_e_r,
       _f_i_l_t_e_r___o_u_t,  _f_r_o_m_t_o,  _m_a_t_c_h___m_a_s_k,  _m_a_t_c_h_e_s and _p_a_t_s_u_b_s_t.  If
       you use these in your user-defined functions, you need to be
       extra careful about this.

       The  match  mode  also  affects the graph variables, used to
       specify explicit graph interior and leaf files.

       _1_3_._1  _C_o_o_k _P_a_t_t_e_r_n_s

       The native Cook pattern matcher has symmetric left-hand-side
       and  right-hand-side  patterns.   This  is best demonstrated
       with an example recipe:
            %.c %.h: %.y
                    set match-mode-cook
            {
                    yacc -d %.y;
                    mv yy.tab.c %.c;

       
       Peter Miller                                        Page 111





       Cook                                              User Guide



                    mv yy.tab.h %.h;
            }
       Notice how the left-hand-side of the  recipe  (the  targets)
       uses  the same style of patterns as the right-hand-side (the
       ingredients and the recipe body).

       This matcher has eleven match "fields", referenced as %%  and
       %%00  to %%99.  The %% character can be escaped as %%%%.  The %% and
       %%11 to %%99 forms match any character except slash  (//);  these
       forms  may  not  match  a  leading  empty  string,  to avoid
       problems with false matches against absolute paths.  The  %%00
       form  matches  all  characters, but must be either empty, or
       have whole path components, including the trailing // on each
       component.

       A few examples will make this clearer:

                        +-------------------------+
                        |string    does not match |
                        +-------------------------+
                        |%.c       snot/fred.c    |
                        |%1/%2.c   etc/boo/fred.c |
                        +-------------------------+
       +---------------------------------------------------------------+
       |string                 matches                 setting         |
       +---------------------------------------------------------------+
       |%.c                    fred.c                  %="fred"        |
       |%1/%2.c                snot/fred.c             %1="snot"       |
       |                                               %2="fred"       |
       |%0%5.c                 fred.c                  %0=""           |
       |                                               %5="fred"       |
       |%0%6.c                 snot/fred.c             %0="snot/"      |
       |                                               %6="fred"       |
       |%0%7.c                 etc/boo/fred.c          %0="etc/boo/"   |
       |                                               %7="fred"       |
       |/usr/%1/%1%2/%3.%2%4   /usr/man/man1/fred.1x   %1="man"        |
       |                                               %2="1"          |
       |                                               %3="fred"       |
       |                                               %4="x"          |
       +---------------------------------------------------------------+
       The  %%00 behavior is designed to allow patterns to range over
       subtrees in a controlled manner.  Note that the use of  this
       sort  of  pattern in a recipe will result in deeper searches
       than the naive recipe designer would expect.

       _1_3_._1_._1  _E_x_a_m_p_l_e_s
       There are two main places where patterns are used: with  the
       _m_a_t_c_h___m_a_s_k and _f_r_o_m_t_o functions, and in recipes.

       You  can  perform  file  name  filtering  and  rewriting  as
       follows:
            source_files = [collect cat MANIFEST];
            object_files =
                    [fromto %0%.c %0%.o [match_mask %0%.c [manifest]]]

       
       Peter Miller                                        Page 112





       Cook                                              User Guide



                    [fromto %0%.y %0%.gen.o [match_mask %0%.y [manifest]]]
                    ;

       The recipes to go with the above files may be
            %0%.o: %0%.c
                    single-thread ["if" %0 "then" %.o]
            {
                    /* note: no slash before dot */
                    cc -c -I%0. %0%.c;
                    if %0 then
                            mv %.o %0%.o;
            }
       This recipe can compile files  in  a  large  project,  where
       source  files  appear  in  a number of sub-directories.  The
       ``-I%0.'' ensures that there are locally include-able  files
       in  the  sub-directories.   If  the ``%0'' had been entirely
       omitted from the recipe, it will only compile files  in  the
       current directory.

       A  common _y_a_c_c recipe, used when there is more than one yacc
       grammar in a project, looks like this:
            %0%.gen.c %0%.gen.h: %0%.y
                    single-thread yy.tab.c yy.tab.h
            {
                    yacc -d %0%.y
                    yy = [collect echo %0% | sed "'s/[^A-Za-z0-9]/_/'"];
                    sed "'s/[yY][yY]/"[yy]"_/g'" yy.tab.c > %0%.gen.c;
                    sed "'s/[yY][yY]/"[yy]"_/g'" yy.tab.h > %0%.gen.h;
                    rm yy.tab.c yy.tab.h;
            }
       To be more selective about  the  ``%0''  portion,  use  more
       pattern elements before or after it.

       _1_3_._2  _R_e_g_u_l_a_r _E_x_p_r_e_s_s_i_o_n_s

       The  regular  expression  pattern matcher uses POSIX regular
       expressions.  It has asymmetric  left-hand-side  and  right-
       hand-side  patterns.   This  is  best  demonstrated  with an
       example recipe:
            \\(.*\\)\\.c \\(.*\\)\\.h: \\1.y
                    set match-mode-regex
            {
                    yacc -d \\1.y;
                    mv yy.tab.c \\1.c;
                    mv yy.tab.h \\1.h;
            }
       Notice how the left-hand-side of the  recipe  (the  targets)
       uses  a completely different style of patterns as the right-
       hand-side (the ingredients and the recipe body).

       All those backslashes are necessary, because Cook  uniformly
       applies  C  escapes  to  strings  when it reads them, and it
       doesn't know you mean a regular expression  backslash  until
       you use it in a recipe context.

       
       Peter Miller                                        Page 113





       Cook                                              User Guide



       See  _r_e___f_o_r_m_a_t(7)  for  a definition of POSIX 1003.2 regular
       expressions; you want the ``basic'' REs.

       Please note that characters which are special to  Cook  will
       need  to be escaped with a backslash, or enclosed in quotes.
       These  include  curly  braces  (``{''  and  ``}''),   square
       brackets   (``[''  and  ``]''),  colon  (``:'')  and  equals
       (``='').  Backslash always  needs  to  be  escaped,  whether
       encoded  in  a  string  or  not,  because within a string it
       serves to escape the string terminator.

       You also need to remember  that  dot  (``.'')  is  a  common
       character  in  filenames,  and frequenty significant in file
       name patters, but it is a regular expression wildcard.   You
       need to escape it to make it literal.

       You  need  to make absolutely certain that when recipes have
       more than one left-hand-size (as in the yacc  example)  that
       the  patterns  _a_l_l  assign  identical values to their nested
       sub-expressions.

       The usual right-hand-side  replacements  are  available:  an
       escaped  number  is  replaced  with  the  _n-th  nested  sub-
       expression; and the ampersand (``&'')  is  replaced  by  the
       whole  left-hand-side  (if you have more than one left-hand-
       side, this is ambiguous).  Backslash may be used  to  escape
       them.

       _1_3_._2_._1  _E_x_a_m_p_l_e_s
       There  are two main places where patterns are used: with the
       _m_a_t_c_h___m_a_s_k and _f_r_o_m_t_o functions, and in recipes.

       You  can  perform  file  name  filtering  and  rewriting  as
       follows:
            set match-mode-regex;
            source_files = [collect cat MANIFEST];
            object_files =
                    [fromto \\(.*\\)\\.c \\1.o
                            [match_mask \\(.*\\)\\.c [manifest]]]
                    [fromto \\(.*\\)\\.y \\1.gen.o
                            [match_mask \\(.*\\)\\.y [manifest]]]
                    ;

       The recipes to go with the above files may be
            \\(.*\\)\\.o: \\1.c
                    single-thread ["if" [not [in [relative_dirname \\1] .]]
                            "then" [notdir \\1.o]]
            {
                    cc -c -I[[relative_dirname \\1] \\1.c;
                    if [not [in [relative_dirname \\1] .]] then
                            mv [notdir \\1.o] \\1.o;
            }
       This  recipe  can  compile  files  in a large project, where
       source files appear in a  number  of  sub-directories.   The

       
       Peter Miller                                        Page 114





       Cook                                              User Guide



       ``-I\\1.'' ensures that there are locally include-able files
       in the sub-directories.

       A common _y_a_c_c recipe, used when there is more than one  yacc
       grammar in a project, looks like this:
            \\(.*\\)\\.gen.c \\(.*\\)\\.gen.h: \\1.y
                    single-thread yy.tab.c yy.tab.h
            {
                    yacc -d \\1.y
                    yy = [collect echo \\1 | sed "'s/[^A-Za-z0-9]/_/'"];
                    sed "'s/[yY][yY]/"[yy]"_/g'" yy.tab.c > \\1.gen.c;
                    sed "'s/[yY][yY]/"[yy]"_/g'" yy.tab.h > \\1.gen.h;
                    rm yy.tab.c yy.tab.h;
            }
       To  be  more  selective  about the ``\\(.*\\)'' portion, use
       more pattern elements before or after it.







































       
       Peter Miller                                        Page 115





       Cook                                              User Guide



       _1_4_.  _S_u_p_p_l_i_e_d _C_o_o_k_b_o_o_k_s

       A number of cookbooks are supplied with ccooookk.  To  make  use
       of one, a preprocessor directive of the form
            #include "_w_h_i_c_h_o_n_e"
       must appear at the start of your cookbook.

       CCooookk  does not have any "built-in" recipes.  All recipes are
       stored  in  text  files,  so  they  are  more  easily  read,
       understood,  copied,  hacked  or  corrected.   The  supplied
       cookbooks live in the _/_u_s_r_/_p_k_g_/_s_h_a_r_e_/_c_o_o_k directory.

       You  may  supply  your  own  "system"  recipes,  by  placing
       cookbooks  into  a directory called _$_H_O_M_E_/_._c_o_o_k or using the
       --IInncclluuddee  command  line  option,  possibly  in  your   _$_C_O_O_K
       environment variable.

       _1_4_._1  _a_s

       This cookbook defines how to use the assembler.

       _1_4_._1_._1  _r_e_c_i_p_e_s


       %.o: %.s  Construct   object  files  from  assembler  source
                 files.

       _1_4_._1_._2  _v_a_r_i_a_b_l_e_s


       as        The assembler command.   Not  altered  if  already
                 defined.

       as_flags  Options   to  pass  the  assembler  command.   Not
                 altered if already defined.  The default is empty.

       as_src    Assembler source files in the current directory.

       dot_src   Source   files   constructable   in   the  current
                 directory  (unioned  with  existing  setting,   if
                 necessary).

       dot_obj   Object   files   constructable   in   the  current
                 directory  (unioned  with  existing  setting,   if
                 necessary).

       dot_clean Files  which  may  be  removed  from  the  current
                 directory in a clean target.







       
       Peter Miller                                        Page 116





       Cook                                              User Guide



       _1_4_._2  _c

       This cookbook describes how to work with C  files.   Include
       file dependencies are automatically determined.

       _1_4_._2_._1  _r_e_c_i_p_e_s


       %.o: %.c  Construct  object  files form C source files, with
                 automatic include file dependency detection.

       %.ln: %.c Construct lint object files from C  source  files,
                 with  automatic include file dependency detection.

       _1_4_._2_._2  _v_a_r_i_a_b_l_e_s


       c_incl    The C include  dependency  sniffer  command.   Not
                 altered if already defined.

       cc        The  C  compiler  command.  Not altered if already
                 defined.

       lint      The lint command.  Not altered if already defined.

       cc_flags  Options  to  pass  to the C compiler command.  Not
                 altered if already defined.  The default is  "-O".

       cc_include_flags Options passed to the C compiler and c_incl
                 controlling include file searching.   Not  altered
                 if already defined.  The default is empty.

       cc_src    C source files in the current directory.

       dot_src   Source   files   constructable   in   the  current
                 directory  (unioned  with  existing  setting,   if
                 necessary).

       dot_obj   Object   files   constructable   in   the  current
                 directory  (unioned  with  existing  setting,   if
                 necessary).

       dot_clean Files  which  may  be  removed  from  the  current
                 directory in a clean target.

       dot_lint_obj Lint object files constructable in the  current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       _1_4_._2_._3  _S_e_e _A_l_s_o
       The ``library'' cookbook,  for  linking  C  sources  into  a
       library.
       The  ``program''  cookbook,  for  linking  C  sources into a
       program.

       
       Peter Miller                                        Page 117





       Cook                                              User Guide



       _1_4_._3  _f_7_7

       This cookbook describes how to work with Fortran files.

       _1_4_._3_._1  _r_e_c_i_p_e_s


       %.o: %.f77 Construct object files form Fortran source files.

       _1_4_._3_._2  _v_a_r_i_a_b_l_e_s


       f77       The  Fortran  compiler  command.   Not  altered if
                 already defined.

       f77_flags Options to pass to the Fortran  compiler  command.
                 Not  altered  if  already defined.  The default is
                 "-O".

       f77_src   Fortran source files in the current directory.

       dot_src   Source  files   constructable   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       dot_obj   Object  files   constructable   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       dot_clean Files  which  may  be  removed  from  the  current
                 directory in a clean target.

       _1_4_._3_._3  _S_e_e _A_l_s_o
       The ``library'' cookbook, for linking Fortran sources into a
       library.
       The ``program'' cookbook, for linking Fortran sources into a
       program.


















       
       Peter Miller                                        Page 118





       Cook                                              User Guide



       _1_4_._4  _g_7_7

       This  cookbook  is  the same as the ``f77'' cookbook, but it
       sets the _f_7_7 variable to the GNU Fortran compiler, g77.

       _1_4_._5  _g_c_c

       This cookbook is the same as the ``c'' cookbook, but it sets
       the _c_c variable to the GNU C compiler, gcc.

       _1_4_._6  _h_o_m_e

       This  cookbook  defined  where  certain directories are, and
       some common uses of those directories, relative to $HOME.

       _1_4_._6_._1  _v_a_r_i_a_b_l_e_s


       home      The current users' home directory.

       bin       The directory to place program binaries into.

       include   The directory to place include files into.

       lib       The directory to place libraries into.

       cc_include_flags The [include] directory is appended to  the
                 search options.

       cc_link_flags The  [lib] directory is appended to the search
                 options.

       _1_4_._7  _l_e_x

       This cookbook describes how to work with lex files.

       _1_4_._7_._1  _r_e_c_i_p_e_s


       %.c: %.l  Construct C source files from lex source files.

       _1_4_._7_._2  _v_a_r_i_a_b_l_e_s


       lex       The lex command.  Not altered if already  defined.

       lex_flags Options  to  pass to the lex command.  Not altered
                 if already defined.  The default is empty.

       lex_src   Lex source files in the current directory.

       dot_src   Source  files   constructible   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       
       Peter Miller                                        Page 119





       Cook                                              User Guide



       dot_obj   Object  files   constructible   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       dot_clean Files  which  may  be  removed  from  the  current
                 directory in a clean target.

       dot_lint_obj Lint  object files constructible in the current
                 directory  (unioned  with  existing  setting,   if
                 necessary).

       _1_4_._8  _l_i_b_r_a_r_y

       This cookbook defines how to construct a library.

       If  an include file (or files) are defined for this library,
       you will have to append them to [install] in your _H_o_w_t_o_._c_o_o_k
       file.

       _1_4_._8_._1  _v_a_r_i_a_b_l_e_s


       all       targets of the all recipe

       install   targets of the install recipe

       me        The   name  of  the  library  to  be  constructed.
                 Defaults to the last component of the pathname  of
                 the current directory.

       ar        The archive command.

       install   targets  of  the install command.  Only defined if
                 the [lib] variable is defined.

       _1_4_._8_._2  _r_e_c_i_p_e_s


       all       construct the targets defined in [all].

       clean     remove the files named in [dot_clean].

       clobber   remove the files name in [dot_clean] and [all].

       install   Construct the  files  named  in  [install].   Only
                 defined if the [lib] variable is defined.

       uninstall Remove the files named in [install].  Only defined
                 if the [lib] variable is defined.






       
       Peter Miller                                        Page 120





       Cook                                              User Guide



       _1_4_._9  _p_r_i_n_t

       This cookbook is  used  to  print  files.   It  will  almost
       certainly need to be changed for every site.

       _1_4_._9_._1  _r_e_c_i_p_e_s


       %.lw: %.ps Print a PostScript file.

       %.lp: %   Print a text file.

       _1_4_._9_._2  _v_a_r_i_a_b_l_e_s


       lp        The   print   command.   Not  altered  if  already
                 defined.

       lp_flags  Options passed to the print command.  Not  altered
                 if already defined.  Defaults to empty.

       _1_4_._1_0  _p_r_o_g_r_a_m

       This cookbook defines how to construct a program.

       If  your program uses any libraries, you will have to append
       them to [ld_libraries] in your _H_o_w_t_o_._c_o_o_k file.

       _1_4_._1_0_._1  _v_a_r_i_a_b_l_e_s


       all       Targets of the all recipe.

       install   targets of the install recipe

       ld        The name of the linker command.   Not  altered  if
                 already  defined.   Set  to the same as the ``cc''
                 variable if set, otherwise set to the same as  the
                 ``f77''  variable if set, otherwise set to ``ld''.

       ld_flags  Not altered if already defined.   The  default  is
                 empty.

       ld_libraries Options  passed to the C compiler when linking,
                 these are typically library search paths (-L)  and
                 libraries  (-l).   Not altered if already defined.
                 The default is empty.

       me        The  name  of  the  program  to  be   constructed.
                 Defaults  to the last component of the pathname of
                 the current directory.




       
       Peter Miller                                        Page 121





       Cook                                              User Guide



       _1_4_._1_0_._2  _r_e_c_i_p_e_s


       all       Construct the targets named in [all].

       clean     Remove the files named in [dot_clean].

       clobber   Remove the files named in [dot_clean] and [all].

       install   Construct the  files  named  in  [install].   Only
                 defined if the [lib] variable is defined.

       uninstall Remove the files named in [install].  Only defined
                 if the [lib] variable is defined.

       _1_4_._1_0_._3  _S_e_e _A_l_s_o
       The ``c'' cookbook, for C sources.
       The ``f77'' cookbook, for Fortran sources.
       The ``usr'' or  ``usr.local''  or  ``home''  cookbooks,  for
       defining install locations.

       _1_4_._1_1  _r_c_s

       This cookbook is used to extract files from RCS.

       _1_4_._1_1_._1  _r_e_c_i_p_e_s


       %: RCS/%,v Extract files from RCS.

       %: %,v    Extract files from RCS.

       _1_4_._1_1_._2  _v_a_r_i_a_b_l_e_s


       co        The RCS checkout command.

       co_flags  Flags for the co command, default to empty.

















       
       Peter Miller                                        Page 122





       Cook                                              User Guide



       _1_4_._1_2  _r_e_c_u_r_s_i_v_e

       This  cookbook  may  be  used  to  construct  recursive cook
       direwctory structures, where  the  top-level  cookbook  only
       invokes cookbooks in deeper directories.

       All  largets  given  to  this  cookbook  result  in all sub-
       directories containing a _H_o_w_t_o_._c_o_o_k file having ccooookk invoked
       with the same target.

       _1_4_._1_2_._1  _R_e_c_i_p_e_s
       The  _a_l_l  recipe  is  defined,  but it does nothing, it only
       exists to set the default target name.

       _1_4_._1_3  _s_c_c_s

       This cookbook is used to extract files from SCCS.

       _1_4_._1_3_._1  _r_e_c_i_p_e_s


       %: SCCS/s.% Extract files from SCCS.

       %: s.%    Extract files from SCCS.

       _1_4_._1_3_._2  _v_a_r_i_a_b_l_e_s


       get       The SCCS get command.

       get_flags Flags for the get command, default to empty.

       _1_4_._1_4  _t_e_x_t

       This cookbook is used to process text documents.

       Include file dependencies are automatically  detected.   The
       requirements  for  various  preprocessors  are automatically
       detected (_e_._g_. eqn, tbl, pic, graf).

       _1_4_._1_4_._1  _r_e_c_i_p_e_s


       %.ps: %.t PostScript for generic *roff source.

       %: %.t    Straight text from *roff source.

       _1_4_._1_4_._2  _v_a_r_i_a_b_l_e_s


       text_incl The    text_incl    command     (finds     include
                 dependencies).  Not altered if already set.



       
       Peter Miller                                        Page 123





       Cook                                              User Guide



       text_roff The    text_roff   command   (finds   preprocessor
                 requirements).  Not altered if already set.

       roff_flags Arguments passed to text_roff, and indirectly  to
                 the  *roff  program.   Not altered if already set.
                 Defaults to empty.

       _1_4_._1_5  _u_s_r_._l_o_c_a_l

       This cookbook defined where  certain  directories  are,  and
       some   common   uses   of  those  directories,  relative  to
       /usr/local.

       _1_4_._1_5_._1  _v_a_r_i_a_b_l_e_s


       bin       The directory to place program binaries into.

       include   The directory to place include files into.

       lib       The directory to place libraries into.

       cc_include_flags The [include] directory  is  added  to  the
                 search options.

       cc_link_flags The  [lib]  directory  is  added to the search
                 options.

       _1_4_._1_6  _u_s_r

       This  cookbook  defined  where  certain   directories   are,
       relative to /usr.

       _1_4_._1_6_._1  _v_a_r_i_a_b_l_e_s


       bin       The directory to place program binaries into.

       include   The directory to place include files into.

       lib       The directory to place libraries into.














       
       Peter Miller                                        Page 124





       Cook                                              User Guide



       _1_4_._1_7  _y_a_c_c___m_a_n_y

       This  cookbook  describes  how  to use yacc.  The difference
       with the "yacc" cookbook is that this cookbook allows you to
       have  more  that  one  yacc  generated  parser  in  the same
       program, by using the classic _s_e_d(1) hack of the output.

       _1_4_._1_8  _y_a_c_c

       This cookbook describes how to use yacc.

       You will have to add "-d" to the  [yacc_flags]  variable  if
       you want %.h files generated.

       If  a  _y_._o_u_t_p_u_t  file  is  constructed,  it will be moved to
       _%_._l_i_s_t.

       _1_4_._1_8_._1  _r_e_c_i_p_e_s


       %.c %.h: %.y Construct C source and header files  from  yacc
                 source files.  Applied if -d in [yacc_flags].

       %.c: %.y  Construct  C  source files from yacc source files.
                 Applied if -d not in [yacc_flags].

       _1_4_._1_8_._2  _v_a_r_i_a_b_l_e_s


       yacc_src  Yacc source files in the current directory.

       dot_src   Source  files   constructable   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       dot_obj   Object  files   constructable   in   the   current
                 directory   (unioned  with  existing  setting,  if
                 necessary).

       dot_clean Files  which  may  be  removed  from  the  current
                 directory in a clean target.

       dot_lint_obj Lint  object files constructable in the current
                 directory  (unioned  with  existing  setting,   if
                 necessary).










       
       Peter Miller                                        Page 125





       Cook                                              User Guide



       _1_5_.  _G_l_o_s_s_a_r_y

       This document employs a number of terms specific to ccooookk.

       _b_o_d_y      A  set  of  statements,  usually  commands,  to be
                 performed to _c_o_o_k the _t_a_r_g_e_ts of  a  _r_e_c_i_p_e  after
                 the _i_n_g_r_e_d_i_e_n_ts exist.

       _c_o_m_m_a_n_d   A  command  is a list of words to be passed to the
                 _o_p_e_r_a_t_i_n_g _s_y_s_t_e_m to be executed.

       _c_o_o_k      When used as a verb, refers to  the  actions  ccooookk
                 would  perform  to  create  a _t_a_r_g_e_t, according to
                 some _r_e_c_i_p_e.

       _c_o_o_k_b_o_o_k  A file containing input for ccooookk, usually _r_e_c_i_p_es.

       _e_x_p_l_i_c_i_t _r_e_c_i_p_e An  explicit recipe is one where the _t_a_r_g_e_ts
                 contain  no  patterns.   That  is,  there  are  no
                 percent ('%%') characters in any of the _t_a_r_g_e_ts.

       _f_i_n_g_e_r_p_r_i_n_t A  cryptographically strong hash of the contents
                 of a file, use to determine if the  file  contents
                 have changed.

       _f_l_a_g      A  flag  modifies  the behavior of a cook session,
                 _r_e_c_i_p_e or command.

       _f_o_r_c_e_d _i_n_g_r_e_d_i_e_n_t A files which must exist before  a  _t_a_r_g_e_t
                 file  of  an  _i_m_p_l_i_c_i_t  _r_e_c_i_p_e may be cooked.  The
                 inability to construct a forced ingredient  is  an
                 error.

       _f_u_n_c_t_i_o_n  A function is an action applied to a word list.

       _g_a_t_e      A gate is a condition which allows the conditional
                 application of a _r_e_c_i_p_e.  The gate condition is in
                 addition  to  the requirement that the ingredients
                 are cookable.

       _i_m_p_l_i_c_i_t _r_e_c_i_p_e An implicit recipe is a recipe with patterns
                 in the _t_a_r_g_e_ts.  That is, there is a percent ('%%')
                 character in at least one of the _t_a_r_g_e_ts.

       _i_n_g_r_e_d_i_e_n_t A files which must exist before a _t_a_r_g_e_t file may
                 be cooked.  In an _i_m_p_l_i_c_i_t _r_e_c_i_p_e the inability to
                 construct of an ingredient means that  the  _r_e_c_i_p_e
                 will  not  be  applied.  In an explicit recipe the
                 inability to construct an ingredient is an  error.

       _l_a_s_t_-_m_o_d_i_f_i_e_d _t_i_m_e
                 UNIX imbues files with several attributes.  One of
                 these is a time-stamp of when the  file  was  last
                 modified.   Usually this is when the file was last

       
       Peter Miller                                        Page 126





       Cook                                              User Guide



                 written to.

       _r_e_c_i_p_e    A _r_e_c_i_p_e consists of several parts.

                   1.  A set of _t_a_r_g_e_ts to be cooked,

                   2.  A set of ingredients of those _t_a_r_g_e_ts, and

                   3.  An optional set of forced ingredients.

                   4.  An optional set of flags.

                   5.  An optional gate.

                   6.  An optional body .

       _t_a_r_g_e_t    The object of a _r_e_c_i_p_e, a thing which is cooked.

       _t_o_u_c_h     UNIX imbues files with several attributes.  One of
                 these  is  a  time-stamp of when the file was last
                 modified.  Usually this is when the file was  last
                 written  to,  however  it  is  possible  to simply
                 adjust  this  attribute,  rather   than   actually
                 writing to the file; this is colloquially known as
                 _t_o_u_c_hing a file.

       _v_a_r_i_a_b_l_e  A variable is a named place holder  for  a  value.
                 The value may be changed.



























       
       Peter Miller                                        Page 127





       Cook                                              User Guide



























































       Peter Miller                                        Page 128









                                 CONTENTS



       1.   Introduction ........................................ 1
            1.1    Why You Want To Use Cook ..................... 1
            1.2    How to Use this Manual ....................... 2
            1.3    Ancient History .............................. 2

       2.   Cook from the Outside ............................... 4
            2.1    What can cook do for me? ..................... 4
            2.2    What is cook doing? .......................... 4
            2.3    What can cook always do? ..................... 4
            2.4    If something goes wrong ...................... 5
            2.5    The Reference Manual ......................... 5

       3.   Cook from a Cookbook ................................ 6
            3.1    What does Cook do? ........................... 6
            3.2    How do I tell Cook what to do? ............... 7
            3.3    Creating a Cookbook .......................... 8

       4.   Cooking in Parallel ................................ 10
            4.1    Command Line Option ......................... 10
            4.2    Cookbook Variable ........................... 10
            4.3    Recipe Writing .............................. 10
            4.4    File Locking ................................ 12
            4.5    Virtual Machine ............................. 12
            4.6    Virtual Machine, Revisited .................. 14

       5.   Include File Dependencies .......................... 17
            5.1    The Manual Method ........................... 17
            5.2    Debugging Cookbooks ......................... 17
            5.3    Tools ....................................... 19
            5.4    The Small Method ............................ 19
            5.5    The Large Method ............................ 20
            5.6    The Cascade Method .......................... 21
            5.7    Dependencies on Derived Files ............... 22
            5.8    Renaming Include Files ...................... 23

       6.   Building Large Projects ............................ 25
            6.1    Whole Project Build ......................... 25
            6.2    Private Work Areas .......................... 31
            6.3    Whole Project Build Advantages .............. 34
            6.4    Heterogenous Build .......................... 34
            6.5    Installing Things ........................... 36
            6.6    Miscellaneous ............................... 37
            6.7    File Fingerprints ........................... 38
            6.8    Coping with Links ........................... 41
            6.9    Coping with Version Stamps .................. 41

       7.   Cookbook Language Definition ....................... 43
            7.1    Lexical Analysis ............................ 43
                   7.1.1   Words and Keywords .................. 43


       
                                     i









                   7.1.2   Escape Sequences .................... 43
                   7.1.3   Quoting ............................. 44
                   7.1.4   Comments ............................ 44
            7.2    Preprocessor ................................ 44
                   7.2.1   include ............................. 44
                   7.2.2   include-cooked ...................... 45
                   7.2.3   include-cooked-nowarn ............... 46
                   7.2.4   if .................................. 46
                   7.2.5   ifdef ............................... 47
                   7.2.6   ifndef .............................. 47
                   7.2.7   pragma .............................. 47
            7.3    Syntax and Semantics ........................ 48
                   7.3.1   Overall Structure ................... 48
                   7.3.2   The Compound Statement .............. 48
                   7.3.3   Variables and Expressions ........... 48
                   7.3.4   Recipes ............................. 51
                   7.3.5   The Explicit Recipe Statement ....... 51
                   7.3.6   The Implicit Recipe Statement ....... 57
                   7.3.7   The Ingredients Recipe Statement .... 58
                   7.3.8   The Cascade Recipe Statement ........ 59
                   7.3.9   Commands ............................ 59
                   7.3.10  The Simple Command Statement ........ 59
                   7.3.11  The Data Command Statement .......... 60
                   7.3.12  The Set Statement ................... 61
                   7.3.13  The Fail Statement .................. 62
                   7.3.14  The If Statement .................... 62
                   7.3.15  The Loop and Loopend Statements ..... 62
                   7.3.16  Functions ........................... 63

       8.   Built-In Functions ................................. 66
            8.1    addprefix ................................... 66
            8.2    addsuffix ................................... 66
            8.3    and ......................................... 66
            8.4    basename .................................... 67
            8.5    cando ....................................... 67
            8.6    catenate .................................... 68
            8.7    collect_lines ............................... 68
            8.8    collect ..................................... 69
            8.9    cook ........................................ 69
            8.10   count ....................................... 70
            8.11   defined ..................................... 70
            8.12   dirname ..................................... 70
            8.13   dir ......................................... 71
            8.14   dos-path .................................... 71
            8.15   downcase .................................... 72
            8.16   entryname ................................... 72
            8.17   execute ..................................... 72
            8.18   exists ...................................... 73
            8.19   exists-symlink .............................. 73
            8.20   expr ........................................ 74
            8.21   filter_out .................................. 75
            8.22   filter ...................................... 75
            8.23   find_command ................................ 76


       
                                    ii









            8.24   findstring .................................. 76
            8.25   firstword ................................... 77
            8.26   fromto ...................................... 77
            8.27   getenv ...................................... 78
            8.28   glob ........................................ 78
            8.29   head ........................................ 79
            8.30   home ........................................ 79
            8.31   if .......................................... 79
            8.32   in .......................................... 80
            8.33   interior_files .............................. 80
            8.34   join ........................................ 81
            8.35   leaf_files .................................. 81
            8.36   matches ..................................... 81
            8.37   match_mask .................................. 82
            8.38   mtime ....................................... 82
            8.39   mtime-seconds ............................... 82
            8.40   notdir ...................................... 83
            8.41   not ......................................... 83
            8.42   operating_system ............................ 84
            8.43   options ..................................... 85
            8.44   or .......................................... 86
            8.45   pathname .................................... 86
            8.46   patsubst .................................... 86
            8.47   prepost ..................................... 87
            8.48   print ....................................... 87
            8.49   quote ....................................... 87
            8.50   read_lines .................................. 88
            8.51   readlink .................................... 88
            8.52   read ........................................ 88
            8.53   relative_dirname ............................ 88
            8.54   resolve ..................................... 89
            8.55   shell ....................................... 89
            8.56   sort_newest ................................. 90
            8.57   sort ........................................ 90
            8.58   split ....................................... 90
            8.59   stringset ................................... 91
            8.60   stripdot .................................... 91
            8.61   strip ....................................... 92
            8.62   substr ...................................... 92
            8.63   subst ....................................... 93
            8.64   suffix ...................................... 93
            8.65   tail ........................................ 94
            8.66   un-dos-path ................................. 94
            8.67   unsplit ..................................... 94
            8.68   upcase ...................................... 95
            8.69   uptodate .................................... 95
            8.70   wildcard .................................... 95
            8.71   word ........................................ 96
            8.72   words ....................................... 97
            8.73   write ....................................... 97

       9.   Predefined Variables ............................... 98
            9.1    arg ......................................... 98


       
                                    iii









            9.2    command-line-goals .......................... 98
            9.3    __FILE__ .................................... 98
            9.4    __FUNCTION__ ................................ 98
            9.5    graph_leaf_file ............................. 98
            9.6    graph_exterior_file ......................... 98
            9.7    graph_interior_file ......................... 98
            9.8    graph_leaf_pattern .......................... 98
            9.9    graph_exterior_pattern ...................... 99
            9.10   graph_interior_pattern ...................... 99
            9.11   __LINE__ .................................... 99
            9.12   need ........................................ 99
            9.13   parallel_hosts .............................. 99
            9.14   parallel_jobs ............................... 99
            9.15   parallel_rsh ................................ 99
            9.16   search_list ................................. 99
            9.17   self ....................................... 100
            9.18   target ..................................... 100
            9.19   targets .................................... 100
            9.20   thread-id .................................. 100
            9.21   younger .................................... 100
            9.22   version .................................... 100

       10.  Functions Library ................................. 101
            10.1   capitalize ................................. 101
            10.2   defined-or-null ............................ 101
            10.3   defined-or-default ......................... 101
            10.4   repeat ..................................... 101
            10.5   variable_by_path ........................... 102

       11.  Actions when Cooking .............................. 103
            11.1   Scan the COOK Environment Variable ......... 103
            11.2   Scan the Command Line ...................... 103
            11.3   Locate the Cookbook ........................ 103
            11.4   Form the Listing Filename .................. 103
            11.5   Create the Listing file .................... 103
            11.6   Scan the Cookbook .......................... 104
            11.7   Determine targets to cook .................. 104
            11.8   Cooking a Target ........................... 104
            11.9   The Dependency Graph ....................... 107
            11.10  File Status ................................ 108

       12.  Option Precedence ................................. 110

       13.  File name patterns ................................ 111
            13.1   Cook Patterns .............................. 111
            13.2   Regular Expressions ........................ 113

       14.  Supplied Cookbooks ................................ 116
            14.1   as ......................................... 116
            14.2   c .......................................... 117
            14.3   f77 ........................................ 118
            14.4   g77 ........................................ 119
            14.5   gcc ........................................ 119


       
                                    iv









            14.6   home ....................................... 119
            14.7   lex ........................................ 119
            14.8   library .................................... 120
            14.9   print ...................................... 121
            14.10  program .................................... 121
            14.11  rcs ........................................ 122
            14.12  recursive .................................. 123
            14.13  sccs ....................................... 123
            14.14  text ....................................... 123
            14.15  usr.local .................................. 124
            14.16  usr ........................................ 124
            14.17  yacc_many .................................. 125
            14.18  yacc ....................................... 125

       15.  Glossary .......................................... 126








































       
                                     v

































































                                    vi


