aboutsummaryrefslogtreecommitdiff
path: root/en_US.ISO8859-1/books/pmake/gods/chapter.sgml
blob: bc57aba8c97bc7013adaec349ec2da54bce48063 (plain) (blame)
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
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<!--
    $FreeBSD$
-->

<chapter id="gods">
  <title>PMake for Gods</title>

  <para>This chapter is devoted to those facilities in
    <application>PMake</application> that allow you to do a great deal
    in a makefile with very little work, as well as do some things you
    could not do in <application>Make</application> without a great
    deal of work (and perhaps the use of other programs).  The problem
    with these features, is they must be handled with care, or you
    will end up with a mess.</para>

  <para>Once more, I assume a greater familiarity with &unix; or Sprite
    than I did in the previous two chapters.</para>

  <section id="searchpaths">
    <title>Search Paths</title>

    <para><application>PMake</application> supports the dispersal of
      files into multiple directories by allowing you to specify
      places to look for sources with <maketarget>.PATH</maketarget>
      targets in the makefile.  The directories you give as sources
      for these targets make up a <quote>search path</quote>.  Only
      those files used exclusively as sources are actually sought on a
      search path, the assumption being that anything listed as a
      target in the makefile can be created by the makefile and thus
      should be in the current directory.</para>

    <para>There are two types of search paths in
      <application>PMake</application>: one is used for all types of
      files (including included makefiles) and is specified with a
      plain <maketarget>.PATH</maketarget> target (e.g. <literal>.PATH
      : RCS</literal>), while the other is specific to a certain
      type of file, as indicated by the file's suffix.  A specific
      search path is indicated by immediately following the
      <maketarget>.PATH</maketarget> with the suffix of the file.  For
      instance:</para>

    <programlisting>.PATH.h         : /sprite/lib/include /sprite/att/lib/include</programlisting>

    <para>would tell <application>PMake</application> to look in the
      directories <filename class="directory">/sprite/lib/include</filename> and
      <filename class="directory">/sprite/att/lib/include</filename> for any
      files whose suffix is <filename>.h</filename>.</para>

    <para>The current directory is always consulted first to see if a
      file exists.  Only if it cannot be found there are the
      directories in the specific search path, followed by those in
      the general search path, consulted.</para>

    <para>A search path is also used when expanding wildcard
      characters.  If the pattern has a recognizable suffix on it,
      the path for that suffix will be used for the expansion.
      Otherwise the default search path is employed.</para>

    <para>When a file is found in some directory other than the
      current one, all local variables that would have contained the
      target's name (<makevar>.ALLSRC</makevar>, and
      <makevar>.IMPSRC</makevar>) will instead contain
      the path to the file, as found by
      <application>PMake</application>.
      Thus if you have a file <filename>../lib/mumble.c</filename>
      and a makefile like this:</para>

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

    <para>the command executed to create mumble would be
      <command>cc -o mumble ../lib/mumble.c.</command>
      (as an aside, the command in this case is not strictly
      necessary, since it will be found using transformation rules
      if it is not given.  This is because <filename>.out</filename>
      is the null suffix by default and a transformation exists
      from <filename>.c</filename> to
      <filename>.out</filename>.  Just thought I would throw that in).
      If a file exists in two directories on the same search path,
      the file in the first directory on the path will be the one
      <application>PMake</application> uses.  So if you have
      a large system spread over many directories, it would
      behoove you to follow a naming convention that avoids such
      conflicts.</para>

    <para>Something you should know about the way search paths are
      implemented is that each directory is read, and its contents
      cached, exactly once &ndash; when it is first encountered
      &ndash; so any changes to the directories while
      <application>PMake</application> is running will not be noted
      when searching for implicit sources, nor will they be found when
      <application>PMake</application> attempts to discover when the
      file was last modified, unless the file was created in the
      current directory.  While people have suggested that
      <application>PMake</application> should read the directories
      each time, my experience suggests that the caching seldom causes
      problems.  In addition, not caching the directories slows things
      down enormously because of <application>PMake</application>'s attempts
      to apply transformation rules through non-existent files &ndash; the
      number of extra file-system searches is truly staggering,
      especially if many files without suffixes are used and the null
      suffix is not changed from <filename>.out</filename>.</para>
  </section>

  <section id="archivesandlibraries">
    <title>Archives and Libraries</title>

    <para>&unix; and Sprite allow you to merge files into an archive
      using the <command>ar</command> command.  Further, if the files
      are relocatable object files, you can run
      <application>ranlib</application> on the archive and get
      yourself a library that you can link into any program you want.
      The main problem with archives is they double the space you need
      to store the archived files, since there is one copy in the
      archive and one copy out by itself.  The problem with libraries
      is you usually think of them as <option>-lm</option> rather
      than <filename>/usr/lib/libm.a</filename> and the linker thinks
      they are out-of-date if you so much as look at them.</para>

    <para><application>PMake</application> solves the problem with
      archives by allowing you to tell it to examine the files in the
      archives (so you can remove the individual files without having
      to regenerate them later).  To handle the problem with
      libraries, <application>PMake</application> adds an additional
      way of deciding if a library is out-of-date: if the table of
      contents is older than the library, or is missing, the library
      is out-of-date.</para>

    <para>A library is any target that looks like <option>-lname</option>
      or that ends in a suffix that was marked as a library using the
      <maketarget>.LIBS</maketarget> target.  <filename>.a</filename>
      is so marked in the system makefile.  Members of an archive are
      specified as <literal>archive(member[member...])</literal>.
      Thus <literal>libdix.a(window.o)</literal> specifies the
      file <filename>window.o</filename> in the archive
      <filename>libdix.a</filename>.  You may also use
      wildcards to specify the members of the archive.  Just
      remember that most the wildcard characters will only find
      existing files.  A file that is a member of an archive is
      treated specially.  If the file does not exist, but it is
      in the archive, the modification time recorded in the
      archive is used for the file when determining if the file
      is out-of-date.  When figuring out how to make an archived
      member target (not the file itself, but the file in the
      archive &ndash; the archive(member) target), special care
      is taken with the transformation rules, as follows:</para>

    <itemizedlist>
      <listitem>
	<para>archive(member) is made to depend on member.</para>
      </listitem>

      <listitem>
	<para>The transformation from the member's suffix to the
	  archive's suffix is applied to the archive(member) target.</para>
      </listitem>

      <listitem>
	<para>The archive(member)'s <makevar>.TARGET</makevar>
	  variable is set to the name of the member if member is
	  actually a target, or the path to the member file if
	  member is only a source.</para>
      </listitem>

      <listitem>
	<para>The <makevar>.ARCHIVE</makevar> variable for the
	  archive(member) target is set to the name of the
	  archive.</para>
      </listitem>

      <listitem>
	<para>The <makevar>.MEMBER</makevar> variable is set to the
	  actual string inside the parentheses.  In most cases,
	  this will be the same as the <makevar>.TARGET</makevar>
	  variable.</para>
      </listitem>

      <listitem>
	<para>The archive(member)'s place in the local variables of
	  the targets that depend on it is taken by the value of its
	  <makevar>.TARGET</makevar> variable.</para>
      </listitem>
    </itemizedlist>

    <para>Thus, a program library could be created with the following
      makefile:</para>

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

    <para>This will cause the three object files to be compiled (if
      the corresponding source files were modified after the object
      file or, if that does not exist, the archived object file), the
      out-of-date ones archived in <filename>libprog.a</filename>, a
      table of contents placed in the archive and the newly-archived
      object files to be removed.</para>

    <para>All this is used in the <filename>makelib.mk</filename> system
      makefile to create a single library with ease.  This makefile looks
      like this:</para>

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

