File: bakefile.5

package info (click to toggle)
bake 1.0-10
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 212 kB
  • ctags: 151
  • sloc: python: 895; sh: 289; ansic: 50; makefile: 41
file content (554 lines) | stat: -rw-r--r-- 17,830 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
.\"
.\" bakefile.5  --  bakefile manpage
.\"
.de Sx
.PP
.ne \\$1
.na
.nf
.RS 4
..
.de Ex
.RE
.fi
.ad
.PP
..
.de Sl
.RS 4
.nr tabpos \\$1
..
.de El
.RE
.PP
..
.de Il
.IP \\$1 \\n[tabpos]
..
.TH BAKEFILE "5" "September 2004" "bake 1.0" "Progammers Utility"
.SH NAME
bakefile \- Bake description file
.SH 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) assigments, further some small helpers.
.PP
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.
.PP
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.
.SH VARIABLES
A variable definition looks like this:
.Sx 1
Var( 'CC',     'gcc-3.3')
Var( 'CFLAGS', '-g -Wall')
.Ex
In shell commands and in prerequisite declarations, macros are
expanded when a `$' (dollar sign) occurs.  A given command
.Sx 1
\&'${CC} -o myprog myprog.o ${CFLAGS}'
.Ex
will expand to
.Sx 1
\&'gcc-3.3 -o myprog myprog.o -g -Wall'
.Ex
.PP
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 \fB-e\fP Option on the command line.
.PP
If a variable isn't defined at all, it is assumed to be the empty
string.
.SH RULES
.SS Static rules
To declare a rule, create an instance of class `Rule':
.Sx 4
Rule( File,
      'myprg',
      'myprg.o mymodule.o',
      'cc -o $@ $&')
.Ex
or
.Sx 4
Rule( cls     = File,
      name    = 'myprg',
      prereqs = 'myprg.o mymodule.o',
      cmds    = 'cc -o $@ $&')
.Ex
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.)
.PP
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:
.Sx 1
prereqs = [ 'myprg.o', 'mymodule.o']
.Ex
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.
.PP
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
.Sx 1
cmds = [ 'cd subdir', './do_sth']
.Ex
you probably meant one of:
.Sx 5
cmds = 'cd subdir ; ./do_sth'
cmds = 'cd subdir\\n./do_sth'
cmds = '''\\
  cd subdir
  ./do_sth'''
.Ex
The macros `$@' and `$&' expand to the target name and to the list of
dependencies.  See the section \fBMACROS\fP for details.
.PP
Commands may be Python functions.  Then they are just called.  Look at
section \fBCOMMAND FUNCTIONS\fP for a description of the parameters to
give.
.SS 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 \fBPhony\fP
targets were designed.  When a rule is declared to be a \fBPhony\fP
target, no file of that name is searched; the target and its
dependencies are just built allways.  An example:
.Sx 1
Rule( Phony, 'all', 'myprog myprog.1', None)
.Ex
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 neccessary.
.SS 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:
.Sx 1
Suffix( File, '.o', '.c', '${CC} -o $@ -c $<')
.Ex
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.
.PP
Suffix rules may depend from single files, too.  Put an equal sign
("=") in front of the file name.
.Sx 2
Suffix( File, '.dvi', '.tex =myfmt.fmt',
    'tex --fmt myfmt $<')
