Man Linux: Main Page and Category List

NAME

       bakefile - Bake description file

DESCRIPTION

       In  analogy  to  Make,  a bakefile is the standard description file for
       Bake.   Bakefiles  are  plain  Python  source  code,  included  by   an
       ‘execfile’  function  call.   There  are  defined  several  classes and
       functions before a bakefile is entered; those are  mainly  build  rules
       and variable (macro) assignments, further some small helpers.

       You  declare rules, suffix rules or variable assignments by creating an
       instance of a class ‘Rule’, ‘Suffix’  or  ‘Var’.   The  object  may  be
       forgotten  by  your  code;  Bake  will  still  keep  it  if once it was
       constructed.

       The declarations may look completely different as  the  makefiles  your
       are  used to.  But the way they work is as close as possible to that of
       Make.

VARIABLES

       A variable definition looks like this:

           Var( ’CC’,     ’gcc-3.3’)
           Var( ’CFLAGS’, ’-g -Wall’)

       In shell commands and in prerequisite declarations, macros are expanded
       when a ‘$’ (dollar sign) occurs.  A given command

           ’${CC} -o myprog myprog.o ${CFLAGS}’

       will expand to

           ’gcc-3.3 -o myprog myprog.o -g -Wall’

       Lowest  precedence  have the environment variables.  If a Bake variable
       is declared, this overrides the environment.  If a variable is assigned
       to  on  the  command  line,  variable  assignments in the previous read
       bakefile are overridden.  The behaviour of environment variables may be
       changed using the -e Option on the command line.

       If  a  variable  isn’t  defined  at  all, it is assumed to be the empty
       string.

RULES

   Static rules
       To declare a rule, create an instance of class ‘Rule’:

           Rule( File,
                 ’myprg’,
                 ’myprg.o mymodule.o’,
                 ’cc -o $@ $&’)

       or

           Rule( cls     = File,
                 name    = ’myprg’,
                 prereqs = ’myprg.o mymodule.o’,
                 cmds    = ’cc -o $@ $&’)

       The first argument, ‘File’ means that a file will be produced  and  its
       time  stamp  has to be checked.  (Phony targets will be described later
       in this section.)

       The second and the third argument correspond to  the  rule  declaration
       line  of Make.  The second is what comes before the colon and the third
       what comes after.  The rule name is the name of the target file  to  be
       built.   The  prerequisites  are  the  files  the target is built from.
       Here, this is a string with file names separated by spaces; it is  also
       allowed to give a Python list:

           prereqs = [ ’myprg.o’, ’mymodule.o’]

       The  prerequisites  are looked up if they match build rules themselves.
       If so, these targets are built first.  The dependent targets are  built
       in the same order as they are specified in the ‘prereqs’ argument.

       The  last  argument  is a command or a list of commands to be executed.
       When a list is given, every command runs in its own  shell.   That  is,
       when you say

           cmds = [ ’cd subdir’, ’./do_sth’]

       you probably meant one of:

           cmds = ’cd subdir ; ./do_sth’
           cmds = ’cd subdir\n./do_sth’
           cmds = ’’’\
             cd subdir
             ./do_sth’’’

       The  macros  ‘$@’ and ‘$&’ expand to the target name and to the list of
       dependencies.  See the section MACROS for details.

       Commands may be Python functions.  Then they are just called.  Look  at
       section  COMMAND FUNCTIONS for a description of the parameters to give.

   Phony targets
       Sometimes not actually a program or document has  to  be  created,  but
       just a couple of jobs have to be put together.  Therefore Phony targets
       were designed.  When a rule is declared to be a Phony target,  no  file
       of  that  name  is  searched;  the target and its dependencies are just
       built allways.  An example:

           Rule( Phony, ’all’, ’myprog myprog.1’, None)

       Given ‘None’ as command means that nothing will be  executed  for  this
       target itself, but both the program ‘myprog’ and its manpage ‘myprog.1’
       are checked, and built if necessary.

   Suffix rules
       Rules cannot only be defined for a singe file, but as well for a  whole
       class, specified by an extension.  So you can say:

           Suffix( File, ’.o’, ’.c’, ’${CC} -o $@ -c $<’)

       Whenever  a  file  with  extension  ‘.o’ is to be built and there is no
       explicit rule, the extension is replaced by ‘.c’  and  the  command  is
       executed with the appropriate macro expansions.

       Suffix  rules  may  depend  from  single files, too.  Put an equal sign
       ("=") in front of the file name.

           Suffix( File, ’.dvi’, ’.tex =myfmt.fmt’,
               ’tex --fmt myfmt $<’)

       Of course, this only makes sense if you have a rule or suffix  rule  to
       build ‘myfmt.fmt’.

       You can also use suffix rules to find automatically the dependencies of
       header  files.   See  the  section  PREREQUISITE   DETECTION   for   an
       explanation.

       The  initial dots in ‘.o’ and ‘.c’ are optional and may be omitted.  If
       a single dot (‘.’) is specified for a prerequisite file built from  the
       target  name stripped off its extension, this dot doesn’t appear in the
       filename composed.  You may even specify  a  single  dot  in  order  to
       indicate  there  is  a prerequise at all and it’s still stripped.  Give
       ‘None’ if you don’t  want  any  prerequise  to  be  present.   This  is
       admittedly a litte bit paradox.