#ifndef _MAKELIB_MK
_MAKELIB_MK    =

#include  &lt;po.mk&gt;

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

ARFLAGS        ?= crl

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

#endif _MAKELIB_MK</programlisting>
  </section>

  <section id="condition">
    <title>On the Condition...</title>

    <para>Like the C compiler before it, <application>PMake</application>
      allows you to configure the makefile, based on the current
      environment, using conditional statements.  A conditional looks like
      this:</para>

      <programlisting>#if boolean expression
lines
#elif another boolean expression
more lines
#else
still more lines
#endif</programlisting>

    <para>They may be nested to a maximum depth of 30 and may occur
      anywhere (except in a comment, of course).  The
      <literal>#</literal> must the very first character on the
      line.</para>

    <para>Each boolean expression is made up of terms that look
      like function calls, the standard C boolean operators
      <literal>&amp;&amp;</literal>, <literal>||</literal>, and
      <literal>!</literal>, and the standard relational operators
      <literal>==</literal>, <literal>!=</literal>, <literal>&gt;</literal>,
      <literal>&gt;=</literal>, <literal>&lt;</literal>, and
      <literal>&lt;=</literal>, with <literal>==</literal> and
      <literal>!=</literal> being overloaded to allow string
      comparisons as well.  <literal>&amp;&amp;</literal> represents logical
      AND; <literal>||</literal> is logical OR and <literal>!</literal>
      is logical NOT.  The arithmetic and string operators take
      precedence over all three of these operators, while NOT
      takes precedence over AND, which takes  precedence over OR.
      This precedence may be overridden with parentheses, and an
      expression may be parenthesized to your heart's content.
      Each term looks like a call on one of four functions:</para>

    <informaltable frame="none">
      <tgroup cols="2">
	<colspec colwidth="1*"/>
	<colspec colwidth="10*"/>

	<tbody>
	  <row valign="top">
	    <entry><literal>make</literal></entry>

	    <entry>The syntax is make(target) where target is
	      a target in the  makefile.  This is true if the
	      given target was specified on the command line, or
	      as the source for a <maketarget>.MAIN</maketarget>
	      target (note that the sources for
	      <maketarget>.MAIN</maketarget> are only used if no
	      targets were given on the command
	      line).</entry>
	  </row>

	  <row valign="top">
	    <entry><literal>defined</literal></entry>

	    <entry>The syntax is
	      <literal>defined(variable)</literal> and is true
	      if variable is defined.  Certain variables are
	      defined in the system makefile that identify the
	      system on which <application>PMake</application>
	      is being run.</entry>
	  </row>

	  <row valign="top">
	    <entry><literal>exists</literal></entry>

	    <entry>The syntax is
	      <literal>exists(file)</literal> and is true if the
	      file can be found on the global search path (i.e.
	      that defined by <makevar>.PATH</makevar> targets, not by
	      <maketarget>.PATH<replaceable>suffix</replaceable></maketarget>
	      targets).</entry>
	  </row>

	  <row valign="top">
	    <entry><literal>empty</literal></entry>

	    <entry>This syntax is much like the others, except
	      the string inside the parentheses is of the same
	      form as you would put between parentheses when
	      expanding a variable, complete with modifiers and
	      everything.  The function returns true if the
	      resulting string is empty.  An undefined
	      variable in this context will cause at the very
	      least a warning message about a malformed
	      conditional, and at the worst will cause the process
	      to stop once it has read the makefile.  If you want
	      to check for a variable being defined or empty,
	      use the expression:
	      <literal>!defined(var) || empty(var)</literal>
	      as the definition of <literal>||</literal> will
	      prevent the <literal>empty()</literal> from being
	      evaluated and causing an error, if the variable
	      is undefined.  This can be used to see if a
	      variable contains a given word, for example:
	      <literal>#if !empty(var:Mword)</literal></entry>
	  </row>
	</tbody>
      </tgroup>
    </informaltable>

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

    <programlisting>#if $(OS) == 4.3