.Ex
Of course, this only makes sense if you have a rule or suffix rule to
build `myfmt.fmt'.
.PP
You can also use suffix rules to find automatically the dependencies
of header files.  See the section \fBPREREQUISITE DETECTION\fP for an
explanation.
.PP
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.
.SH MACROS
.SS Expansion
Macro expansion applies to that of Make.  Variable names longer than
one character have to be put into parenthesis or into braces.
.Sl 14
.Il $(VAR)
VAR expansion
.Il ${VAR}
VAR expansion (alternate form)
.Il $V
single character name
.Il $$
$ (dollar sign)
.El
.SS Order
Macros are expanded as late as possible.  That is, if you define in
your bakefile
.Sx 2
Var( 'PREFIX', '/usr/local')
Var( 'MANDIR', '${PREFIX}/man')
.Ex
the macro `${MANDIR}' normally will expand to `/usr/local/man'.  As
soon as you write
.Sx 1
$ bake PREFIX=/usr/share install
.Ex
the target `install' will be built with `${MANDIR}' expanding to
`/usr/share/man', as `MANDIR' itself actually still means
`${PREFIX}/man'.
.SS Built-in macros
The `$' Macros apply to those of Make as well, but there are some
additional ones.  Especially, `&' means \fIall\fP dependencies, not
only the newer ones.  There are as well macros yielding the number of
depedencies and of newer targets.  The full list is:
.Sl 8
.Il @
target name
.Il *
target basename (without extension)
.Il &
dependencies
.Il #
number of dependencies
.Il <
newer dependencies
.Il =
number of newer dependencies
.El
.SS Slicing
Variables may be sliced Python-like.  A colon-separated list will be
split, and there evaluate:
.Sl 14
.Il ${PATH[0]}
first directory in path
.Il ${PATH[-1]}
last directory in path
.Il ${PATH[1:]}
all directories in path but the first
.El
The built-in macros that are lists (dependencies and newer
dependencies) may be indexed as well:
.Sl 14
.Il ${&[0]}
the first prerequisite
.El
If you find a case where it makes sense to index newer dependencies,
please let me know.
.SH "COMMAND FUNCTIONS"
.SS Simple build functions
Commands may be specified as Python functions.  Then, they are called
directly.
.Sx 5
def buildhp( name, macros):
    import myhomepage
    myhomepage.buildit()

Rule( File, 'index.html', None, buildhp)
.Ex
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
.Sx 1
newer = macros( '<')
.Ex
See the \fBUTILITIES\fP section for further examples.  You will find
there some handy functions you probably now might think of.
.SS 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.
.Sx 4
def compressit( name, macros):
    Command( 'gzip -${SPEED} $@', macros).system()

Rule( File, 'myprog.1.gz', None, compressit)
.Ex
If the command is returning with an exit code other than 0, an
exception is raised.  You may catch and examine it.
.Sx 5
try:
    Command( 'diff -q $@ $@.prev', macros).system()
except ExitCode, e:
    if e.code == 1:
        print 'Different from previous.'
.Ex
To process the commands output call its `popen' function.
.Sx 1
headerdeps = Command( 'cpp -MM $@', macros).popen()
.Ex
(Don't do that; to resolve C header dependencies, there is provided
a special solution.  See the section below.)
.SH "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.
.Sx 9
def buildhp( name, macros):
    import myhomepage
    myhomepage.buildit()

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

Rule( File, 'index.html', sourceshp, buildhp)
.Ex
.SS 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.
.Sx 3
cpp = Cpp( 'incdir otherdir')

Suffix( File, 'c', cpp.findheaders)
.Ex
or, shortly,
.Sx 4
Suffix(
    File,
    '.c',
    Cpp( '${MYLIB}').findheaders)
.Ex
As you see, macros are expanded.
.SH "SUBORDINATE INSTANCES"
You might be temptated to specify a command like
.Sx 1
Rule( Phony, 'examples', '',
    cmds = 'cd examples ; bake all')
.Ex
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:
.Sx 4
def buildexamples( name, macros):
    Subbake().main( macros, '-Cexamples', 'all')

