File: large.so

package info (click to toggle)
cook 2.5-1
  • links: PTS
  • area: main
  • in suites: slink
  • size: 5,860 kB
  • ctags: 3,247
  • sloc: ansic: 41,260; sh: 10,022; yacc: 3,397; makefile: 3,244; awk: 136
file content (701 lines) | stat: -rw-r--r-- 23,474 bytes parent folder | download
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
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
.\"
.\"	cook - file construction tool
.\"	Copyright (C) 1998 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.
.\"
.\" MANIFEST: User Guide, Building Large Projects
.\"
.H 1 "Building Large Projects"
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.
.P
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.
.H 2 "Whole Project Build"
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.
.P
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 tree\*F.
.FS
If you ever want to use Aegis for configuration management,
this is what Aegis expects.
.FE
This works well the the \fIsearch_list\fP functionality, mentioned below,
which simplifies the structure of private work areas.
.H 3 "Project Directory Structure"
In the examples use in this chapter,
the following directory structure is assumed:
.PS c
dir_factor = 0.6

folder_height = 0.16 * dir_factor
folder_width  = 0.25 * dir_factor
folder_miter  = 0.03 * dir_factor

define folder {
B: box invis wid folder_width ht folder_height
line from B.sw \
	then to B.nw-(0,folder_miter) \
	then to B.nw+(folder_miter,0) \
	then to B.n-(folder_miter/2,0) \
	then to B.n+(folder_miter/2,-folder_miter) \
	then to B.ne-(0,folder_miter) \
	then to B.se \
	then to B.sw
}

document_height = 0.22 * dir_factor
document_width  = 0.16 * dir_factor
document_miter  = 0.05 * dir_factor

define document {
B: box invis wid document_width ht document_height
line from B.sw \
	then to B.nw \
	then to B.ne-(document_miter,0) \
	then to B.ne-(0,document_miter) \
	then to B.se \
	then to B.sw
line from B.ne-(document_miter,0) \
	then to B.ne-(document_miter,document_miter) \
	then to B.ne-(0,document_miter)
}

dir_wid = 0.35 * dir_factor
dir_ht = 0.26 * dir_factor

boxwid = dir_wid
boxht = dir_ht
A: box invis
   [ folder ] with .c at A.c
   "\fIProject\fP" ljust at A.e

   line from A.s then down boxht/2 then right boxwid/2
B: box invis
   [ document ] with .c at B.c
   "\f(CWHowto.cook\fP" ljust at B.e

   line from A.s then down 3*boxht/2 then right boxwid/2
C: box invis
   [ folder ] with .c at C.c
   "\f(CWlibrary\fP" ljust at C.e

   line from C.s then down boxht/2 then right boxwid/2
C1: box invis
   [ document ] with .c at C1.c
   "\fIsource1\fP\f(CW.c\fP" ljust at C1.e

   line from C.s then down 3*boxht/2 then right boxwid/2
C2: box invis
   [ document ] with .c at C2.c
   "\fIsource2\fP\f(CW.c\fP" ljust at C2.e

   line from C.s then down 5*boxht/2 then right boxwid/2
C3: box invis
   [ document ] with .c at C3.c
   "\fIetc...\fP" ljust at C3.e

   line from A.s then down 11*boxht/2 then right boxwid/2
I: box invis
   [ folder ] with .c at I.c
   "\f(CWinclude\fP" ljust at I.e

   line from I.s then down boxht/2 then right boxwid/2
I1: box invis
   [ document ] with .c at I1.c
   "\fIapi1\fP\f(CW.h\fP" ljust at I1.e

   line from I.s then down 3*boxht/2 then right boxwid/2
I2: box invis
   [ document ] with .c at I2.c
   "\fIapi2\fP\f(CW.h\fP" ljust at I2.e

   line from I.s then down 5*boxht/2 then right boxwid/2
I3: box invis
   [ document ] with .c at I3.c
   "\fIetc...\fP" ljust at I3.e

   line from A.s then down 19*boxht/2 then right boxwid/2
D: box invis
   [ folder ] with .c at D.c
   "\fIprogram1\fP" ljust at D.e

   line from D.s then down boxht/2 then right boxwid/2
D1: box invis
   [ document ] with .c at D1.c
   "\fIsource3\fP\f(CW.c\fP" ljust at D1.e

   line from D.s then down 3*boxht/2 then right boxwid/2