#if $(MACHINE) == "sun3"

#if $(LOAD_ADDR) &gt; 0xc000</programlisting>

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

    <programlisting>#if $(LOAD)</programlisting>

    <para>would see if <literal>LOAD</literal> contains a
      non-zero value and:</para>

    <programlisting>#if !$(LOAD)</programlisting>

    <para>would test if <literal>LOAD</literal> contains a
      zero value.</para>

    <para>In addition to the bare <literal>#if</literal>, there are other
      forms that apply one of the first two functions to each term.
      They are as follows:</para>

    <informaltable frame="none">
      <tgroup cols="3">
	<tbody>
	  <row>
	    <entry><literal>ifdef</literal></entry>

	    <entry><literal>defined</literal></entry>
	  </row>

	  <row>
	    <entry><literal>ifndef</literal></entry>

	    <entry><literal>!defined</literal></entry>
	  </row>

	  <row>
	    <entry><literal>ifmake</literal></entry>

	    <entry><literal>make</literal></entry>
	  </row>

	  <row>
	    <entry><literal>ifnmake</literal></entry>

	    <entry><literal>!make</literal></entry>
	  </row>
	</tbody>
      </tgroup>
    </informaltable>

    <para>There are also the <quote><literal>else
      if</literal></quote> forms: <literal>elif</literal>,
      <literal>elifdef</literal>, <literal>elifndef</literal>,
      <literal>elifmake</literal>, and <literal>elifnmake</literal>.</para>

    <para>For instance, if you wish to create two versions of a
      program, one of which is optimized (the production version) and
      the other of which is for debugging (has symbols for dbx),
      you have two choices: you can create two makefiles, one of
      which uses the <option>-g</option> flag for the compilation,
      while the other uses the <option>-O</option> flag, or you
      can use another target (call it debug) to create the debug
      version.  The construct below will take care of this for you.
      I have also made it  so  defining the variable
      <envar>DEBUG</envar> (say with <command>pmake -D DEBUG</command>)
      will also cause the debug version to be made.</para>

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

    <para>There are, of course, problems with this approach.  The most
      glaring annoyance is that if you want to go from making a
      debug version to making a production version, you have to
      remove all the object files, or you will get some optimized
      and some debug versions in the same program.  Another
      annoyance is you have to be careful not to make two targets that
      <quote>conflict</quote> because of some conditionals in the makefile.
      For instance:</para>

    <programlisting>#if make(print)