Rule( Phony, 'examples', '', buildexamples)
.Ex
You can use macros in the command line.  In the following example the
subdirectory is entered and the same target (`clean') is built there.
.Sx 4
def doexamples( name, macros):
    Subbake().main( macros, '-Cexamples', "$@")

Rule( Phony, 'clean', '', [ 'rm -fv *.o', doexamples])
.Ex
Of course, the string `"$@"' could have been written as `name'.
.SH "UTILITIES"
The C preprocessor header files detection mechanism is described in
the \fBPREREQUISITE DETECTION\fP section.  Here now are some other
handy tools for bakefile design.  If you want to propose or contribute
another one, please write me.
.SS 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.
.Sx 3
def builddocs( name, macros):
    docdir = ChDir( 'doc', macros)
    # do something in doc
.Ex
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.
.PP
The `macros' parameter may be omitted.
.SS Extensions
If you're building filenames by adding or splitting off extensions,
you may use the functions that Bake uses internally.
.Sx 3
path, name, ext = Ext.split( 'mydir/myfile.py')

full = Ext.join( 'path', 'to', 'prog', 'c')
.Ex
In the join function, the last two arguments are supposed to be name
and extension, the preceding ones are used to build a path.
.SS Line joining
Joining lines may be done with a very simple function call.  Just type
.Sx 1
cmds = joinlines( [ 'rm -f *~', 'rm -f \\\\#*#'])
.Ex
to build one multiline string.  This function claims to be understood
as the opposite of strings `splitlines'.
.SS 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.
.Sx 6
def archive( name, macros):
    a = Archive()
    a.targzall()
    a.zipall()

Rule( Phony, 'archive', 'wipe', archive)
.Ex
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'.
.PP
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'.
.Sx 3
def archive( name, macros):
    a = Archive( prefix = False, fakeroot = True)
    a.targzall( to = '~/save')
.Ex
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.
.PP
The archiver may be told to give the tar or zip file another name in
two ways.
.Sx 2
a = Archive( 'savedwork')
a = Archive( name = 'savedwork')
.Ex
or
.Sx 2
a = Archive()
a.name += time.strftime( '-%Y-%m-%d')
.Ex
In the second example, the name of the archive that will be built may
be `myproj-1.7-2004-09-23.tar.gz'.
.PP
You may create both a tar and a zip archive at one time calling the
`bothall' function.
.Sx 3
def archive( name, macros):
    a = Archive()
    a.bothall()
.Ex
.SS 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.
.Sx 11
class MyProjInst( Installer):

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

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

mi = MyProjInst()
mi.stdrules()
.Ex
The `stdrules' function defines five rules as if you would have
written
.Sx 5
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)
.Ex
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.
.PP
The `package' target doesn't install any file at all; it builds a
directory named `package' (the target name) containing the files that
are neccessary for building a package.  You don't need to be root, all
users and groups with id >= 500 are changed to root.
.PP
Further, you may specify installation scripts to be executed before or
after installation/removal.
.Sx 6
class MyProjInst( Installer):

    # ...

    def postinst( self):
        return 'python compileall.py ${MODDIR}'
.Ex
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.
.SH "COMMON TRICKS"
.SS Password entering
As dependent targets are built in the order they're specified, here
the password will be requested before the homepage is built.
.Sx 19
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)
.Ex
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.
.SS 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.
.Sx 6
def removedeps( name, macros):
    import genhp
    genhp.removeall()

Rule( Phony, 'clean', [
    removedeps, 'rm -f *.pyc'])
.Ex
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.
.SH "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.
.Sx 1
Var( 'CC', find_cc())
.Ex
Here, `find_cc' should be a function previously defined, returning a
string containing the systems preferred C compiler.
.PP
A more complex implementation reflecting the operating system your
project is compiled on would be:
.Sx 6
def createosh( name, macros):
    f = open( 'osdef.h', 'w')
    if sys.platform == 'win32':
        print >>f, '#define THANK_YOU_FREDDIE'

Rule( File, 'osdef.h', '', createosh)
.Ex
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.
.SH "SEE ALSO"
\fBbake\fP(1)
.PP
\fIPython Documentation\fP: http://www.python.org/doc/
.SH AUTHOR
(C) 2004 Bertram Scharpf <software@bertram-scharpf.de>