D2: box invis
   [ document ] with .c at D2.c
   "\fIsource4\fP\f(CW.c\fP" ljust at D2.e

   line from D.s then down 5*boxht/2 then right boxwid/2
D3: box invis
   [ document ] with .c at D3.c
   "\fIetc...\fP" ljust at D3.e

   line from A.s then down 27*boxht/2 then right boxwid/2
E: box invis
   [ folder ] with .c at E.c
   "\fIprogram2\fP" ljust at E.e

   line from E.s then down boxht/2 then right boxwid/2
E1: box invis
   [ document ] with .c at E1.c
   "\fIsource5\fP\f(CW.c\fP" ljust at E1.e

   line from E.s then down 3*boxht/2 then right boxwid/2
E2: box invis
   [ document ] with .c at E2.c
   "\fIsource6\fP\f(CW.c\fP" ljust at E2.e

   line from E.s then down 5*boxht/2 then right boxwid/2
E3: box invis
   [ document ] with .c at E3.c
   "\fIetc...\fP" ljust at E3.e

.\" Make sure the width of the text is taken into account when the
.\" picture is centered within the column.
box invis wid 1 with .w at E3.e
.PE
Below the project directory is a \f(CWlibrary\fP 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.
.P
Next to the \f(CWlibrary\fP directiry is the \f(CWinclude\fP directory.
This describes interfaces and data shared by the project.  Information
which is proivate to the internals of the library or a programs belongs
there, not in the shared include space.
.P
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.
.P
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 \f(CWlibrary\fP directory, or all of your programs may
be below a single \f(CWcommand\fP directory.  With simple changes to
the examples given in this chapter, you will be able to cope with just
about any project structure.
.H 3 "File Manifest"
There are many ways of discovering the source files you are working with.
Many configuration management systems are able to give you a liost of them.
For example, if you were using Aegis, you would say
.eB
change_files =
	[collect aegis -l cf -terse -p [project] -c [change]];
project_files =
	[collect aegis -l pf -terse -p [project] -c [change]];
manifest =
	[sort [change_files] [project_files]];
.eE
.P
If you were using RCS, you could find all the the RCS files, and
reconstruct the original filenames from them, \fIviz:\fP
.eB
manifest =
	[fromto ./%0RCS/%,v %0%
		[collect find .  -path "*/RCS/*,v" -print]
	];
.eE
.P
Or you could simply scan the directory tree:
.eB
manifest =
	[fromto ./%0% %0%
		[collect find .  ! -type d -print]
	];
.eE
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.
.H 3 "Compiling C Sources"
Recalling the 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.
.P
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 behaviour.
.P
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.
.P
So, when cooking C sources, you need recipes of the form
.eB
cc = gcc;
cc_flags = -g -Wall -O;

%0%.o: %0%.c
{
	[cc] [cc_flags] -c %0%.c
		-o [target];
}
.eE
The ``\f(CW%0\fP'' part of the patterns matches zero or more directory parts.
If your compiler insists on putting the output (\f(CW.o\fP) file into
the current directory (the top level one) you will need to move it, after:
.eB
%0%.o: %0%.c
{
	[cc] [cc_flags] -c %0%.c;
	mv %.o [target];
}
.eE
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
.eB
%1/%0%.o: %1/%0%.c
{
	[cc] [cc_flags] -I%1 -c %0%.c
		-o [target];
}
.eE
Or by using the dirname of the source file
.eB
%0%.o: %0%.c
{
	[cc] [cc_flags] -I[dirname %0%.c] -c %0%.c
		-o [target];
}
.eE
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 hieracy directly in the directory hierachy.
.P
The common include file will also need to be searched.  Because of where
the command is issued, it is rather simple to add the \f(CWinclude\fP
directory, \fIviz:\fP
.eB
%0%.o: %0%.c
{
	[cc] [cc_flags]
		-I[dirname %0%.c] -Iinclude
		-c %0%.c -o [target];
}
.eE
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 \f(CWcc-flags\fP used, based on the target file,
or even the directory conating the file,
without compromising the generality of the recipe\*F.
.FS
Hint: use a function, and pass \f(CW[target]\fP as the argument.
.FE
.H 3 "Tracking Include Dependencies"
When it comes to tracking include dependencies using \fIc_incl\fP,
you need to remember, again, that the Cook happens from a single place.
All of the recipes that \fIc_incl\fP writes for you must be \fIrelative
to that place\fP.
.P
Continuing our example, and assuming we are using the cascase include
method described in the previous chapter, we need include dependency
files which look similar to
.eB
cascade \fIprogram1\fP/\fIsource3\fP.c =
include/\fIapi1\fP.h
;
.eE
Working backwards, we need to create the dependency file using
the following recipe:
.eB
%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];
}
.eE
For other source languages, you will need to
use the \fIc_incl --language\fP option.
.P
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.
.eB
dep-files =
	[addsuffix .d
		[match_mask %0%.c [manifest] ]
		[match_mask %0%.h [manifest] ]
	];
#include-cooked [dep-files]
.eE
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 taks to read the cookbook.
.P
Notice that adding a new source file will automaticaly cause it to be
scanned for include dependencies, without modifucation to the cookbook.
.H 3 "Linking Libraries"
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:
.eB
%/lib%.a: [[target]_obj]
	set unlink
{
	ar cq [target] [[target]_obj];
}
.eE
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...
.eB
library/liblibrary.a_obj =
	[fromto %0%.c %0%.o
		[match_mask "library/%0%.c" [manifest] ]
	];
.eE
The great thing about this construct is that you can build a loop,
using Cook's loop statement, that assignes a variable for each of your
libraries, if you have more than one.
.P
Notice that adding a new library source file will automaticaly cause it to be
compiled into the library, without modifucation to the cookbook.
.H 3 "Linking Commands"
We'll use a similar trick for each of the programs you want to link...
First the link line
.eB
bin/%: [[target]_obj]
	set mkdir
{
	[cc] -o [target] [[target]_obj];
}
.eE
Then the objects variable.  Note how we add a library \fIfilename\fP
here, this will still only use the library portions actually referenced,
not the whole library, so it won't bloat your programs.
.eB
bin/\fIprogram\fP_obj =
	[fromto %0%.c %0%.o
		[match_mask \fIprogram\fP/%0%.c [manifest] ]
	]
	library/liblibrary.a
	;
.eE
.P
Notice that adding a new program source file will automaticaly cause it to be
compiled and linked into the program, without modification to the cookbook.
.P
The loop construct tends to obscure things, which is why the essential
assignment was given first.
This next fragment shows the whole loop.
.eB
programs =
	[fromto %/main.c %
		[match_mask %/main.c [manifest] ]
	];
.eE
.eB
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]
			]
		]
		library/liblibrary.a
		;
}
.eE
And now tell Cook you actually want it to do something,
like build all of the programs...
.eB
all: [addprefix bin/ [commands]];
.eE
.P
Notice they way the \f(CWcommands\fP variable is constructed: just adding
a new command (and its \f(CWmain.c\fP file) will automatically cause it
to be built, without modification to the cookbook.

.H 2 "Private Work Areas"
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.
.H 3 "Directory Structure"
The method suggested here uses Cook's \fIsearch_list\fP 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.
.PS
golden = (1+sqrt(5))/2
boxht = 0.7
boxwid = boxht * golden
B1: box "\fIRepository\fP" "\f(CWmain.c\fP" "\f(CWpart1.c\fP" "" fill 0.05
B2: box "\fIWork Area\fP" "\f(CWmain.c\fP" "" "\f(CWpart2.c\fP" \
	fill 0 with .nw at B1.nw-(0.5,boxht*0.8)
B3: box "\fICombined View\fP" "\f(CWmain.c\fP" "\f(CWpart1.c\fP" \
	"\f(CWpart2.c\fP" with .w at 1/2<B1.e,B2.e>+(1,0)
arrow from B1.e to 1/3<B3.nw,B3.sw>
arrow from B2.e to 2/3<B3.nw,B3.sw>
line dashed from B1.ne to B2.ne
line dashed from B1.nw to B2.nw
line dashed from B1.se to B2.se
.PE
When recipes are run, the results are written into the work area,
which means that the repository can be completely read-only.
.P
It follows from this, that the directory structure of the work
area exactly parallels the directory structure of the repository.
\fIExcept\fP you only check out files into your work area that you
actually need to change.
.H 3 "Finding the Cookbook"
Setting the search list is done with a simple assignment.
In your work area, create a simple \f(CWHowto.cook\fP file,
containing only 3 lines:
.eB
set mkdir;
search_list = . /project/repository ;
#include /project/repository/Howto.cook
.eE
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 (\f(CWmain.cook\fP), and changing
\f(CWHowto.cook\fP to always read
.eB
set mkdir;
search_list = . /project/repository ;
#include [resolve main.cook]
.eE
The \f(CW[resolve]\fP function walks the search list, looking for
the file\*F.
.FS
The search list defaults to just dot (the current directory) if not set.
.FE
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.
.P
The unexplained \f(CWmkdir\fP flag is used to request that directories
be automatically created before recipe bodies are run.  This may be
necessary, for example, if a \f(CW.c\fP file in the repository needs to
be recompiled because a \f(CW.h\fP file in the work area has been changed.
.H 3 "File Manifest"
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.
.eB
manifest = [collect find [search_list] ! -type d -print] ;
dirs = [search_list]
loop
{
	dir = [head [dirs]];
	if [not [count [dir]]] then
		loopstop;
	dirs = [tail [dirs]];

	manifest = [fromto [dir]/%0% %0% [manifest]];
}
manifest = [stringset [manifest]]; /* remove duplicates */
.eE
This is messier than it looks.  It copes with an arbitrarily long
search list.  If you can guarantee there are only two, unroll the loop.
You still need to remove the duplicates.
.H 3 "Compiling C Sources"
The C compilation recipe needs to be changed to read...
.eB
%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];
}
.eE
This ensures that the rights places are searched for
include files.
.H 3 "Tracking Include Dependencies"
A similar change needs to be made to the include dependencies recipe...
.eB
%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];
}
.eE
Note that the form of the output of this recipe \fIdoes not\fP 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.
.H 3 "Linking Libraries"
The library recipe needs few modifications.
.eB
%/lib%.a: [[target]_obj]
	set unlink
{
	ar cq [target] [resolve [[target]_obj]];
}
.eE
The variable assignment given above requires no modifications.
.H 3 "Linking Commands"
The command linking recipe requires few modifications.
.eB
bin/%: [[target]_obj]
	set mkdir
{
	[cc] -o [target] [resolve [[target]_obj]];
}
.eE
The variable assignment needs no modifications.
.H 2 "Whole Project Build Advantages"
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.
.P
It greatly simplifies the creating of work areas for developers,
by using Cook's \fIsearch_list\fP functionality.
.P
A whole project build also permits the \fIcook -continue\fP option to
work in the presence of a wider range of errors.
.P
The whole project build also permits the \fIcook -parallel\fP option to
parallelize more operations.
.H 2 "Heterogenous Build"
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.
.P
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 (\f(CWcc\fP,
\f(CWas\fP, \f(CWld\fP, \fIetc\fP) 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 \f(CWgcc\fP command.  This example will exploit this normal
installation practice.
.H 3 "Cross Compiling C Sources"
In order to support cross compiling,
the C compilation recipe needs to be changed to read...
.eB
%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];
}
.eE
This uses the first directory element of the \fItarget\fP to be the
architecture name.  This allows multiple architectures to be compiled
in the same source tree, simultaneously.
.P
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.
.P
The \fIhost-binding\fP 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 \f(CWdefined-or-null\fP function is available in the ``functions''
library distributed with Cook.)
.P
Remembering these architectures follow the GNU convention,
these lines could read
.eB
i386-linux-hosts = fast faster fastest ;
.eE
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 whn you use the \f(CW--parallel\fP option.
.H 3 "Tracking Include Dependencies"
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 furture.
.H 3 "Linking Libraries"
The library recipe needs few modifications.
.eB
%1/%/lib%.a: [%/lib%.a_obj]
	set unlink
{
	%1-ar cq [target] [resolve [%/lib%.a_obj]];
}
.eE
The variable assignment given above requires no modifications.
.H 3 "Linking Commands"
The command linking recipe requires few modifications.
.eB
%1/bin/%: [bin/%_obj]
	set mkdir
{
	%1-gcc -o [target] [resolve [bin/%_obj]];
}
.eE
The variable assignment needs no modifications.
.H 3 "What to Build"
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.
.eB
all:
	[addprefix i386-linux/bin/ [commands]]
	[addprefix sparc-linux/bin/ [commands]]
	[addprefix sparc-solaris2.0/bin/ [commands]]
	[addprefix m68k-sunos4.1.3/bin/ [commands]]
	;
.eE
.P
All of these architectures will be built in a single Cook invocation,
on appropriate machines if necessary.  The use of \f(CW--continue\fP
and \f(CW--parallel\fP work over the entire scope of the build.