FORMATTER = ditroff -Plaser_printer
#endif
#if make(draft)
FORMATTER = nroff -Pdot_matrix_printer
#endif</programlisting>

    <para>would wreak havoc if you tried <command>pmake draft print</command>
      since you would use the same formatter for each target.  As I said,
      this all gets somewhat complicated.</para>
  </section>

  <section id="ashell">
    <title id="ashelltitle">A Shell is a Shell is a Shell</title>

    <para>In normal operation, the Bourne Shell (better known
      as <application>sh</application>) is used to execute the
      commands to re-create  targets.  <application>PMake</application>
      also allows you to specify a different shell for it to use when
      executing these commands.  There are several things
      <application>PMake</application> must know about
      the shell you wish to use.  These things are specified as the
      sources for the <maketarget>.SHELL</maketarget> target by
      keyword, as follows:</para>

    <variablelist>
      <varlistentry>
	<term><literal>path=path</literal></term>

	<listitem>
	  <para><application>PMake</application> needs to know where
	    the shell actually resides, so it can execute it.  If
	    you specify this and nothing else,
	    <application>PMake</application> will use the last
	    component of the path and look in its table of the
	    shells it knows and use the specification it finds, if
	    any.  Use this if you just want to use a different
	    version of the <application>Bourne</application> or
	    <application>C Shell</application> (yes,
	    <application>PMake</application> knows how to use the
	    <application>C Shell</application> too).</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>name=name</literal></term>

	<listitem>
	  <para>This is the name by which the shell is to be
	    known.  It is a single word and, if no other keywords
	    are specified (other than path), it is the name by
	    which <application>PMake</application> attempts to find
	    a specification for it (as  mentioned above).  You
	    can use this if you would just rather use the C Shell
	    than the <application>Bourne Shell</application>
	    (<literal>.SHELL: name=csh</literal> will do it).</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>quiet=echo-off</literal> command</term>

	<listitem>
	  <para>As mentioned before, <application>PMake</application>
	    actually controls whether commands are printed by
	    introducing commands into the shell's input stream.
	    This keyword, and the next two, control what those commands
	    are.  The <literal>quiet</literal> keyword is the command
	    used to turn echoing off.  Once it is turned off, echoing is
	    expected to remain off until the <literal>echo-on</literal>
	    command is given.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>echo=echo-on</literal> command</term>

	<listitem>
	  <para>The command <application>PMake</application>
	    should give to turn echoing back on again.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>filter=printed echo-off</literal> command</term>

	<listitem>
	  <para>Many shells will echo the
	    <literal>echo-off</literal> command when it is given.
	    This keyword tells <application>PMake</application> in what
	    format the shell actually prints the <literal>echo-off</literal>
	    command.  Wherever <application>PMake</application>
	    sees this string in the shell's output, it will
	    delete it and  any  following whitespace, up to and
	    including the next newline.  See the example at the
	    end of this section for more details.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>echoFlag=flag</literal> to turn echoing on</term>

	<listitem>
	  <para>Unless a target has been marked
	    <literal>.SILENT</literal>, <application>PMake</application>
	    wants to start the shell running with echoing on.  To do
	    this, it passes this flag to the shell as one of its
	    arguments. If either this or the next flag begins with a
	    <literal>-</literal>, the flags will be passed to the
	    shell as separate arguments.  Otherwise, the two will
	    be concatenated (if they are used at the same time, of
	    course).</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>errFlag=flag</literal> to turn error checking on</term>

	<listitem>
	  <para>Likewise, unless a  target  is  marked
	    <literal>.IGNORE</literal>,
	    <application>PMake</application> wishes error-checking
	    to be on from the very start. To this end, it will pass
	    this flag to the shell as an argument.  The same
	    rules for an initial <literal>-</literal> apply as for
	    the <literal>echoFlag</literal>.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>check=command</literal> to turn error checking on</term>

	<listitem>
	  <para>Just as for echo-control, error-control is achieved
	    by inserting  commands into the shell's input stream.
	    This is the command to make the shell check for errors.
	    It also serves another purpose if the shell does not
	    have error-control as commands, but I will get into that
	    in a minute.  Again, once error checking has been turned
	    on, it is expected to remain on until it is turned off
	    again.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>ignore=command</literal>to turn error checking off</term>

	<listitem>
	  <para>This is the command <application>PMake</application>
	    uses to turn error checking off.  It has another use if
	    the shell does not do errorcontrol, but I will tell you
	    about that...now.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term><literal>hasErrCtl=yes</literal> or <literal>no</literal></term>

	<listitem>
	  <para>This takes a value that is either
	    <literal>yes</literal> or <literal>no</literal>.  Now
	    you might think that the existence of the check and
	    ignore keywords would be enough to tell
	    <application>PMake</application> if the shell can do
	    error-control, but you would be wrong.  If
	    <literal>hasErrCtl</literal> is <literal>yes</literal>,
	    <application>PMake</application> uses the check and
	    ignore commands in a straight-forward manner.  If this
	    is no, however, their use is rather different.  In this
	    case, the check command is used as a template, in which
	    the string <literal>%s</literal> is replaced by the
	    command that is about to be executed, to produce a
	    command for the shell that will echo the command to be
	    executed.  The ignore command is also used as a template,
	    again with <literal>%s</literal> replaced by the command
	    to be executed, to produce a command that will
	    execute the command to be executed and ignore any error
	    it returns.  When these strings are used as templates,
	    you must provide newline(s) (<literal>\n</literal>) in
	    the appropriate place(s).</para>
	</listitem>
      </varlistentry>
    </variablelist>

    <para>The strings that follow these keywords may be enclosed in
      single or double quotes (the quotes will be stripped off) and
      may contain the usual C backslash-characters
      (<literal>\n</literal> is newline, <literal>\r</literal> is
      return, <literal>\b</literal> is backspace,
      <literal>\'</literal> escapes a single-quote inside
      single-quotes, <literal>\"</literal> escapes a double-quote
      inside double-quotes).  Now for an example.</para>

    <para>This is actually the contents of the &lt;shx.mk> system
      makefile, and causes <application>PMake</application>
      to use the <application>Bourne Shell</application> in such a way
      that  each command is printed as it is executed. That is, if
      more than one command is given on a line, each will be
      printed separately.  Similarly, each time the body of a loop
      is executed, the commands within that loop will be printed,
      etc.  The specification runs like this:</para>

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

    <para>It tells <application>PMake</application> the following:</para>

    <itemizedlist>
      <listitem>
	<para>The shell is located in the file
	  <filename>/bin/sh</filename>.  It need not tell
	  <application>PMake</application> that the name of the
	  shell is sh as <application>PMake</application> can
	  figure that out for itself (it is the last component of the
	  path).</para>
      </listitem>

      <listitem>
	<para>The command to stop echoing is set <literal>-</literal>.</para>
      </listitem>

      <listitem>
	<para>The command to start echoing is set <option>-x</option>.</para>
      </listitem>

      <listitem>
	<para>When the echo off command is executed, the shell
	  will print <literal>+</literal> set <literal>-</literal>
	  (The <literal>+</literal> comes from using the
	  <option>-x</option> flag (rather than the
	  <option>-v</option> flag <application>PMake</application>
	  usually uses)).  <application>PMake</application> will
	  remove all occurrences of this string from the output, so
	  you do not notice extra commands you did not put
	  there.</para>
      </listitem>

      <listitem>
	<para>The flag the <application>Bourne Shell</application>
	  will take to start echoing in this way is the
	  <option>-x</option> flag.  The <application>Bourne
	  Shell</application> will only take its flag arguments
	  concatenated as its first argument, so neither this nor
	  the errFlag specification begins with a
	  <literal>-</literal>.</para>
      </listitem>

      <listitem>
	<para>The flag to use to turn error-checking on from the
	  start is <option>-e</option>.</para>
      </listitem>

      <listitem>
	<para>The shell can turn error-checking on and off, and
	  the commands to do so are <literal>set +e</literal> and
	  <literal>set -e</literal>, respectively.</para>
      </listitem>
    </itemizedlist>

    <para>I should note that this specification is for
      <application>Bourne  Shells</application>
      that are not part of Berkeley &unix;, as shells from Berkeley
      do not do error control.  You can get a similar effect,
      however, by changing the last three lines to be:</para>

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

    <para>This will cause <application>PMake</application> to execute
      the two commands:</para>

    <programlisting>echo "+ cmd"