MACROS

   Expansion
       Macro  expansion  applies  to that of Make.  Variable names longer than
       one character have to be put into parenthesis or into braces.

           $(VAR)        VAR expansion

           ${VAR}        VAR expansion (alternate form)

           $V            single character name

           $$            $ (dollar sign)

   Order
       Macros are expanded as late as possible.  That is,  if  you  define  in
       your bakefile

           Var( ’PREFIX’, ’/usr/local’)
           Var( ’MANDIR’, ’${PREFIX}/man’)

       the  macro  ‘${MANDIR}’  normally  will expand to ‘/usr/local/man’.  As
       soon as you write

           $ bake PREFIX=/usr/share install

       the target ‘install’  will  be  built  with  ‘${MANDIR}’  expanding  to
       ‘/usr/share/man’,    as    ‘MANDIR’   itself   actually   still   means
       ‘${PREFIX}/man’.

   Built-in macros
       The ‘$’ Macros apply to those of Make  as  well,  but  there  are  some
       additional  ones.  Especially, ‘&’ means all dependencies, not only the
       newer  ones.   There  are  as  well  macros  yielding  the  number   of
       dependencies and of newer targets.  The full list is:

           @       target name

           *       target basename (without extension)

           &       dependencies

           #       number of dependencies

           <       newer dependencies

           =       number of newer dependencies

   Slicing
       Variables  may  be  sliced Python-like.  A colon-separated list will be
       split, and there evaluate:

           ${PATH[0]}    first directory in path

           ${PATH[-1]}   last directory in path

           ${PATH[1:]}   all directories in path but the first

       The  built-in  macros  that   are   lists   (dependencies   and   newer
       dependencies) may be indexed as well:

           ${&[0]}       the first prerequisite

       If  you  find  a case where it makes sense to index newer dependencies,
       please let me know.

COMMAND FUNCTIONS

   Simple build functions
       Commands may be specified as Python functions.  Then, they  are  called
       directly.

           def buildhp( name, macros):
               import myhomepage
               myhomepage.buildit()

           Rule( File, ’index.html’, None, buildhp)

       The  function  handed over has to take two arguments.  The first is the
       name of the target to be built.  The second is a function returning the
       macro  expansions.   For  example,  if  you  want  to  find  the  newer
       dependencies, you have to call

           newer = macros( ’<’)

       See the UTILITIES section for further examples.  You  will  find  there
       some handy functions you probably now might think of.

   Calling shell commands
       If  your  function  will call shell commands itself, you should use the
       same interface as is used internally.  Then macro expansion  will  take
       place  and  the  no-act option as well as the verbositiy levels will be
       redeemed.  You should  hand  over  the  macro  expansion  function  you
       retrieved from Bake.

           def compressit( name, macros):
               Command( ’gzip -${SPEED} $@’, macros).system()

           Rule( File, ’myprog.1.gz’, None, compressit)

       If  the  command  is  returning  with  an  exit  code  other than 0, an
       exception is raised.  You may catch and examine it.

           try:
               Command( ’diff -q $@ $@.prev’, macros).system()
           except ExitCode, e:
               if e.code == 1:
                   print ’Different from previous.’

       To process the commands output call its ‘popen’ function.

           headerdeps = Command( ’cpp -MM $@’, macros).popen()

       (Don’t do that; to resolve C header dependencies, there is  provided  a
       special solution.  See the section below.)