sh -c 'cmd || true'</programlisting>

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

  <section id="compatibility">
    <title>Compatibility</title>

    <para>There are three (well, 3 1/2) levels of
      backwards-compatibility built into
      <application>PMake</application>.  Most makefiles will need none
      at all.  Some may need a little bit of work to operate correctly
      when run in parallel.  Each level encompasses the previous
      levels (e.g. <option>-B</option> (one shell per command) implies
      <option>-V</option>).  The three levels are described in the
      following three sections.</para>
  </section>

  <section id="defcon3">
    <title>DEFCON 3 &ndash; Variable Expansion</title>

    <para>As noted before, <application>PMake</application> will not
      expand a variable unless it knows of a value for it.  This can
      cause problems for makefiles that expect to leave variables
      undefined except in special circumstances (e.g. if more flags
      need to be passed to the C compiler or the output from a text
      processor should be sent to a different printer).  If the
      variables are enclosed in curly braces
      (<literal>${PRINTER}</literal>), the shell will let them pass.
      If they are enclosed in parentheses, however, the shell will
      declare a syntax error and the make will come to a grinding
      halt.</para>

    <para>You have two choices: change the makefile to define the
      variables (their values can be overridden on the command line,
      since that is where they would have been set if you used
      <application>Make</application>, anyway) or always give the
      <option>-V</option> flag (this can be done with the
      <maketarget>.MAKEFLAGS</maketarget> target, if you want).</para>
  </section>

  <section id="defcon2">
    <title>DEFCON 2 &ndash; The Number of the Beast</title>

    <para>Then there are the makefiles that expect certain commands,
      such as changing to a different directory, to not affect
      other commands in a target's creation script.  You can solve
      this is either by going back to executing one shell per
      command (which is what the <option>-B</option> flag forces
      <application>PMake</application> to do), which
      slows the process down a good bit and requires you to use
      semicolons and escaped newlines for shell constructs, or by
      changing the makefile to execute the offending command(s) in
      a subshell (by placing the line inside parentheses), like
      so:</para>

    <programlisting>install :: .MAKE
      (cd src; $(.PMAKE) install)
      (cd lib; $(.PMAKE) install)
      (cd man; $(.PMAKE) install)</programlisting>

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

  <section id="defcon1">
    <title>DEFCON 1 &ndash; Imitation is the Not the Highest Form of
      Flattery</title>

    <para>The final category of makefile is the one where every command
      requires input, the dependencies are incompletely specified, or
      you simply cannot create more than one target at a time, as
      mentioned earlier.  In addition, you may not have the time or
      desire to upgrade the makefile to run smoothly with
      <application>PMake</application>.  If you are the conservative
      sort, this is the compatibility mode for you.  It is entered
      either by giving <application>PMake</application> the
      <option>-M</option> flag (for <application>Make</application>),
      or by executing <application>PMake</application> as
      <command>make.</command>  In either case,
      <application>PMake</application> performs things exactly like
      <application>Make</application> (while still supporting most
      of the nice new features <application>PMake</application>
      provides).  This includes:</para>

    <itemizedlist>
      <listitem>
	<para>No parallel execution.</para>
      </listitem>

      <listitem>
	<para>Targets are made in the exact order specified by the
	  makefile.  The sources for each target are made in strict
	  left-to-right order, etc.</para>
      </listitem>

      <listitem>
	<para>A single Bourne shell is used to execute each command,
	  thus the shell's <varname>$$</varname> variable is
	  useless, changing directories does not work across command
	  lines, etc.</para>
      </listitem>

      <listitem>
	<para>If no special characters exist in a command line,
	  <application>PMake</application> will break the command
	  into words itself and execute the command directly,
	  without executing a shell first.  The characters that
	  cause <application>PMake</application> to execute a shell
	  are: <literal>#</literal>, <literal>=</literal>,
	  <literal>|</literal>, <literal>^</literal>,
	  <literal>(</literal>, <literal>)</literal>,
	  <literal>{</literal>, <literal>}</literal>,
	  <literal>;</literal>, <literal>&amp;</literal>,
	  <literal>&gt;</literal>, <literal>&lt;</literal>,
	  <literal>*</literal>, <literal>?</literal>,
	  <literal>[</literal>, <literal>]</literal>,
	  <literal>:</literal>, <literal>$</literal>,
	  <literal>`</literal>, and <literal>\</literal>.  You should
	  notice that these are all the characters that are given
	  special meaning by the shell (except <literal>'</literal>
	  and <literal>,</literal> which
	  <application>PMake</application> deals with all by its
	  lonesome).</para>
      </listitem>

      <listitem>
	<para>The use of the null suffix is turned off.</para>
      </listitem>
    </itemizedlist>
  </section>

  <section id="theway">
    <title>The Way Things Work</title>

    <para>When <application>PMake</application> reads the makefile, it
      parses sources and targets into nodes in a graph.  The graph is
      directed only in the sense that <application>PMake</application>
      knows which way is up.  Each node contains not only links to all
      its parents and children (the nodes that depend on it and those
      on which it depends, respectively), but also a count of the
      number of its children that have already been processed.</para>

    <para>The most important thing to know about how
      <application>PMake</application> uses this graph is that the
      traversal is breadth-first and occurs in two passes.</para>

    <para>After <application>PMake</application> has parsed the
      makefile, it begins with the nodes the user has told it to make
      (either on the command line, or via a
      <maketarget>.MAIN</maketarget> target, or by the target being
      the first in the file not labeled with the
      <literal>.NOTMAIN</literal> attribute) placed in a queue.  It
      continues to take the node off the front of the queue, mark it
      as something that needs to be made, pass the node to
      <literal>Suff_FindDeps</literal> (mentioned earlier) to find any
      implicit sources for the node, and place all the node's children
      that have yet to be marked at the end of the queue.  If any of
      the children is a <maketarget>.USE</maketarget> rule, its
      attributes are applied to the parent, then its commands are
      appended to the parent's list of commands and its children are
      linked to its parent.  The parent's unmade children counter is
      then decremented (since the <maketarget>.USE</maketarget> node
      has been processed).  You will note that this allows a
      <maketarget>.USE</maketarget> node to have children that are
      <maketarget>.USE</maketarget> nodes and the rules will be
      applied in sequence.  If the node has no children, it is placed
      at the end of another queue to be examined in the  second pass.
      This process continues until the first queue is empty.</para>

    <para>At this point, all the leaves of the graph are in the
      examination queue.  <application>PMake</application> removes the
      node at the head of the queue and sees if it is out-of-date.  If
      it is, it is passed to a function that will execute the commands
      for the node asynchronously.  When the commands have completed,
      all the node's parents have their unmade children counter
      decremented and, if the counter is then 0, they are placed on
      the examination queue.  Likewise, if the node is up-to-date.
      Only those parents that were marked on the downward pass are
      processed in this way.  Thus <application>PMake</application>
      traverses the graph back up to the nodes the user instructed it
      to create.  When the examination queue is empty and no shells
      are running to create a target, <application>PMake</application>
      is finished.</para>

    <para>Once all targets have been processed,
      <application>PMake</application> executes the commands attached
      to the <maketarget>.END</maketarget> target, either explicitly
      or through the use of an ellipsis in a shell script.  If there
      were no errors during the entire process but there are still
      some targets unmade (<application>PMake</application> keeps a
      running count of how many targets are left to be made), there is
      a cycle in the graph.  <application>PMake</application> does a
      depth-first traversal of the graph to find all the targets that
      were not made and prints them out one by one.</para>
  </section>
</chapter>