PREREQUISITE DETECTION

       Like commands may be Python functions, the prerequisites field may be a
       function, too.  If it is,  the  dependencies  will  be  the  list  this
       function  returns.   The function has to take the same arguments as the
       build command functions.

           def buildhp( name, macros):
               import myhomepage
               myhomepage.buildit()

           def sourceshp( name, macros):
               import myhomepage
               return myhomepage.findsources()

           Rule( File, ’index.html’, sourceshp, buildhp)

   C preprocessing
       You don’t need to worry about how  to  process  the  output  of  the  C
       preprocessors  ‘-MM’  option.   Bake defines a class that does this for
       you.  You just have to specify your include  directories  (‘cpp’  needs
       them)  and  hand over the objects ‘findheaders’ bound method.  As there
       is no compile command to execute at this state, the  commands  argument
       is omitted.

           cpp = Cpp( ’incdir otherdir’)

           Suffix( File, ’c’, cpp.findheaders)

       or, shortly,

           Suffix(
               File,
               ’.c’,
               Cpp( ’${MYLIB}’).findheaders)

       As you see, macros are expanded.

SUBORDINATE INSTANCES

       You might be temptated to specify a command like

           Rule( Phony, ’examples’, ’’,
               cmds = ’cd examples ; bake all’)

       Although  this  will  work,  there is a better way to do it.  You don’t
       need to load the interpreter twice.  There is a  class  ‘Subbake’  that
       preserves  the initial state and recovers it after the job is finished.
       The command function might look like this:

           def buildexamples( name, macros):
               Subbake().main( macros, ’-Cexamples’, ’all’)

           Rule( Phony, ’examples’, ’’, buildexamples)

       You can use macros in the command line.  In the following  example  the
       subdirectory is entered and the same target (‘clean’) is built there.

           def doexamples( name, macros):
               Subbake().main( macros, ’-Cexamples’, "$@")

           Rule( Phony, ’clean’, ’’, [ ’rm -fv *.o’, doexamples])

       Of course, the string ‘"$@"’ could have been written as ‘name’.

UTILITIES

       The C preprocessor header files detection mechanism is described in the
       PREREQUISITE DETECTION section.  Here now are some  other  handy  tools
       for bakefile design.  If you want to propose or contribute another one,
       please write me.

   Changing the Directory
       If you temporarily want to change to another working directory  and  if
       you want to be sure to get back in any case, use the ‘ChDir’ class.  It
       use is very simple.

           def builddocs( name, macros):
               docdir = ChDir( ’doc’, macros)
               # do something in doc

       When the function is left, the ‘docdir’ instance is  destroyed.   Then,
       it will automatically change to the directory where you were before the
       object was instantiated.

       The ‘macros’ parameter may be omitted.

   Extensions
       If you’re building filenames by adding or splitting off extensions, you
       may use the functions that Bake uses internally.

           path, name, ext = Ext.split( ’mydir/myfile.py’)

           full = Ext.join( ’path’, ’to’, ’prog’, ’c’)

       In  the  join  function, the last two arguments are supposed to be name
       and extension, the preceding ones are used to build a path.

   Line joining
       Joining lines may be done with a very simple function call.  Just type

           cmds = joinlines( [ ’rm -f *~’, ’rm -f \\#*#’])

       to build one multiline string.  This function claims to  be  understood
       as the opposite of strings ‘splitlines’.

   Archiving
       Bake provides a way to convieniently archive your today work.  It packs
       all contents of a directory including all subdirectories into a tar  or
       into a zip archive.

           def archive( name, macros):
               a = Archive()
               a.targzall()
               a.zipall()

           Rule( Phony, ’archive’, ’wipe’, archive)

       This  builds both a tar and a zip archive in the parent diectory of the
       current.  It is given the same name as the current directory, added the
       extensions  ‘.tar.gz’  and  ‘.zip’  respectively.   For example, if the
       directory you’re in is ‘/home/user/myproj-1.7’, your tar  archive  file
       will be ‘/home/user/myproj-1.7.tar.gz’.

       Of course, you may specify some options.  By default, the file names in
       the archive are prefixed with the directories name.   A  file  ‘main.c’
       will   appear  as  ‘myproj-1.7/main.c’.   You  may  turn  off  this  by
       specifying ‘prefix = False’.

           def archive( name, macros):
               a = Archive( prefix = False, fakeroot = True)
               a.targzall( to = ’~/save’)

       In this example, further the files in the tar archive  will  have  user
       and  group  owner  ‘root’.   The  archive  won’t be saved in the parent
       directory but in the users preferred archiving  directory  ‘save’  that
       will  be created if it doesn’t exist.  If ‘to’ is not an absolute path,
       it will be meant relatively to the current parent directory.

       The archiver may be told to give the tar or zip file  another  name  in
       two ways.

           a = Archive( ’savedwork’)
           a = Archive( name = ’savedwork’)

       or

           a = Archive()
           a.name += time.strftime( ’-%Y-%m-%d’)

       In  the  second example, the name of the archive that will be built may
       be ‘myproj-1.7-2004-09-23.tar.gz’.

       You may create both a tar and a zip archive at  one  time  calling  the
       ‘bothall’ function.

           def archive( name, macros):
               a = Archive()
               a.bothall()

   Installing
       Instead  of  writing  long  installation  routines  you  may use Bake’s
       installer feature.  Just define a class that returns what its files  to
       install are.

           class MyProjInst( Installer):

               def execfiles( self):
                   return { ’${BINDIR}’: ’myproj’,
                            ’${MANDIR}/man1’: ’myproj.1’}

               def conffiles( self):
                   return { ’/etc’: ’myprojrc’}

           mi = MyProjInst()
           mi.stdrules()

       The ‘stdrules’ function defines five rules as if you would have written

           Rule( Phony, ’check’,   self.prereqs, self.check)
           Rule( Phony, ’install’, self.prereqs, self.install)
           Rule( Phony, ’remove’,  self.prereqs, self.remove)
           Rule( Phony, ’purge’,   self.prereqs, self.purge)
           Rule( Phony, ’package’, self.prereqs, self.package)

       The difference between ‘remove’ and ‘purge’ is that in the latter  case
       the configuration files are deleted, too.  The ‘install’ target doesn’t
       overwrite existing config files but  creates  ones  with  an  extension
       ‘.bake-new’ instead.

       The  ‘package’  target  doesn’t  install  any  file at all; it builds a
       directory named ‘package’ (the target name) containing the  files  that
       are  necessary  for building a package.  You don’t need to be root, all
       users and groups with id >= 500 are changed to root.

       Further, you may specify installation scripts to be executed before  or
       after installation/removal.

           class MyProjInst( Installer):

               # ...

               def postinst( self):
                   return ’python compileall.py ${MODDIR}’

       This  way, you may specify the functions ‘preinst’, ‘postinst’, ‘prerm’
       and ‘postrm’ resulting in a script  being  called  at  the  appropriate
       time.   If you create a package, the scripts are written to the package
       directory and they are given the same names as the functions.

COMMON TRICKS

   Password entering
       As dependent targets are built in the order they’re specified, here the
       password will be requested before the homepage is built.

           site = ’ftp.nny.xx’
           user = ’fry’
           passwd = None

           def getpasswd( name, macros):
               import getpass
               global passwd
               passwd = getpass.getpass(
                   ’Password for %s on %s:’ % (user, site))

           def upload( name, macros):
               import ftplib
               f = ftplib.FTP( site)
               f.login( user, passwd)
               # ...
               f.quit()

           Rule( Phony, ’passwd’, ’’, getpasswd)
           Rule( Phony, ’upload’, ’passwd index.html’, upload)

       This  makes  it convenient to start an upload procedure.  You may first
       enter the password instead of waiting until the homepage is  built  and
       then  entering  it  so that the process can continue with uploading the
       files.

   Caution with cleanup
       Be sure you do not build new files during a cleanup.  For  example,  in
       the following code the order in that the commands are specified makes a
       slight difference.

           def removedeps( name, macros):
               import genhp
               genhp.removeall()

           Rule( Phony, ’clean’, [
               removedeps, ’rm -f *.pyc’])

       As the function ‘removedeps’ imports a module, it will produce  a  file
       ‘genhp.pyc’.   This  is removed by the second command.  If the commands
       were given in reverse  order,  the  temporary  file  ‘genhp.pyc’  would
       survive the procedure.

CONFIGURATION SCRIPTS

       Bake  doesn’t  need  to  be  preceded  by  configuration  scripts.   As
       bakefiles are Python code, you may specify  detection  functions.   You
       are  encouraged  to  put them at the end of the bakefile, but you don’t
       need to do so.

           Var( ’CC’, find_cc())

       Here, ‘find_cc’ should be a function previously  defined,  returning  a
       string containing the systems preferred C compiler.

       A  more  complex  implementation  reflecting  the operating system your
       project is compiled on would be:

           def createosh( name, macros):
               f = open( ’osdef.h’, ’w’)
               if sys.platform == ’win32’:
                   print >>f, ’#define THANK_YOU_FREDDIE’

           Rule( File, ’osdef.h’, ’’, createosh)

       A header file will only be created if it doesn’t exist.  As it is  part
       of  a  rule and it will be built on any request of ‘osdef.h’, it should
       be mentioned within a ‘clean’ or ‘wipe’ target.

SEE ALSO

       bake(1)

       Python Documentation: http://www.python.org/doc/

AUTHOR

       (C) 2004 Bertram Scharpf <software@bertram-scharpf.de>