aboutsummaryrefslogtreecommitdiff
path: root/share/doc/psd/12.make/tutorial.ms
blob: e9d16dcacf149b5fb455ab80d25db79a2c7d3f69 (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
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
.\" Copyright (c) 1988, 1989 by Adam de Boor
.\" Copyright (c) 1989 by Berkeley Softworks
.\" Copyright (c) 1988, 1989, 1993
.\"	The Regents of the University of California.  All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" Adam de Boor.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the University nor the names of its contributors
.\"    may be used to endorse or promote products derived from this software
.\"    without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\"	@(#)tutorial.ms	8.1 (Berkeley) 8/18/93
.\" $FreeBSD$
.\"
.EH 'PSD:12-%''PMake \*- A Tutorial'
.OH 'PMake \*- A Tutorial''PSD:12-%'
.\" xH is a macro to provide numbered headers that are automatically stuffed
.\" into a table-of-contents, properly indented, etc. If the first argument
.\" is numeric, it is taken as the depth for numbering (as for .NH), else
.\" the default (1) is assumed.
.\"
.\" @P The initial paragraph distance.
.\" @Q The piece of section number to increment (or 0 if none given)
.\" @R Section header.
.\" @S Indent for toc entry
.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter)
.de xH
.NH \\$1
\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
.nr PD .1v
.XS \\n%
.ta 0.6i
\\*(SN	\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
.XE
.nr PD .3v
..
.ig
.\" CW is used to place a string in fixed-width or switch to a
.\" fixed-width font.
.\" C is a typewriter font for a laserwriter. Use something else if
.\" you don't have one...
.de CW
.ie !\\n(.$ .ft S
.el \&\\$3\fS\\$1\fP\\$2
..
.\" Anything I put in a display I want to be in fixed-width
.am DS
.CW
..
.\" The stuff in .No produces a little stop sign in the left margin
.\" that says NOTE in it. Unfortunately, it does cause a break, but
.\" hey. Can't have everything. In case you're wondering how I came
.\" up with such weird commands, they came from running grn on a
.\" gremlin file...
.de No
.br
.ne 0.5i
.po -0.5i
.br
.mk 
.nr g3 \\n(.f
.nr g4 \\n(.s
.ig ft
.sp -1
.\" .st cf
\D's -1u'\D't 5u'
.sp -1
\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u'
.sp -1
\D't 3u'
.sp -1
.sp 7u
\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u'
.sp -1
.ft R
.ps 6
.nr g8 \\n(.d
.ds g9 "NOTE
.sp 74u
\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9
.sp |\\n(g8u
.sp 166u
.ig br
\D't 3u'\D's -1u'
.br
.po
.rt 
.ft \\n(g3
.ps \\n(g4
..
.de Bp
.ie !\\n(.$ .IP \(bu 2
.el .IP "\&" 2
..
.po +.3i
.TL
PMake \*- A Tutorial
.AU
Adam de Boor
.AI
Berkeley Softworks
2150 Shattuck Ave, Penthouse
Berkeley, CA 94704
adam@bsw.uu.net
\&...!uunet!bsw!adam
.FS
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appears in all copies.
The University of California, Berkeley Softworks, and Adam de Boor make no
representations about the suitability of this software for any
purpose.  It is provided "as is" without express or implied warranty.
.FE
.PP
.xH 1 Introduction
.LP
PMake is a program for creating other programs, or anything else you
can think of for it to do.  The basic idea behind PMake is that, for
any given system, be it a program or a document or whatever, there
will be some files that depend on the state of other files (on when
they were last modified). PMake takes these dependencies, which you
must specify, and uses them to build whatever it is you want it to
build.
.LP
PMake is almost fully-compatible with Make, with which you may already
be familiar. PMake's most important feature is its ability to run
several different jobs at once, making the creation of systems
considerably faster. It also has a great deal more functionality than
Make. Throughout the text, whenever something is mentioned that is an
important difference between PMake and Make (i.e.  something that will
cause a makefile to fail if you don't do something about it), or is
simply important, it will be flagged with a little sign in the left
margin, like this:
.No
.LP
This tutorial is divided into three main sections corresponding to basic,
intermediate and advanced PMake usage. If you already know Make well,
you will only need to skim chapter 2 (there are some aspects of
PMake that I consider basic to its use that didn't exist in Make).
Things in chapter 3 make life much easier, while those in chapter 4
are strictly for those who know what they are doing. Chapter 5 has
definitions for the jargon I use and chapter 6 contains possible
solutions to the problems presented throughout the tutorial.
.xH 1 The Basics of PMake
.LP
PMake takes as input a file that tells a) which files depend on which
other files to be complete and b) what to do about files that are
``out-of-date.'' This file is known as a ``makefile'' and is usually
.Ix 0 def makefile
kept in the top-most directory of the system to be built. While you
can call the makefile anything you want, PMake will look for
.CW Makefile
and
.CW makefile
(in that order) in the current directory if you don't tell it
otherwise.
.Ix 0 def makefile default
To specify a different makefile, use the
.B \-f
flag (e.g.
.CW "pmake -f program.mk" ''). ``
.Ix 0 ref flags -f
.Ix 0 ref makefile other
.LP
A makefile has four different types of lines in it:
.RS
.IP \(bu 2
File dependency specifications
.IP \(bu 2
Creation commands
.IP \(bu 2
Variable assignments
.IP \(bu 2
Comments, include statements and conditional directives
.RE
.LP
Any line may be continued over multiple lines by ending it with a
backslash.
.Ix 0 def "continuation line"
The backslash, following newline and any initial whitespace
on the following line are compressed into a single space before the
input line is examined by PMake.
.xH 2 Dependency Lines
.LP
As mentioned in the introduction, in any system, there are
dependencies between the files that make up the system.  For instance,
in a program made up of several C source files and one header file,
the C files will need to be re-compiled should the header file be
changed. For a document of several chapters and one macro file, the
chapters will need to be reprocessed if any of the macros changes.
.Ix 0 def "dependency"
These are dependencies and are specified by means of dependency lines in
the makefile.
.LP
.Ix 0 def "dependency line"
On a dependency line, there are targets and sources, separated by a
one- or two-character operator.
The targets ``depend'' on the sources and are usually created from
them.
.Ix 0 def target
.Ix 0 def source
.Ix 0 ref operator
Any number of targets and sources may be specified on a dependency line.
All the targets in the line are made to depend on all the sources.
Targets and sources need not be actual files, but every source must be
either an actual file or another target in the makefile.
If you run out of room, use a backslash at the end of the line to continue onto
the next one.
.LP
Any file may be a target and any file may be a source, but the
relationship between the two (or however many) is determined by the
``operator'' that separates them.
.Ix 0 def operator
Three types of operators exist: one specifies that the datedness of a
target is determined by the state of its sources, while another
specifies other files (the sources) that need to be dealt with before
the target can be re-created. The third operator is very similar to
the first, with the additional condition that the target is
out-of-date if it has no sources. These operations are represented by
the colon, the exclamation point and the double-colon, respectively, and are
mutually exclusive. Their exact semantics are as follows:
.IP ":"
.Ix 0 def operator colon
.Ix 0 def :
If a colon is used, a target on the line is considered to be
``out-of-date'' (and in need of creation) if 
.RS
.IP \(bu 2
any of the sources has been modified more recently than the target, or
.IP \(bu 2
the target doesn't exist.
.RE
.Ix 0 def out-of-date
.IP "\&"
Under this operation, steps will be taken to re-create the target only
if it is found to be out-of-date by using these two rules.
.IP "!"
.Ix 0 def operator force
.Ix 0 def !
If an exclamation point is used, the target will always be re-created,
but this will not happen until all of its sources have been examined
and re-created, if necessary.
.IP "::"
.Ix 0 def operator double-colon
.Ix 0 def ::
If a double-colon is used, a target is out-of-date if:
.RS
.IP \(bu 2
any of the sources has been modified more recently than the target, or
.IP \(bu 2
the target doesn't exist, or
.IP \(bu 2
the target has no sources.
.RE
.IP "\&"
If the target is out-of-date according to these rules, it will be re-created.
This operator also does something else to the targets, but I'll go
into that in the next section (``Shell Commands'').
.LP
Enough words, now for an example. Take that C program I mentioned
earlier. Say there are three C files
.CW a.c , (
.CW b.c
and
.CW  c.c )
each of which
includes the file
.CW defs.h .
The dependencies between the files could then be expressed as follows:
.DS
program         : a.o b.o c.o
a.o b.o c.o     : defs.h
a.o             : a.c
b.o             : b.c
c.o             : c.c
.DE
.LP
You may be wondering at this point, where
.CW a.o ,
.CW b.o
and
.CW c.o
came in and why
.I they
depend on
.CW defs.h
and the C files don't. The reason is quite simple:
.CW program
cannot be made by linking together .c files \*- it must be
made from .o files. Likewise, if you change
.CW defs.h ,
it isn't the .c files that need to be re-created, it's the .o files.
If you think of dependencies in these terms \*- which files (targets)
need to be created from which files (sources) \*- you should have no problems.
.LP
An important thing to notice about the above example, is that all the
\&.o files appear as targets on more than one line. This is perfectly
all right: the target is made to depend on all the sources mentioned
on all the dependency lines. E.g.
.CW a.o
depends on both
.CW defs.h
and
.CW a.c .
.Ix 0 ref dependency
.No
.LP
The order of the dependency lines in the makefile is
important: the first target on the first dependency line in the
makefile will be the one that gets made if you don't say otherwise.
That's why
.CW program
comes first in the example makefile, above.
.LP
Both targets and sources may contain the standard C-Shell wildcard
characters
.CW { , (
.CW } ,
.CW * ,
.CW ? ,
.CW [ ,
and
.CW ] ),
but the non-curly-brace ones may only appear in the final component
(the file portion) of the target or source. The characters mean the
following things:
.IP \fB{}\fP
These enclose a comma-separated list of options and cause the pattern
to be expanded once for each element of the list. Each expansion
contains a different element. For example, 
.CW src/{whiffle,beep,fish}.c
expands to the three words
.CW src/whiffle.c ,
.CW src/beep.c ,
and 
.CW src/fish.c .
These braces may be nested and, unlike the other wildcard characters,
the resulting words need not be actual files. All other wildcard
characters are expanded using the files that exist when PMake is
started.
.IP \fB*\fP
This matches zero or more characters of any sort. 
.CW src/*.c
will expand to the same three words as above as long as 
.CW src
contains those three files (and no other files that end in 
.CW .c ).
.IP \fB?\fP
Matches any single character.
.IP \fB[]\fP
This is known as a character class and contains either a list of
single characters, or a series of character ranges 
.CW a-z , (
for example means all characters between a and z), or both. It matches
any single character contained in the list. E.g.
.CW [A-Za-z]
will match all letters, while
.CW [0123456789]
will match all numbers.
.xH 2 Shell Commands
.LP
``Isn't that nice,'' you say to yourself, ``but how are files
actually `re-created,' as he likes to spell it?''
The re-creation is accomplished by commands you place in the makefile.
These commands are passed to the Bourne shell (better known as
``/bin/sh'') to be executed and are
.Ix 0 ref shell
.Ix 0 ref re-creation
.Ix 0 ref update
expected to do what's necessary to update the target file (PMake
doesn't actually check to see if the target was created. It just
assumes it's there).
.Ix 0 ref target
.LP
Shell commands in a makefile look a lot like shell commands you would
type at a terminal, with one important exception: each command in a
makefile
.I must
be preceded by at least one tab.
.LP
Each target has associated with it a shell script made up of
one or more of these shell commands. The creation script for a target
should immediately follow the dependency line for that target. While
any given target may appear on more than one dependency line, only one
of these dependency lines may be followed by a creation script, unless
the `::' operator was used on the dependency line.
.Ix 0 ref operator double-colon
.Ix 0 ref ::
.No
.LP
If the double-colon was used, each dependency line for the target
may be followed by a shell script. That script will only be executed
if the target on the associated dependency line is out-of-date with
respect to the sources on that line, according to the rules I gave
earlier.
I'll give you a good example of this later on.
.LP
To expand on the earlier makefile, you might add commands as follows:
.DS
program         : a.o b.o c.o
        cc a.o b.o c.o \-o program
a.o b.o c.o     : defs.h
a.o             : a.c
        cc \-c a.c
b.o             : b.c
        cc \-c b.c
c.o             : c.c
        cc \-c c.c
.DE
.LP
Something you should remember when writing a makefile is, the
commands will be executed if the
.I target
on the dependency line is out-of-date, not the sources.
.Ix 0 ref target
.Ix 0 ref source
.Ix 0 ref out-of-date
In this example, the command
.CW "cc \-c a.c" '' ``
will be executed if
.CW a.o
is out-of-date. Because of the `:' operator,
.Ix 0 ref :
.Ix 0 ref operator colon
this means that should
.CW a.c
.I or
.CW defs.h
have been modified more recently than
.CW a.o ,
the command will be executed
.CW a.o "\&" (
will be considered out-of-date).
.Ix 0 ref out-of-date
.LP
Remember how I said the only difference between a makefile shell
command and a regular shell command was the leading tab? I lied. There
is another way in which makefile commands differ from regular ones.
The first two characters after the initial whitespace are treated
specially.
If they are any combination of `@' and `\-', they cause PMake to do
different things.
.LP
In most cases, shell commands are printed before they're
actually executed. This is to keep you informed of what's going on. If
an `@' appears, however, this echoing is suppressed. In the case of an
.CW echo
command, say
.CW "echo Linking index" ,'' ``
it would be
rather silly to see
.DS
echo Linking index
Linking index
.DE
.LP
so PMake allows you to place an `@' before the command
.CW "@echo Linking index" '') (``
to prevent the command from being printed.
.LP
The other special character is the `\-'. In case you didn't know,
shell commands finish with a certain ``exit status.'' This status is
made available by the operating system to whatever program invoked the
command. Normally this status will be 0 if everything went ok and
non-zero if something went wrong. For this reason, PMake will consider
an error to have occurred if one of the shells it invokes returns a non-zero
status. When it detects an error, PMake's usual action is to abort
whatever it's doing and exit with a non-zero status itself (any other
targets that were being created will continue being made, but nothing
new will be started. PMake will exit after the last job finishes).
This behavior can be altered, however, by placing a `\-' at the front
of a command
.CW "\-mv index index.old" ''), (``
certain command-line arguments,
or doing other things, to be detailed later. In such
a case, the non-zero status is simply ignored and PMake keeps chugging
along.
.No
.LP
Because all the commands are given to a single shell to execute, such
things as setting shell variables, changing directories, etc., last
beyond the command in which they are found. This also allows shell
compound commands (like
.CW for
loops) to be entered in a natural manner.
Since this could cause problems for some makefiles that depend on
each command being executed by a single shell, PMake has a
.B \-B
.Ix 0 ref compatibility
.Ix 0 ref flags -B
flag (it stands for backwards-compatible) that forces each command to
be given to a separate shell. It also does several other things, all
of which I discourage since they are now old-fashioned.\|.\|.\|.
.No
.LP
A target's shell script is fed to the shell on its (the shell's) input stream.
This means that any commands, such as
.CW ci
that need to get input from the terminal won't work right \*- they'll
get the shell's input, something they probably won't find to their
liking. A simple way around this is to give a command like this:
.DS
ci $(SRCS) < /dev/tty
.DE
This would force the program's input to come from the terminal. If you
can't do this for some reason, your only other alternative is to use
PMake in its fullest compatibility mode. See 
.B Compatibility
in chapter 4.
.Ix 0 ref compatibility
.LP
.xH 2 Variables
.LP
PMake, like Make before it, has the ability to save text in variables
to be recalled later at your convenience. Variables in PMake are used
much like variables in the shell and, by tradition, consist of
all upper-case letters (you don't
.I have
to use all upper-case letters.
In fact there's nothing to stop you from calling a variable
.CW @^&$%$ .
Just tradition). Variables are assigned-to using lines of the form
.Ix 0 def variable assignment
.DS
VARIABLE = value
.DE
.Ix 0 def variable assignment
appended-to by
.DS
VARIABLE += value
.DE
.Ix 0 def variable appending
.Ix 0 def variable assignment appended
.Ix 0 def +=
conditionally assigned-to (if the variable isn't already defined) by
.DS
VARIABLE ?= value
.DE
.Ix 0 def variable assignment conditional
.Ix 0 def ?=
and assigned-to with expansion (i.e. the value is expanded (see below)
before being assigned to the variable\*-useful for placing a value at
the beginning of a variable, or other things) by
.DS
VARIABLE := value
.DE
.Ix 0 def variable assignment expanded
.Ix 0 def :=
.LP
Any whitespace before
.I value
is stripped off. When appending, a space is placed between the old
value and the stuff being appended.
.LP
The final way a variable may be assigned to is using
.DS
VARIABLE != shell-command
.DE
.Ix 0 def variable assignment shell-output
.Ix 0 def !=
In this case, 
.I shell-command
has all its variables expanded (see below) and is passed off to a
shell to execute. The output of the shell is then placed in the
variable. Any newlines (other than the final one) are replaced by
spaces before the assignment is made. This is typically used to find
the current directory via a line like:
.DS
CWD             != pwd
.DE
.LP
.B Note:
this is intended to be used to execute commands that produce small amounts
of output (e.g. ``pwd''). The implementation is less than intelligent and will
likely freeze if you execute something that produces thousands of
bytes of output (8 Kb is the limit on many UNIX systems).
.LP
The value of a variable may be retrieved by enclosing the variable
name in parentheses or curly braces and preceding the whole thing
with a dollar sign.
.LP
For example, to set the variable CFLAGS to the string
.CW "\-I/sprite/src/lib/libc \-O" ,'' ``
you would place a line
.DS
CFLAGS = \-I/sprite/src/lib/libc \-O
.DE
in the makefile and use the word
.CW "$(CFLAGS)"
wherever you would like the string
.CW "\-I/sprite/src/lib/libc \-O"
to appear. This is called variable expansion.
.Ix 0 def variable expansion
.No
.LP
Unlike Make, PMake will not expand a variable unless it knows
the variable exists. E.g. if you have a
.CW "${i}"
in a shell command and you have not assigned a value to the variable
.CW i 
(the empty string is considered a value, by the way), where Make would have
substituted the empty string, PMake will leave the
.CW "${i}"
alone.
To keep PMake from substituting for a variable it knows, precede the
dollar sign with another dollar sign.
(e.g. to pass
.CW "${HOME}"
to the shell, use
.CW "$${HOME}" ).
This causes PMake, in effect, to expand the
.CW $
macro, which expands to a single
.CW $ .
For compatibility, Make's style of variable expansion will be used
if you invoke PMake with any of the compatibility flags (\c
.B \-V ,
.B \-B
or
.B \-M .
The
.B \-V
flag alters just the variable expansion).
.Ix 0 ref flags -V
.Ix 0 ref flags -B
.Ix 0 ref flags -M
.Ix 0 ref compatibility
.LP
.Ix 0 ref variable expansion
There are two different times at which variable expansion occurs:
When parsing a dependency line, the expansion occurs immediately
upon reading the line. If any variable used on a dependency line is
undefined, PMake will print a message and exit.
Variables in shell commands are expanded when the command is
executed.
Variables used inside another variable are expanded whenever the outer
variable is expanded (the expansion of an inner variable has no effect
on the outer variable. I.e. if the outer variable is used on a dependency
line and in a shell command, and the inner variable changes value
between when the dependency line is read and the shell command is
executed, two different values will be substituted for the outer
variable).
.Ix 0 def variable types
.LP
Variables come in four flavors, though they are all expanded the same
and all look about the same. They are (in order of expanding scope):
.RS
.IP \(bu 2
Local variables.
.Ix 0 ref variable local
.IP \(bu 2
Command-line variables.
.Ix 0 ref variable command-line
.IP \(bu 2
Global variables.
.Ix 0 ref variable global
.IP \(bu 2
Environment variables.
.Ix 0 ref variable environment
.RE
.LP
The classification of variables doesn't matter much, except that the
classes are searched from the top (local) to the bottom (environment)
when looking up a variable. The first one found wins.
.xH 3 Local Variables
.LP
.Ix 0 def variable local
Each target can have as many as seven local variables. These are
variables that are only ``visible'' within that target's shell script
and contain such things as the target's name, all of its sources (from
all its dependency lines), those sources that were out-of-date, etc.
Four local variables are defined for all targets. They are:
.RS
.IP ".TARGET"
.Ix 0 def variable local .TARGET
.Ix 0 def .TARGET
The name of the target.
.IP ".OODATE"
.Ix 0 def variable local .OODATE
.Ix 0 def .OODATE
The list of the sources for the target that were considered out-of-date.
The order in the list is not guaranteed to be the same as the order in
which the dependencies were given.
.IP ".ALLSRC"
.Ix 0 def variable local .ALLSRC
.Ix 0 def .ALLSRC
The list of all sources for this target in the order in which they
were given.
.IP ".PREFIX"
.Ix 0 def variable local .PREFIX
.Ix 0 def .PREFIX
The target without its suffix and without any leading path. E.g. for
the target
.CW ../../lib/compat/fsRead.c ,
this variable would contain
.CW fsRead .
.RE
.LP
Three other local variables are set only for certain targets under
special circumstances. These are the ``.IMPSRC,''
.Ix 0 ref variable local .IMPSRC
.Ix 0 ref .IMPSRC
``.ARCHIVE,''
.Ix 0 ref variable local .ARCHIVE
.Ix 0 ref .ARCHIVE
and ``.MEMBER''
.Ix 0 ref variable local .MEMBER
.Ix 0 ref .MEMBER
variables. When they are set and how they are used is described later.
.LP
Four of these variables may be used in sources as well as in shell
scripts.
.Ix 0 def "dynamic source"
.Ix 0 def source dynamic
These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The
variables in the sources are expanded once for each target on the
dependency line, providing what is known as a ``dynamic source,''
.Rd 0
allowing you to specify several dependency lines at once. For example,
.DS
$(OBJS)         : $(.PREFIX).c
.DE
will create a dependency between each object file and its
corresponding C source file.
.xH 3 Command-line Variables
.LP
.Ix 0 def variable command-line
Command-line variables are set when PMake is first invoked by giving a
variable assignment as one of the arguments. For example,
.DS
pmake "CFLAGS = -I/sprite/src/lib/libc -O"
.DE
would make 
.CW CFLAGS
be a command-line variable with the given value. Any assignments to
.CW CFLAGS
in the makefile will have no effect, because once it
is set, there is (almost) nothing you can do to change a command-line
variable (the search order, you see). Command-line variables may be
set using any of the four assignment operators, though only
.CW =
and
.CW ?=
behave as you would expect them to, mostly because assignments to
command-line variables are performed before the makefile is read, thus
the values set in the makefile are unavailable at the time.
.CW +=
.Ix 0 ref +=
.Ix 0 ref variable assignment appended
is the same as
.CW = ,
because the old value of the variable is sought only in the scope in
which the assignment is taking place (for reasons of efficiency that I
won't get into here).
.CW :=
and
.CW ?=
.Ix 0 ref :=
.Ix 0 ref ?=
.Ix 0 ref variable assignment expanded
.Ix 0 ref variable assignment conditional
will work if the only variables used are in the environment.
.CW !=
is sort of pointless to use from the command line, since the same
effect can no doubt be accomplished using the shell's own command
substitution mechanisms (backquotes and all that).
.xH 3 Global Variables
.LP
.Ix 0 def variable global
Global variables are those set or appended-to in the makefile.
There are two classes of global variables: those you set and those PMake sets.
As I said before, the ones you set can have any name you want them to have,
except they may not contain a colon or an exclamation point.
The variables PMake sets (almost) always begin with a
period and always contain upper-case letters, only. The variables are
as follows:
.RS
.IP .PMAKE
.Ix 0 def variable global .PMAKE
.Ix 0 def .PMAKE
.Ix 0 def variable global MAKE
.Ix 0 def MAKE
The name by which PMake was invoked is stored in this variable. For
compatibility, the name is also stored in the MAKE variable.
.IP .MAKEFLAGS
.Ix 0 def variable global .MAKEFLAGS
.Ix 0 def .MAKEFLAGS variable
.Ix 0 def variable global MFLAGS
.Ix 0 def MFLAGS
All the relevant flags with which PMake was invoked. This does not
include such things as
.B \-f
or variable assignments. Again for compatibility, this value is stored
in the MFLAGS variable as well.
.RE
.LP
Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the
section on special targets in chapter 3.
.Ix 0 ref variable global .INCLUDES
.Ix 0 ref variable global .LIBS
.LP
Global variables may be deleted using lines of the form:
.Ix 0 def #undef
.Ix 0 def variable deletion
.DS
#undef \fIvariable\fP
.DE
The
.CW # ' `
must be the first character on the line. Note that this may only be
done on global variables.
.xH 3 Environment Variables
.LP
.Ix 0 def variable environment
Environment variables are passed by the shell that invoked PMake and
are given by PMake to each shell it invokes. They are expanded like
any other variable, but they cannot be altered in any way.
.LP
One special environment variable,
.CW PMAKE ,
.Ix 0 def variable environment PMAKE
is examined by PMake for command-line flags, variable assignments,
etc., it should always use. This variable is examined before the
actual arguments to PMake are. In addition, all flags given to PMake,
either through the
.CW PMAKE
variable or on the command line, are placed in this environment
variable and exported to each shell PMake executes. Thus recursive
invocations of PMake automatically receive the same flags as the
top-most one.
.LP
Using all these variables, you can compress the sample makefile even more:
.DS
OBJS            = a.o b.o c.o
program         : $(OBJS)
        cc $(.ALLSRC) \-o $(.TARGET)
$(OBJS)         : defs.h
a.o             : a.c
        cc \-c a.c
b.o             : b.c
        cc \-c b.c
c.o             : c.c
        cc \-c c.c
.DE
.Ix 0 ref variable local .ALLSRC
.Ix 0 ref .ALLSRC
.Ix 0 ref variable local .TARGET
.Ix 0 ref .TARGET
.Rd 3
.xH 2 Comments
.LP
.Ix 0 def comments
Comments in a makefile start with a `#' character and extend to the
end of the line. They may appear
anywhere you want them, except in a shell command (though the shell
will treat it as a comment, too). If, for some reason, you need to use the `#'
in a variable or on a dependency line, put a backslash in front of it.
PMake will compress the two into a single `#' (Note: this isn't true
if PMake is operating in full-compatibility mode).
.Ix 0 ref flags -M
.Ix 0 ref compatibility
.xH 2 Parallelism
.No
.LP
PMake was specifically designed to re-create several targets at once,
when possible. You do not have to do anything special to cause this to
happen (unless PMake was configured to not act in parallel, in which
case you will have to make use of the
.B \-L
and
.B \-J
flags (see below)),
.Ix 0 ref flags -L
.Ix 0 ref flags -J
but you do have to be careful at times.
.LP
There are several problems you are likely to encounter. One is
that some makefiles (and programs) are written in such a way that it is
impossible for two targets to be made at once. The program
.CW xstr ,
for example,
always modifies the files
.CW strings
and
.CW x.c .
There is no way to change it. Thus you cannot run two of them at once
without something being trashed. Similarly, if you have commands
in the makefile that always send output to the same file, you will not
be able to make more than one target at once unless you change the
file you use. You can, for instance, add a
.CW $$$$
to the end of the file name to tack on the process ID of the shell
executing the command (each
.CW $$
expands to a single
.CW $ ,
thus giving you the shell variable
.CW $$ ).
Since only one shell is used for all the
commands, you'll get the same file name for each command in the
script.
.LP
The other problem comes from improperly-specified dependencies that
worked in Make because of its sequential, depth-first way of examining
them. While I don't want to go into depth on how PMake
works (look in chapter 4 if you're interested), I will warn you that
files in two different ``levels'' of the dependency tree may be
examined in a different order in PMake than they were in Make. For
example, given the makefile
.DS
a               : b c
b               : d
.DE
PMake will examine the targets in the order
.CW c ,
.CW d ,
.CW b ,
.CW a .
If the makefile's author expected PMake to abort before making
.CW c
if an error occurred while making
.CW b ,
or if
.CW b
needed to exist before
.CW c
was made,
s/he will be sorely disappointed. The dependencies are
incomplete, since in both these cases,
.CW c
would depend on
.CW b .
So watch out.
.LP
Another problem you may face is that, while PMake is set up to handle the
output from multiple jobs in a graceful fashion, the same is not so for input.
It has no way to regulate input to different jobs,
so if you use the redirection from
.CW /dev/tty
I mentioned earlier, you must be careful not to run two of the jobs at once.
.xH 2 Writing and Debugging a Makefile
.LP
Now you know most of what's in a makefile, what do you do next? There
are two choices: (1) use one of the uncommonly-available makefile
generators or (2) write your own makefile (I leave out the third choice of
ignoring PMake and doing everything by hand as being beyond the bounds
of common sense).
.LP
When faced with the writing of a makefile, it is usually best to start
from first principles: just what
.I are
you trying to do? What do you want the makefile finally to produce?
.LP
To begin with a somewhat traditional example, let's say you need to
write a makefile to create a program,
.CW expr ,
that takes standard infix expressions and converts them to prefix form (for
no readily apparent reason). You've got three source files, in C, that
make up the program:
.CW main.c ,
.CW parse.c ,
and
.CW output.c .
Harking back to my pithy advice about dependency lines, you write the
first line of the file:
.DS
expr            : main.o parse.o output.o
.DE
because you remember
.CW expr
is made from
.CW .o
files, not
.CW .c
files. Similarly for the
.CW .o
files you produce the lines:
.DS
main.o          : main.c
parse.o         : parse.c
output.o        : output.c
main.o parse.o output.o : defs.h
.DE
.LP
Great. You've now got the dependencies specified. What you need now is
commands. These commands, remember, must produce the target on the
dependency line, usually by using the sources you've listed.
You remember about local variables? Good, so it should come
to you as no surprise when you write
.DS
expr            : main.o parse.o output.o
        cc -o $(.TARGET) $(.ALLSRC)
.DE
Why use the variables? If your program grows to produce postfix
expressions too (which, of course, requires a name change or two), it
is one fewer place you have to change the file. You cannot do this for
the object files, however, because they depend on their corresponding
source files
.I and
.CW defs.h ,
thus if you said
.DS
	cc -c $(.ALLSRC)
.DE
you'd get (for
.CW main.o ):
.DS
	cc -c main.c defs.h
.DE
which is wrong. So you round out the makefile with these lines:
.DS
main.o          : main.c
        cc -c main.c
parse.o         : parse.c
        cc -c parse.c
output.o        : output.c
        cc -c output.c
.DE
.LP
The makefile is now complete and will, in fact, create the program you
want it to without unnecessary compilations or excessive typing on
your part. There are two things wrong with it, however (aside from it
being altogether too long, something I'll address in chapter 3):
.IP 1)
The string
.CW "main.o parse.o output.o" '' ``
is repeated twice, necessitating two changes when you add postfix
(you were planning on that, weren't you?). This is in direct violation
of de Boor's First Rule of writing makefiles:
.QP
.I
Anything that needs to be written more than once
should be placed in a variable.
.IP "\&"
I cannot emphasize this enough as being very important to the
maintenance of a makefile and its program.
.IP 2)
There is no way to alter the way compilations are performed short of
editing the makefile and making the change in all places. This is evil
and violates de Boor's Second Rule, which follows directly from the
first:
.QP
.I
Any flags or programs used inside a makefile should be placed in a variable so
they may be changed, temporarily or permanently, with the greatest ease.
.LP
The makefile should more properly read:
.DS
OBJS            = main.o parse.o output.o
expr            : $(OBJS)
        $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
main.o          : main.c
        $(CC) $(CFLAGS) -c main.c
parse.o         : parse.c
        $(CC) $(CFLAGS) -c parse.c
output.o        : output.c
        $(CC) $(CFLAGS) -c output.c
$(OBJS)         : defs.h
.DE
Alternatively, if you like the idea of dynamic sources mentioned in
section 2.3.1,
.Rm 0 2.3.1
.Rd 4
.Ix 0 ref "dynamic source"
.Ix 0 ref source dynamic
you could write it like this:
.DS
OBJS            = main.o parse.o output.o
expr            : $(OBJS)
        $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
$(OBJS)         : $(.PREFIX).c defs.h
        $(CC) $(CFLAGS) -c $(.PREFIX).c
.DE
These two rules and examples lead to de Boor's First Corollary:
.QP
.I
Variables are your friends.
.LP
Once you've written the makefile comes the sometimes-difficult task of
.Ix 0 ref debugging
making sure the darn thing works. Your most helpful tool to make sure
the makefile is at least syntactically correct is the
.B \-n
.Ix 0 ref flags -n
flag, which allows you to see if PMake will choke on the makefile. The
second thing the
.B \-n
flag lets you do is see what PMake would do without it actually doing
it, thus you can make sure the right commands would be executed were
you to give PMake its head.
.LP
When you find your makefile isn't behaving as you hoped, the first
question that comes to mind (after ``What time is it, anyway?'') is
``Why not?'' In answering this, two flags will serve you well:
.CW "-d m" '' ``
.Ix 0 ref flags -d
and
.CW "-p 2" .'' ``
.Ix 0 ref flags -p
The first causes PMake to tell you as it examines each target in the
makefile and indicate why it is deciding whatever it is deciding. You
can then use the information printed for other targets to see where
you went wrong. The
.CW "-p 2" '' ``
flag makes PMake print out its internal state when it is done,
allowing you to see that you forgot to make that one chapter depend on
that file of macros you just got a new version of. The output from
.CW "-p 2" '' ``
is intended to resemble closely a real makefile, but with additional
information provided and with variables expanded in those commands
PMake actually printed or executed.
.LP
Something to be especially careful about is circular dependencies.
.Ix 0 def dependency circular
E.g.
.DS
a		: b
b		: c d
d		: a
.DE
In this case, because of how PMake works,
.CW c
is the only thing PMake will examine, because
.CW d
and
.CW a
will effectively fall off the edge of the universe, making it
impossible to examine
.CW b
(or them, for that matter).
PMake will tell you (if run in its normal mode) all the targets
involved in any cycle it looked at (i.e. if you have two cycles in the
graph (naughty, naughty), but only try to make a target in one of
them, PMake will only tell you about that one. You'll have to try to
make the other to find the second cycle). When run as Make, it will
only print the first target in the cycle.
.xH 2 Invoking PMake
.LP
.Ix 0 ref flags
.Ix 0 ref arguments
.Ix 0 ref usage
PMake comes with a wide variety of flags to choose from.
They may appear in any order, interspersed with command-line variable
assignments and targets to create.
The flags are as follows:
.IP "\fB\-d\fP \fIwhat\fP"
.Ix 0 def flags -d
.Ix 0 ref debugging
This causes PMake to spew out debugging information that
may prove useful to you. If you can't
figure out why PMake is doing what it's doing, you might try using
this flag. The
.I what
parameter is a string of single characters that tell PMake what
aspects you are interested in. Most of what I describe will make
little sense to you, unless you've dealt with Make before. Just
remember where this table is and come back to it as you read on.
The characters and the information they produce are as follows:
.RS
.IP a
Archive searching and caching.
.IP c
Conditional evaluation.
.IP d
The searching and caching of directories.
.IP j
Various snippets of information related to the running of the multiple
shells. Not particularly interesting.
.IP m
The making of each target: what target is being examined; when it was
last modified; whether it is out-of-date; etc.
.IP p
Makefile parsing.
.IP r
Remote execution.
.IP s
The application of suffix-transformation rules. (See chapter 3)
.IP t
The maintenance of the list of targets.
.IP v
Variable assignment.
.RE
.IP "\&"
Of these all, the
.CW m
and
.CW s
letters will be most useful to you.
If the
.B \-d
is the final argument or the argument from which it would get these
key letters (see below for a note about which argument would be used)
begins with a
.B \- ,
all of these debugging flags will be set, resulting in massive amounts
of output.
.IP "\fB\-f\fP \fImakefile\fP"
.Ix 0 def flags -f
Specify a makefile to read different from the standard makefiles
.CW Makefile "\&" (
or
.CW makefile ).
.Ix 0 ref makefile default
.Ix 0 ref makefile other
If
.I makefile
is ``\-'', PMake uses the standard input. This is useful for making
quick and dirty makefiles.\|.\|.
.Ix 0 ref makefile "quick and dirty"
.IP \fB\-h\fP
.Ix 0 def flags -h
Prints out a summary of the various flags PMake accepts. It can also
be used to find out what level of concurrency was compiled into the
version of PMake you are using (look at
.B \-J
and
.B \-L )
and various other information on how PMake was configured.
.Ix 0 ref configuration
.Ix 0 ref makefilesystem
.IP \fB\-i\fP
.Ix 0 def flags -i
If you give this flag, PMake will ignore non-zero status returned
by any of its shells. It's like placing a `\-' before all the commands
in the makefile.
.IP \fB\-k\fP
.Ix 0 def flags -k
This is similar to
.B \-i
in that it allows PMake to continue when it sees an error, but unlike
.B \-i ,
where PMake continues blithely as if nothing went wrong,
.B \-k
causes it to recognize the error and only continue work on those
things that don't depend on the target, either directly or indirectly (through
depending on something that depends on it), whose creation returned the error.
The `k' is for ``keep going''.\|.\|.
.Ix 0 ref target
.IP \fB\-l\fP
.Ix 0 def flags -l
PMake has the ability to lock a directory against other
people executing it in the same directory (by means of a file called
``LOCK.make'' that it creates and checks for in the directory). This
is a Good Thing because two people doing the same thing in the same place
can be disastrous for the final product (too many cooks and all that).
Whether this locking is the default is up to your system
administrator. If locking is on,
.B \-l
will turn it off, and vice versa. Note that this locking will not
prevent \fIyou\fP from invoking PMake twice in the same place \*- if
you own the lock file, PMake will warn you about it but continue to execute.
.IP "\fB\-m\fP \fIdirectory\fP"
.Ix 0 def flags -m
Tells PMake another place to search for included makefiles via the <...>
style.  Several
.B \-m
options can be given to form a search path.  If this construct is used the
default system makefile search path is completely overridden.
To be explained in chapter 3, section 3.2.
.Rm 2 3.2
.IP \fB\-n\fP
.Ix 0 def flags -n
This flag tells PMake not to execute the commands needed to update the
out-of-date targets in the makefile. Rather, PMake will simply print
the commands it would have executed and exit. This is particularly
useful for checking the correctness of a makefile. If PMake doesn't do
what you expect it to, it's a good chance the makefile is wrong.
.IP "\fB\-p\fP \fInumber\fP"
.Ix 0 def flags -p
.Ix 0 ref debugging
This causes PMake to print its input in a reasonable form, though
not necessarily one that would make immediate sense to anyone but me. The
.I number
is a bitwise-or of 1 and 2 where 1 means it should print the input
before doing any processing and 2 says it should print it after
everything has been re-created. Thus
.CW "\-p 3"
would print it twice\*-once before processing and once after (you
might find the difference between the two interesting). This is mostly
useful to me, but you may find it informative in some bizarre circumstances.
.IP \fB\-q\fP
.Ix 0 def flags -q
If you give PMake this flag, it will not try to re-create anything. It
will just see if anything is out-of-date and exit non-zero if so.
.IP \fB\-r\fP
.Ix 0 def flags -r
When PMake starts up, it reads a default makefile that tells it what
sort of system it's on and gives it some idea of what to do if you
don't tell it anything. I'll tell you about it in chapter 3. If you
give this flag, PMake won't read the default makefile.
.IP \fB\-s\fP
.Ix 0 def flags -s
This causes PMake to not print commands before they're executed. It
is the equivalent of putting an `@' before every command in the
makefile.
.IP \fB\-t\fP
.Ix 0 def flags -t
Rather than try to re-create a target, PMake will simply ``touch'' it
so as to make it appear up-to-date. If the target didn't exist before,
it will when PMake finishes, but if the target did exist, it will
appear to have been updated.
.IP \fB\-v\fP
.Ix 0 def flags -v
This is a mixed-compatibility flag intended to mimic the System V
version of Make. It is the same as giving
.B \-B ,
and
.B \-V
as well as turning off directory locking. Targets can still be created
in parallel, however. This is the mode PMake will enter if it is
invoked either as
.CW smake '' ``
or
.CW vmake ''. ``
.IP \fB\-x\fP
.Ix 0 def flags -x
This tells PMake it's ok to export jobs to other machines, if they're
available. It is used when running in Make mode, as exporting in this
mode tends to make things run slower than if the commands were just
executed locally.
.IP \fB\-B\fP
.Ix 0 ref compatibility
.Ix 0 def flags -B
Forces PMake to be as backwards-compatible with Make as possible while
still being itself.
This includes:
.RS
.IP \(bu 2
Executing one shell per shell command
.IP \(bu 2
Expanding anything that looks even vaguely like a variable, with the
empty string replacing any variable PMake doesn't know.
.IP \(bu 2
Refusing to allow you to escape a `#' with a backslash.
.IP \(bu 2
Permitting undefined variables on dependency lines and conditionals
(see below). Normally this causes PMake to abort.
.RE
.IP \fB\-C\fP
.Ix 0 def flags -C
This nullifies any and all compatibility mode flags you may have given
or implied up to the time the
.B \-C
is encountered. It is useful mostly in a makefile that you wrote for PMake
to avoid bad things happening when someone runs PMake as
.CW make '' ``
or has things set in the environment that tell it to be compatible.
.B \-C
is
.I not
placed in the
.CW PMAKE
environment variable or the
.CW .MAKEFLAGS
or
.CW MFLAGS
global variables.
.Ix 0 ref variable environment PMAKE
.Ix 0 ref variable global .MAKEFLAGS
.Ix 0 ref variable global MFLAGS
.Ix 0 ref .MAKEFLAGS variable
.Ix 0 ref MFLAGS
.IP "\fB\-D\fP \fIvariable\fP"
.Ix 0 def flags -D
Allows you to define a variable to have 
.CW 1 '' ``
as its value.  The variable is a global variable, not a command-line
variable. This is useful mostly for people who are used to the C
compiler arguments and those using conditionals, which I'll get into
in section 4.3
.Rm 1 4.3
.IP "\fB\-I\fP \fIdirectory\fP"
.Ix 0 def flags -I
Tells PMake another place to search for included makefiles. Yet
another thing to be explained in chapter 3 (section 3.2, to be
precise).
.Rm 2 3.2
.IP "\fB\-J\fP \fInumber\fP"
.Ix 0 def flags -J
Gives the absolute maximum number of targets to create at once on both
local and remote machines.
.IP "\fB\-L\fP \fInumber\fP"
.Ix 0 def flags -L
This specifies the maximum number of targets to create on the local
machine at once. This may be 0, though you should be wary of doing
this, as PMake may hang until a remote machine becomes available, if
one is not available when it is started.
.IP \fB\-M\fP
.Ix 0 ref compatibility
.Ix 0 def flags -M
This is the flag that provides absolute, complete, full compatibility
with Make. It still allows you to use all but a few of the features of
PMake, but it is non-parallel. This is the mode PMake enters if you
call it
.CW make .'' ``
.IP \fB\-P\fP
.Ix 0 def flags -P
.Ix 0 ref "output control"
When creating targets in parallel, several shells are executing at
once, each wanting to write its own two cent's-worth to the screen.
This output must be captured by PMake in some way in order to prevent
the screen from being filled with garbage even more indecipherable
than you usually see. PMake has two ways of doing this, one of which
provides for much cleaner output and a clear separation between the
output of different jobs, the other of which provides a more immediate
response so one can tell what is really happening. The former is done
by notifying you when the creation of a target starts, capturing the
output and transferring it to the screen all at once when the job
finishes. The latter is done by catching the output of the shell (and
its children) and buffering it until an entire line is received, then
printing that line preceded by an indication of which job produced
the output. Since I prefer this second method, it is the one used by
default. The first method will be used if you give the
.B \-P
flag to PMake.
.IP \fB\-V\fP
.Ix 0 def flags -V
As mentioned before, the
.B \-V
flag tells PMake to use Make's style of expanding variables,
substituting the empty string for any variable it doesn't know.
.IP \fB\-W\fP
.Ix 0 def flags -W
There are several times when PMake will print a message at you that is
only a warning, i.e. it can continue to work in spite of your having
done something silly (such as forgotten a leading tab for a shell
command). Sometimes you are well aware of silly things you have done
and would like PMake to stop bothering you. This flag tells it to shut
up about anything non-fatal.
.IP \fB\-X\fP
.Ix 0 def flags -X
This flag causes PMake to not attempt to export any jobs to another
machine.
.LP
Several flags may follow a single `\-'. Those flags that require
arguments take them from successive parameters. E.g.
.DS
pmake -fDnI server.mk DEBUG /chip2/X/server/include
.DE
will cause PMake to read
.CW server.mk
as the input makefile, define the variable
.CW DEBUG
as a global variable and look for included makefiles in the directory
.CW /chip2/X/server/include .
.xH 2 Summary
.LP
A makefile is made of four types of lines:
.RS
.IP \(bu 2
Dependency lines
.IP \(bu 2
Creation commands
.IP \(bu 2
Variable assignments
.IP \(bu 2
Comments, include statements and conditional directives
.RE
.LP
A dependency line is a list of one or more targets, an operator
.CW : ', (`
.CW :: ', `
or
.CW ! '), `
and a list of zero or more sources. Sources may contain wildcards and
certain local variables.
.LP
A creation command is a regular shell command preceded by a tab. In
addition, if the first two characters after the tab (and other
whitespace) are a combination of
.CW @ ' `
or
.CW - ', `
PMake will cause the command to not be printed (if the character is
.CW @ ') `
or errors from it to be ignored (if
.CW - '). `
A blank line, dependency line or variable assignment terminates a
creation script. There may be only one creation script for each target
with a
.CW : ' `
or
.CW ! ' `
operator.
.LP
Variables are places to store text. They may be unconditionally
assigned-to using the
.CW = ' `
.Ix 0 ref =
.Ix 0 ref variable assignment
operator, appended-to using the
.CW += ' `
.Ix 0 ref +=
.Ix 0 ref variable assignment appended
operator, conditionally (if the variable is undefined) assigned-to
with the
.CW ?= ' `
.Ix 0 ref ?=
.Ix 0 ref variable assignment conditional
operator, and assigned-to with variable expansion with the
.CW := ' `
.Ix 0 ref :=
.Ix 0 ref variable assignment expanded
operator. The output of a shell command may be assigned to a variable
using the
.CW != ' `
.Ix 0 ref !=
.Ix 0 ref variable assignment shell-output
operator.  Variables may be expanded (their value inserted) by enclosing
their name in parentheses or curly braces, preceded by a dollar sign.
A dollar sign may be escaped with another dollar sign. Variables are
not expanded if PMake doesn't know about them. There are seven local
variables:
.CW .TARGET ,
.CW .ALLSRC ,
.CW .OODATE ,
.CW .PREFIX ,
.CW .IMPSRC ,
.CW .ARCHIVE ,
and
.CW .MEMBER .
Four of them
.CW .TARGET , (
.CW .PREFIX ,
.CW .ARCHIVE ,
and
.CW .MEMBER )
may be used to specify ``dynamic sources.''
.Ix 0 ref "dynamic source"
.Ix 0 ref source dynamic
Variables are good. Know them. Love them. Live them.
.LP
Debugging of makefiles is best accomplished using the
.B \-n ,
.B "\-d m" ,
and
.B "\-p 2"
flags.
.xH 2 Exercises
.ce
\s+4\fBTBA\fP\s0
.xH 1 Short-cuts and Other Nice Things
.LP
Based on what I've told you so far, you may have gotten the impression
that PMake is just a way of storing away commands and making sure you
don't forget to compile something. Good. That's just what it is.
However, the ways I've described have been inelegant, at best, and
painful, at worst.
This chapter contains things that make the
writing of makefiles easier and the makefiles themselves shorter and
easier to modify (and, occasionally, simpler). In this chapter, I
assume you are somewhat more
familiar with Sprite (or UNIX, if that's what you're using) than I did
in chapter 2, just so you're on your toes.
So without further ado...
.xH 2 Transformation Rules
.LP
As you know, a file's name consists of two parts: a base name, which
gives some hint as to the contents of the file, and a suffix, which
usually indicates the format of the file.
Over the years, as
.UX
has developed,
naming conventions, with regard to suffixes, have also developed that have
become almost as incontrovertible as Law. E.g. a file ending in
.CW .c
is assumed to contain C source code; one with a
.CW .o
suffix is assumed to be a compiled, relocatable object file that may
be linked into any program; a file with a
.CW .ms
suffix is usually a text file to be processed by Troff with the \-ms
macro package, and so on.
One of the best aspects of both Make and PMake comes from their
understanding of how the suffix of a file pertains to its contents and
their ability to do things with a file based solely on its suffix. This
ability comes from something known as a transformation rule. A
transformation rule specifies how to change a file with one suffix
into a file with another suffix.
.LP
A transformation rule looks much like a dependency line, except the
target is made of two known suffixes stuck together. Suffixes are made
known to PMake by placing them as sources on a dependency line whose
target is the special target
.CW .SUFFIXES .
E.g.
.DS
\&.SUFFIXES       : .o .c
\&.c.o            :
        $(CC) $(CFLAGS) -c $(.IMPSRC)
.DE
The creation script attached to the target is used to transform a file with
the first suffix (in this case,
.CW .c )
into a file with the second suffix (here,
.CW .o ).
In addition, the target inherits whatever attributes have been applied
to the transformation rule.
The simple rule given above says that to transform a C source file
into an object file, you compile it using
.CW cc
with the
.CW \-c
flag.
This rule is taken straight from the system makefile. Many
transformation rules (and suffixes) are defined there, and I refer you
to it for more examples (type
.CW "pmake -h" '' ``
to find out where it is).
.LP
There are several things to note about the transformation rule given
above:
.RS
.IP 1)
The
.CW .IMPSRC 
variable.
.Ix 0 def variable local .IMPSRC
.Ix 0 def .IMPSRC
This variable is set to the ``implied source'' (the file from which
the target is being created; the one with the first suffix), which, in this
case, is the .c file.
.IP 2)
The
.CW CFLAGS
variable. Almost all of the transformation rules in the system
makefile are set up using variables that you can alter in your
makefile to tailor the rule to your needs. In this case, if you want
all your C files to be compiled with the
.B \-g
flag, to provide information for
.CW dbx ,
you would set the
.CW CFLAGS
variable to contain
.CW -g
.CW "CFLAGS = -g" '') (``
and PMake would take care of the rest.
.RE
.LP
To give you a quick example, the makefile in 2.3.4 
.Rm 3 2.3.4
could be changed to this:
.DS
OBJS            = a.o b.o c.o
program         : $(OBJS)
        $(CC) -o $(.TARGET) $(.ALLSRC)
$(OBJS)         : defs.h
.DE
The transformation rule I gave above takes the place of the 6 lines\**
.FS
This is also somewhat cleaner, I think, than the dynamic source
solution presented in 2.6
.FE
.Rm 4 2.6
.DS
a.o             : a.c
        cc -c a.c
b.o             : b.c
        cc -c b.c
c.o             : c.c
        cc -c c.c
.DE
.LP
Now you may be wondering about the dependency between the
.CW .o
and
.CW .c
files \*- it's not mentioned anywhere in the new makefile. This is
because it isn't needed: one of the effects of applying a
transformation rule is the target comes to depend on the implied
source. That's why it's called the implied
.I source .
.LP
For a more detailed example. Say you have a makefile like this:
.DS
a.out           : a.o b.o
        $(CC) $(.ALLSRC)
.DE
and a directory set up like this:
.DS
total 4
-rw-rw-r--  1 deboor         34 Sep  7 00:43 Makefile
-rw-rw-r--  1 deboor        119 Oct  3 19:39 a.c
-rw-rw-r--  1 deboor        201 Sep  7 00:43 a.o
-rw-rw-r--  1 deboor         69 Sep  7 00:43 b.c
.DE
While just typing
.CW pmake '' ``
will do the right thing, it's much more informative to type
.CW "pmake -d s" ''. ``
This will show you what PMake is up to as it processes the files. In
this case, PMake prints the following:
.DS
Suff_FindDeps (a.out)
	using existing source a.o
	applying .o -> .out to "a.o"
Suff_FindDeps (a.o)
	trying a.c...got it
	applying .c -> .o to "a.c"
Suff_FindDeps (b.o)
	trying b.c...got it
	applying .c -> .o to "b.c"
Suff_FindDeps (a.c)
	trying a.y...not there
	trying a.l...not there
	trying a.c,v...not there
	trying a.y,v...not there
	trying a.l,v...not there
Suff_FindDeps (b.c)
	trying b.y...not there
	trying b.l...not there
	trying b.c,v...not there
	trying b.y,v...not there
	trying b.l,v...not there
--- a.o ---
cc  -c a.c
--- b.o ---
cc  -c b.c
--- a.out ---
cc a.o b.o
.DE
.LP
.CW Suff_FindDeps
is the name of a function in PMake that is called to check for implied
sources for a target using transformation rules.
The transformations it tries are, naturally
enough, limited to the ones that have been defined (a transformation
may be defined multiple times, by the way, but only the most recent
one will be used). You will notice, however, that there is a definite
order to the suffixes that are tried. This order is set by the
relative positions of the suffixes on the
.CW .SUFFIXES
line \*- the earlier a suffix appears, the earlier it is checked as
the source of a transformation. Once a suffix has been defined, the
only way to change its position in the pecking order is to remove all
the suffixes (by having a
.CW .SUFFIXES
dependency line with no sources) and redefine them in the order you
want. (Previously-defined transformation rules will be automatically
redefined as the suffixes they involve are re-entered.)
.LP
Another way to affect the search order is to make the dependency
explicit. In the above example,
.CW a.out
depends on
.CW a.o
and
.CW b.o .
Since a transformation exists from
.CW .o
to
.CW .out ,
PMake uses that, as indicated by the
.CW "using existing source a.o" '' ``
message.
.LP
The search for a transformation starts from the suffix of the target
and continues through all the defined transformations, in the order
dictated by the suffix ranking, until an existing file with the same
base (the target name minus the suffix and any leading directories) is
found. At that point, one or more transformation rules will have been
found to change the one existing file into the target.
.LP
For example, ignoring what's in the system makefile for now, say you
have a makefile like this:
.DS
\&.SUFFIXES       : .out .o .c .y .l
\&.l.c            :
        lex $(.IMPSRC)
        mv lex.yy.c $(.TARGET)
\&.y.c            :
        yacc $(.IMPSRC)
        mv y.tab.c $(.TARGET)
\&.c.o            :
        cc -c $(.IMPSRC)
\&.o.out          :
        cc -o $(.TARGET) $(.IMPSRC)
.DE
and the single file
.CW jive.l .
If you were to type
.CW "pmake -rd ms jive.out" ,'' ``
you would get the following output for
.CW jive.out :
.DS
Suff_FindDeps (jive.out)
	trying jive.o...not there
	trying jive.c...not there
	trying jive.y...not there
	trying jive.l...got it
	applying .l -> .c to "jive.l"
	applying .c -> .o to "jive.c"
	applying .o -> .out to "jive.o"
.DE
and this is why: PMake starts with the target
.CW jive.out ,
figures out its suffix
.CW .out ) (
and looks for things it can transform to a
.CW .out
file. In this case, it only finds
.CW .o ,
so it looks for the file
.CW jive.o .
It fails to find it, so it looks for transformations into a
.CW .o
file. Again it has only one choice:
.CW .c .
So it looks for
.CW jive.c
and, as you know, fails to find it. At this point it has two choices:
it can create the
.CW .c
file from either a
.CW .y
file or a
.CW .l
file. Since
.CW .y
came first on the
.CW .SUFFIXES
line, it checks for
.CW jive.y
first, but can't find it, so it looks for
.CW jive.l
and, lo and behold, there it is.
At this point, it has defined a transformation path as follows:
.CW .l
\(->
.CW .c
\(->
.CW .o
\(->
.CW .out
and applies the transformation rules accordingly. For completeness,
and to give you a better idea of what PMake actually did with this
three-step transformation, this is what PMake printed for the rest of
the process:
.DS
Suff_FindDeps (jive.o)
	using existing source jive.c
	applying .c -> .o to "jive.c"
Suff_FindDeps (jive.c)
	using existing source jive.l
	applying .l -> .c to "jive.l"
Suff_FindDeps (jive.l)
Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
Examining jive.c...non-existent...out-of-date
--- jive.c ---
lex jive.l
\&.\|.\|. meaningless lex output deleted .\|.\|.
mv lex.yy.c jive.c
Examining jive.o...non-existent...out-of-date
--- jive.o ---
cc -c jive.c
Examining jive.out...non-existent...out-of-date
--- jive.out ---
cc -o jive.out jive.o
.DE
.LP
One final question remains: what does PMake do with targets that have
no known suffix? PMake simply pretends it actually has a known suffix
and searches for transformations accordingly.
The suffix it chooses is the source for the
.CW .NULL
.Ix 0 ref .NULL
target mentioned later. In the system makefile, 
.CW .out
is chosen as the ``null suffix''
.Ix 0 def suffix null
.Ix 0 def "null suffix"
because most people use PMake to create programs. You are, however,
free and welcome to change it to a suffix of your own choosing.
The null suffix is ignored, however, when PMake is in compatibility
mode (see chapter 4).
.xH 2 Including Other Makefiles
.Ix 0 def makefile inclusion
.Rd 2
.LP
Just as for programs, it is often useful to extract certain parts of a
makefile into another file and just include it in other makefiles
somehow. Many compilers allow you say something like
.DS
#include "defs.h"
.DE
to include the contents of
.CW defs.h
in the source file. PMake allows you to do the same thing for
makefiles, with the added ability to use variables in the filenames.
An include directive in a makefile looks either like this:
.DS
#include <file>
.DE
or this
.DS
#include "file"
.DE
The difference between the two is where PMake searches for the file:
the first way, PMake will look for
the file only in the system makefile directory (or directories)
(to find out what that directory is, give PMake the
.B \-h
flag).
.Ix 0 ref flags -h
The system makefile directory search path can be overridden via the
.B \-m
option.
.Ix 0 ref flags -m
For files in double-quotes, the search is more complex:
.RS
.IP 1)
The directory of the makefile that's including the file.
.IP 2)
The current directory (the one in which you invoked PMake).
.IP 3)
The directories given by you using
.B \-I
flags, in the order in which you gave them.
.IP 4)
Directories given by
.CW .PATH
dependency lines (see chapter 4).
.IP 5)
The system makefile directory.
.RE
.LP
in that order.
.LP
You are free to use PMake variables in the filename\*-PMake will
expand them before searching for the file. You must specify the
searching method with either angle brackets or double-quotes
.I outside
of a variable expansion. I.e. the following
.DS
SYSTEM	= <command.mk>

#include $(SYSTEM)
.DE
won't work.
.xH 2 Saving Commands
.LP
.Ix 0 def ...
There may come a time when you will want to save certain commands to
be executed when everything else is done. For instance: you're
making several different libraries at one time and you want to create the
members in parallel. Problem is,
.CW ranlib
is another one of those programs that can't be run more than once in
the same directory at the same time (each one creates a file called
.CW __.SYMDEF
into which it stuffs information for the linker to use. Two of them
running at once will overwrite each other's file and the result will
be garbage for both parties). You might want a way to save the ranlib
commands til the end so they can be run one after the other, thus
keeping them from trashing each other's file. PMake allows you to do
this by inserting an ellipsis (``.\|.\|.'') as a command between
commands to be run at once and those to be run later.
.LP
So for the
.CW ranlib
case above, you might do this:
.Rd 5
.DS
lib1.a          : $(LIB1OBJS)
        rm -f $(.TARGET)
        ar cr $(.TARGET) $(.ALLSRC)
        ...
        ranlib $(.TARGET)

lib2.a          : $(LIB2OBJS)
        rm -f $(.TARGET)
        ar cr $(.TARGET) $(.ALLSRC)
        ...
        ranlib $(.TARGET)
.DE
.Ix 0 ref variable local .TARGET
.Ix 0 ref variable local .ALLSRC
This would save both
.DS
ranlib $(.TARGET)
.DE
commands until the end, when they would run one after the other
(using the correct value for the
.CW .TARGET
variable, of course).
.LP
Commands saved in this manner are only executed if PMake manages to
re-create everything without an error.
.xH 2 Target Attributes
.LP
PMake allows you to give attributes to targets by means of special
sources. Like everything else PMake uses, these sources begin with a
period and are made up of all upper-case letters. There are various
reasons for using them, and I will try to give examples for most of
them. Others you'll have to find uses for yourself. Think of it as ``an
exercise for the reader.'' By placing one (or more) of these as a source on a
dependency line, you are ``marking the target(s) with that
attribute.'' That's just the way I phrase it, so you know.
.LP
Any attributes given as sources for a transformation rule are applied
to the target of the transformation rule when the rule is applied.
.Ix 0 def attributes
.Ix 0 ref source
.Ix 0 ref target
.nr pw 12
.IP .DONTCARE \n(pw
.Ix 0 def attributes .DONTCARE
.Ix 0 def .DONTCARE
If a target is marked with this attribute and PMake can't figure out
how to create it, it will ignore this fact and assume the file isn't
really needed or actually exists and PMake just can't find it. This may prove
wrong, but the error will be noted later on, not when PMake tries to create
the target so marked. This attribute also prevents PMake from
attempting to touch the target if it is given the
.B \-t
flag.
.Ix 0 ref flags -t
.IP .EXEC \n(pw
.Ix 0 def attributes .EXEC
.Ix 0 def .EXEC
This attribute causes its shell script to be executed while having no
effect on targets that depend on it. This makes the target into a sort
of subroutine.  An example. Say you have some LISP files that need to
be compiled and loaded into a LISP process. To do this, you echo LISP
commands into a file and execute a LISP with this file as its input
when everything's done. Say also that you have to load other files
from another system before you can compile your files and further,
that you don't want to go through the loading and dumping unless one
of
.I your
files has changed. Your makefile might look a little bit
like this (remember, this is an educational example, and don't worry
about the
.CW COMPILE
rule, all will soon become clear, grasshopper):
.DS
system          : init a.fasl b.fasl c.fasl
        for i in $(.ALLSRC);
        do
                echo -n '(load "' >> input
                echo -n ${i} >> input
                echo '")' >> input
        done
        echo '(dump "$(.TARGET)")' >> input
        lisp < input

a.fasl          : a.l init COMPILE
b.fasl          : b.l init COMPILE
c.fasl          : c.l init COMPILE
COMPILE         : .USE
        echo '(compile "$(.ALLSRC)")' >> input
init            : .EXEC
        echo '(load-system)' > input
.DE
.Ix 0 ref .USE
.Ix 0 ref attributes .USE
.Ix 0 ref variable local .ALLSRC
.IP "\&"
.CW .EXEC
sources, don't appear in the local variables of targets that depend on
them (nor are they touched if PMake is given the
.B \-t
flag).
.Ix 0 ref flags -t
Note that all the rules, not just that for
.CW system ,
include
.CW init
as a source. This is because none of the other targets can be made
until
.CW init
has been made, thus they depend on it.
.IP .EXPORT \n(pw
.Ix 0 def attributes .EXPORT
.Ix 0 def .EXPORT
This is used to mark those targets whose creation should be sent to
another machine if at all possible. This may be used by some
exportation schemes if the exportation is expensive. You should ask
your system administrator if it is necessary.
.IP .EXPORTSAME \n(pw
.Ix 0 def attributes .EXPORTSAME
.Ix 0 def .EXPORTSAME
Tells the export system that the job should be exported to a machine
of the same architecture as the current one. Certain operations (e.g.
running text through
.CW nroff )
can be performed the same on any architecture (CPU and
operating system type), while others (e.g. compiling a program with
.CW cc )
must be performed on a machine with the same architecture. Not all
export systems will support this attribute.
.IP .IGNORE \n(pw
.Ix 0 def attributes .IGNORE
.Ix 0 def .IGNORE attribute
Giving a target the
.CW .IGNORE
attribute causes PMake to ignore errors from any of the target's commands, as
if they all had `\-' before them.
.IP .INVISIBLE \n(pw
.Ix 0 def attributes .INVISIBLE
.Ix 0 def .INVISIBLE
This allows you to specify one target as a source for another without
the one affecting the other's local variables. Useful if, say, you
have a makefile that creates two programs, one of which is used to
create the other, so it must exist before the other is created. You
could say
.DS
prog1           : $(PROG1OBJS) prog2 MAKEINSTALL
prog2           : $(PROG2OBJS) .INVISIBLE MAKEINSTALL
.DE
where
.CW MAKEINSTALL
is some complex .USE rule (see below) that depends on the
.Ix 0 ref .USE
.CW .ALLSRC
variable containing the right things. Without the
.CW .INVISIBLE
attribute for
.CW prog2 ,
the
.CW MAKEINSTALL
rule couldn't be applied. This is not as useful as it should be, and
the semantics may change (or the whole thing go away) in the
not-too-distant future.
.IP .JOIN \n(pw
.Ix 0 def attributes .JOIN
.Ix 0 def .JOIN
This is another way to avoid performing some operations in parallel
while permitting everything else to be done so. Specifically it
forces the target's shell script to be executed only if one or more of the
sources was out-of-date. In addition, the target's name,
in both its
.CW .TARGET
variable and all the local variables of any target that depends on it,
is replaced by the value of its
.CW .ALLSRC
variable.
As an example, suppose you have a program that has four libraries that
compile in the same directory along with, and at the same time as, the
program. You again have the problem with
.CW ranlib
that I mentioned earlier, only this time it's more severe: you
can't just put the ranlib off to the end since the program
will need those libraries before it can be re-created. You can do
something like this:
.DS
program         : $(OBJS) libraries
        cc -o $(.TARGET) $(.ALLSRC)

libraries       : lib1.a lib2.a lib3.a lib4.a .JOIN
        ranlib $(.OODATE)
.DE
.Ix 0 ref variable local .TARGET
.Ix 0 ref variable local .ALLSRC
.Ix 0 ref variable local .OODATE
.Ix 0 ref .TARGET
.Ix 0 ref .ALLSRC
.Ix 0 ref .OODATE
In this case, PMake will re-create the
.CW $(OBJS)
as necessary, along with
.CW lib1.a ,
.CW lib2.a ,
.CW lib3.a
and
.CW lib4.a .
It will then execute
.CW ranlib
on any library that was changed and set
.CW program 's
.CW .ALLSRC
variable to contain what's in
.CW $(OBJS)
followed by
.CW "lib1.a lib2.a lib3.a lib4.a" .'' ``
In case you're wondering, it's called
.CW .JOIN
because it joins together different threads of the ``input graph'' at
the target marked with the attribute.
Another aspect of the .JOIN attribute is it keeps the target from
being created if the
.B \-t
flag was given.
.Ix 0 ref flags -t
.IP .MAKE \n(pw
.Ix 0 def attributes .MAKE
.Ix 0 def .MAKE
The
.CW .MAKE
attribute marks its target as being a recursive invocation of PMake.
This forces PMake to execute the script associated with the target (if
it's out-of-date) even if you gave the
.B \-n
or
.B \-t
flag. By doing this, you can start at the top of a system and type
.DS
pmake -n
.DE
and have it descend the directory tree (if your makefiles are set up
correctly), printing what it would have executed if you hadn't
included the
.B \-n
flag.
.IP .NOEXPORT \n(pw
.Ix 0 def attributes .NOEXPORT
.Ix 0 def .NOEXPORT attribute
If possible, PMake will attempt to export the creation of all targets to
another machine (this depends on how PMake was configured). Sometimes,
the creation is so simple, it is pointless to send it to another
machine. If you give the target the
.CW .NOEXPORT
attribute, it will be run locally, even if you've given PMake the
.B "\-L 0"
flag.
.IP .NOTMAIN \n(pw
.Ix 0 def attributes .NOTMAIN
.Ix 0 def .NOTMAIN
Normally, if you do not specify a target to make in any other way,
PMake will take the first target on the first dependency line of a
makefile as the target to create. That target is known as the ``Main
Target'' and is labeled as such if you print the dependencies out
using the
.B \-p
flag.
.Ix 0 ref flags -p
Giving a target this attribute tells PMake that the target is
definitely
.I not
the Main Target.
This allows you to place targets in an included makefile and
have PMake create something else by default.
.IP .PRECIOUS \n(pw
.Ix 0 def attributes .PRECIOUS
.Ix 0 def .PRECIOUS attribute
When PMake is interrupted (you type control-C at the keyboard), it
will attempt to clean up after itself by removing any half-made
targets. If a target has the
.CW .PRECIOUS
attribute, however, PMake will leave it alone. An additional side
effect of the `::' operator is to mark the targets as
.CW .PRECIOUS .
.Ix 0 ref operator double-colon
.Ix 0 ref ::
.IP .SILENT \n(pw
.Ix 0 def attributes .SILENT
.Ix 0 def .SILENT attribute
Marking a target with this attribute keeps its commands from being
printed when they're executed, just as if they had an `@' in front of them.
.IP .USE \n(pw
.Ix 0 def attributes .USE
.Ix 0 def .USE
By giving a target this attribute, you turn it into PMake's equivalent
of a macro. When the target is used as a source for another target,
the other target acquires the commands, sources and attributes (except
.CW .USE )
of the source.
If the target already has commands, the
.CW .USE
target's commands are added to the end. If more than one .USE-marked
source is given to a target, the rules are applied sequentially.
.IP "\&" \n(pw
The typical .USE rule (as I call them) will use the sources of the
target to which it is applied (as stored in the
.CW .ALLSRC
variable for the target) as its ``arguments,'' if you will.
For example, you probably noticed that the commands for creating
.CW lib1.a
and
.CW lib2.a
in the example in section 3.3
.Rm 5 3.3
were exactly the same. You can use the
.CW .USE
attribute to eliminate the repetition, like so:
.DS
lib1.a          : $(LIB1OBJS) MAKELIB
lib2.a          : $(LIB2OBJS) MAKELIB

MAKELIB         : .USE
        rm -f $(.TARGET)
        ar cr $(.TARGET) $(.ALLSRC)
        ...
        ranlib $(.TARGET)
.DE
.Ix 0 ref variable local .TARGET
.Ix 0 ref variable local .ALLSRC
.IP "\&" \n(pw
Several system makefiles (not to be confused with The System Makefile)
make use of these  .USE rules to make your
life easier (they're in the default, system makefile directory...take a look).
Note that the .USE rule source itself
.CW MAKELIB ) (
does not appear in any of the targets's local variables.
There is no limit to the number of times I could use the
.CW MAKELIB
rule. If there were more libraries, I could continue with
.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' ``
and so on and so forth.
.xH 2 Special Targets
.LP
As there were in Make, so there are certain targets that have special
meaning to PMake. When you use one on a dependency line, it is the
only target that may appear on the left-hand-side of the operator.
.Ix 0 ref target
.Ix 0 ref operator
As for the attributes and variables, all the special targets
begin with a period and consist of upper-case letters only.
I won't describe them all in detail because some of them are rather
complex and I'll describe them in more detail than you'll want in
chapter 4.
The targets are as follows:
.nr pw 10
.IP .BEGIN \n(pw
.Ix 0 def .BEGIN
Any commands attached to this target are executed before anything else
is done. You can use it for any initialization that needs doing.
.IP .DEFAULT \n(pw
.Ix 0 def .DEFAULT
This is sort of a .USE rule for any target (that was used only as a
source) that PMake can't figure out any other way to create. It's only
``sort of'' a .USE rule because only the shell script attached to the
.CW .DEFAULT
target is used. The
.CW .IMPSRC
variable of a target that inherits
.CW .DEFAULT 's
commands is set to the target's own name.
.Ix 0 ref .IMPSRC
.Ix 0 ref variable local .IMPSRC
.IP .END \n(pw
.Ix 0 def .END
This serves a function similar to
.CW .BEGIN ,
in that commands attached to it are executed once everything has been
re-created (so long as no errors occurred). It also serves the extra
function of being a place on which PMake can hang commands you put off
to the end. Thus the script for this target will be executed before
any of the commands you save with the ``.\|.\|.''.
.Ix 0 ref ...
.IP .EXPORT \n(pw
The sources for this target are passed to the exportation system compiled
into PMake. Some systems will use these sources to configure
themselves. You should ask your system administrator about this.
.IP .IGNORE \n(pw
.Ix 0 def .IGNORE target
.Ix 0 ref .IGNORE attribute
.Ix 0 ref attributes .IGNORE
This target marks each of its sources with the
.CW .IGNORE
attribute. If you don't give it any sources, then it is like
giving the
.B \-i
flag when you invoke PMake \*- errors are ignored for all commands.
.Ix 0 ref flags -i
.IP .INCLUDES \n(pw
.Ix 0 def .INCLUDES target
.Ix 0 def variable global .INCLUDES
.Ix 0 def .INCLUDES variable
The sources for this target are taken to be suffixes that indicate a
file that can be included in a program source file.
The suffix must have already been declared with
.CW .SUFFIXES
(see below).
Any suffix so marked will have the directories on its search path
(see
.CW .PATH ,
below) placed in the
.CW .INCLUDES
variable, each preceded by a
.B \-I
flag. This variable can then be used as an argument for the compiler
in the normal fashion. The
.CW .h
suffix is already marked in this way in the system makefile.
.Ix 0 ref makefilesystem
E.g. if you have
.DS
\&.SUFFIXES       : .bitmap
\&.PATH.bitmap    : /usr/local/X/lib/bitmaps
\&.INCLUDES       : .bitmap
.DE
PMake will place
.CW "-I/usr/local/X/lib/bitmaps" '' ``
in the
.CW .INCLUDES
variable and you can then say
.DS
cc $(.INCLUDES) -c xprogram.c
.DE
(Note: the
.CW .INCLUDES
variable is not actually filled in until the entire makefile has been read.)
.IP .INTERRUPT \n(pw
.Ix 0 def .INTERRUPT
When PMake is interrupted,
it will execute the commands in the script for this target, if it
exists.
.IP .LIBS \n(pw
.Ix 0 def .LIBS target
.Ix 0 def .LIBS variable
.Ix 0 def variable global .LIBS
This does for libraries what
.CW .INCLUDES
does for include files, except the flag used is
.B \-L ,
as required by those linkers that allow you to tell them where to find
libraries. The variable used is
.CW .LIBS .
Be forewarned that PMake may not have been compiled to do this if the
linker on your system doesn't accept the
.B \-L
flag, though the
.CW .LIBS
variable will always be defined once the makefile has been read.
.IP .MAIN \n(pw
.Ix 0 def .MAIN
If you didn't give a target (or targets) to create when you invoked
PMake, it will take the sources of this target as the targets to
create.
.IP .MAKEFLAGS \n(pw
.Ix 0 def .MAKEFLAGS target
This target provides a way for you to always specify flags for PMake
when the makefile is used. The flags are just as they would be typed
to the shell (except you can't use shell variables unless they're in
the environment),
though the
.B \-f
and
.B \-r
flags have no effect.
.IP .NULL \n(pw
.Ix 0 def .NULL
.Ix 0 ref suffix null
.Ix 0 ref "null suffix"
This allows you to specify what suffix PMake should pretend a file has
if, in fact, it has no known suffix. Only one suffix may be so
designated. The last source on the dependency line is the suffix that
is used (you should, however, only give one suffix.\|.\|.).
.IP .PATH \n(pw
.Ix 0 def .PATH
If you give sources for this target, PMake will take them as
directories in which to search for files it cannot find in the current
directory. If you give no sources, it will clear out any directories
added to the search path before. Since the effects of this all get
very complex, I'll leave it til chapter four to give you a complete
explanation.
.IP .PATH\fIsuffix\fP \n(pw
.Ix 0 ref .PATH
This does a similar thing to
.CW .PATH ,
but it does it only for files with the given suffix. The suffix must
have been defined already. Look at
.B "Search Paths"
(section 4.1)
.Rm 6 4.1
for more information.
.IP .PRECIOUS \n(pw
.Ix 0 def .PRECIOUS target
.Ix 0 ref .PRECIOUS attribute
.Ix 0 ref attributes .PRECIOUS
Similar to
.CW .IGNORE ,
this gives the
.CW .PRECIOUS
attribute to each source on the dependency line, unless there are no
sources, in which case the
.CW .PRECIOUS
attribute is given to every target in the file.
.IP .RECURSIVE \n(pw
.Ix 0 def .RECURSIVE
.Ix 0 ref attributes .MAKE
.Ix 0 ref .MAKE
This target applies the
.CW .MAKE
attribute to all its sources. It does nothing if you don't give it any sources.
.IP .SHELL \n(pw
.Ix 0 def .SHELL
PMake is not constrained to only using the Bourne shell to execute
the commands you put in the makefile. You can tell it some other shell
to use with this target. Check out
.B "A Shell is a Shell is a Shell"
(section 4.4)
.Rm 7 4.4
for more information.
.IP .SILENT \n(pw
.Ix 0 def .SILENT target
.Ix 0 ref .SILENT attribute
.Ix 0 ref attributes .SILENT
When you use
.CW .SILENT
as a target, it applies the
.CW .SILENT
attribute to each of its sources. If there are no sources on the
dependency line, then it is as if you gave PMake the
.B \-s
flag and no commands will be echoed.
.IP .SUFFIXES \n(pw
.Ix 0 def .SUFFIXES
This is used to give new file suffixes for PMake to handle. Each
source is a suffix PMake should recognize. If you give a
.CW .SUFFIXES
dependency line with no sources, PMake will forget about all the
suffixes it knew (this also nukes the null suffix).
For those targets that need to have suffixes defined, this is how you do it.
.LP
In addition to these targets, a line of the form
.DS
\fIattribute\fP : \fIsources\fP
.DE
applies the
.I attribute
to all the targets listed as
.I sources .
.xH 2 Modifying Variable Expansion
.LP
.Ix 0 def variable expansion modified
.Ix 0 ref variable expansion
.Ix 0 def variable modifiers
Variables need not always be expanded verbatim. PMake defines several
modifiers that may be applied to a variable's value before it is
expanded. You apply a modifier by placing it after the variable name
with a colon between the two, like so:
.DS
${\fIVARIABLE\fP:\fImodifier\fP}
.DE
Each modifier is a single character followed by something specific to
the modifier itself.
You may apply as many modifiers as you want \*- each one is applied to
the result of the previous and is separated from the previous by
another colon.
.LP
There are seven ways to modify a variable's expansion, most of which
come from the C shell variable modification characters:
.RS
.IP "M\fIpattern\fP"
.Ix 0 def :M
.Ix 0 def modifier match
This is used to select only those words (a word is a series of
characters that are neither spaces nor tabs) that match the given
.I pattern .
The pattern is a wildcard pattern like that used by the shell, where
.CW *
means 0 or more characters of any sort;
.CW ?
is any single character;
.CW [abcd]
matches any single character that is either `a', `b', `c' or `d'
(there may be any number of characters between the brackets);
.CW [0-9]
matches any single character that is between `0' and `9' (i.e. any
digit. This form may be freely mixed with the other bracket form), and
`\\' is used to escape any of the characters `*', `?', `[' or `:',
leaving them as regular characters to match themselves in a word.
For example, the system makefile
.CW <makedepend.mk>
uses
.CW "$(CFLAGS:M-[ID]*)" '' ``
to extract all the
.CW \-I
and
.CW \-D
flags that would be passed to the C compiler. This allows it to
properly locate include files and generate the correct dependencies.
.IP "N\fIpattern\fP"
.Ix 0 def :N
.Ix 0 def modifier nomatch
This is identical to
.CW :M
except it substitutes all words that don't match the given pattern.
.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]"
.Ix 0 def :S
.Ix 0 def modifier substitute
Causes the first occurrence of
.I search-string
in the variable to be replaced by
.I replacement-string ,
unless the
.CW g
flag is given at the end, in which case all occurrences of the string
are replaced. The substitution is performed on each word in the
variable in turn. If 
.I search-string
begins with a
.CW ^ ,
the string must match starting at the beginning of the word. If
.I search-string
ends with a
.CW $ ,
the string must match to the end of the word (these two may be
combined to force an exact match). If a backslash precedes these two
characters, however, they lose their special meaning. Variable
expansion also occurs in the normal fashion inside both the
.I search-string
and the
.I replacement-string ,
.B except
that a backslash is used to prevent the expansion of a
.CW $ ,
not another dollar sign, as is usual.
Note that
.I search-string
is just a string, not a pattern, so none of the usual
regular-expression/wildcard characters have any special meaning save
.CW ^
and
.CW $ .
In the replacement string,
the
.CW &
character is replaced by the
.I search-string
unless it is preceded by a backslash.
You are allowed to use any character except
colon or exclamation point to separate the two strings. This so-called
delimiter character may be placed in either string by preceding it
with a backslash.
.IP T
.Ix 0 def :T
.Ix 0 def modifier tail
Replaces each word in the variable expansion by its last
component (its ``tail''). For example, given
.DS
OBJS = ../lib/a.o b /usr/lib/libm.a
TAILS = $(OBJS:T)
.DE
the variable
.CW TAILS
would expand to
.CW "a.o b libm.a" .'' ``
.IP H
.Ix 0 def :H
.Ix 0 def modifier head
This is similar to
.CW :T ,
except that every word is replaced by everything but the tail (the
``head''). Using the same definition of
.CW OBJS ,
the string
.CW "$(OBJS:H)" '' ``
would expand to
.CW "../lib /usr/lib" .'' ``
Note that the final slash on the heads is removed and
anything without a head is replaced by the empty string.
.IP E
.Ix 0 def :E
.Ix 0 def modifier extension
.Ix 0 def modifier suffix
.Ix 0 ref suffix "variable modifier"
.CW :E
replaces each word by its suffix (``extension''). So
.CW "$(OBJS:E)" '' ``
would give you
.CW ".o .a" .'' ``
.IP R
.Ix 0 def :R
.Ix 0 def modifier root
.Ix 0 def modifier base
This replaces each word by everything but the suffix (the ``root'' of
the word).
.CW "$(OBJS:R)" '' ``
expands to ``
.CW "../lib/a b /usr/lib/libm" .''
.RE
.LP
In addition, the System V style of substitution is also supported.
This looks like:
.DS
$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP)
.DE
It must be the last modifier in the chain. The search is anchored at
the end of each word, so only suffixes or whole words may be replaced.
.xH 2 More on Debugging
.xH 2 More Exercises
.IP (3.1)
You've got a set programs, each of which is created from its own
assembly-language source file (suffix
.CW .asm ).
Each program can be assembled into two versions, one with error-checking
code assembled in and one without. You could assemble them into files
with different suffixes
.CW .eobj \& (
and
.CW .obj ,
for instance), but your linker only understands files that end in
.CW .obj .
To top it all off, the final executables
.I must
have the suffix
.CW .exe .
How can you still use transformation rules to make your life easier
(Hint: assume the error-checking versions have
.CW ec
tacked onto their prefix)?
.IP (3.2)
Assume, for a moment or two, you want to perform a sort of
``indirection'' by placing the name of a variable into another one,
then you want to get the value of the first by expanding the second
somehow. Unfortunately, PMake doesn't allow constructs like
.DS I
$($(FOO))
.DE
What do you do? Hint: no further variable expansion is performed after
modifiers are applied, thus if you cause a $ to occur in the
expansion, that's what will be in the result.
.xH 1 PMake for Gods
.LP
This chapter is devoted to those facilities in PMake that allow you to
do a great deal in a makefile with very little work, as well as do
some things you couldn't do in Make 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.
.LP
Once more, I assume a greater familiarity with
.UX
or Sprite than I did in the previous two chapters.
.xH 2 Search Paths
.Rd 6
.LP
PMake supports the dispersal of files into multiple directories by
allowing you to specify places to look for sources with
.CW .PATH
targets in the makefile. The directories you give as sources for these
targets make up a ``search path.'' 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.
.LP
There are two types of search paths
in PMake: one is used for all types of files (including included
makefiles) and is specified with a plain
.CW .PATH
target (e.g.
.CW ".PATH : RCS" ''), ``
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
.CW .PATH
with the suffix of the file. For instance
.DS
\&.PATH.h         : /sprite/lib/include /sprite/att/lib/include
.DE
would tell PMake to look in the directories
.CW /sprite/lib/include
and
.CW /sprite/att/lib/include
for any files whose suffix is
.CW .h .
.LP
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.
.LP
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.
.LP
When a file is found in some directory other than the current one, all
local variables that would have contained the target's name
.CW .ALLSRC , (
and
.CW .IMPSRC )
will instead contain the path to the file, as found by PMake.
Thus if you have a file
.CW ../lib/mumble.c
and a makefile
.DS
\&.PATH.c         : ../lib
mumble          : mumble.c
        $(CC) -o $(.TARGET) $(.ALLSRC)
.DE
the command executed to create
.CW mumble
would be
.CW "cc -o mumble ../lib/mumble.c" .'' ``
(As an aside, the command in this case isn't strictly necessary, since
it will be found using transformation rules if it isn't given. This is because
.CW .out
is the null suffix by default and a transformation exists from
.CW .c
to
.CW .out .
Just thought I'd throw that in.)
.LP
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 PMake 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.
.LP
Something you should know about the way search paths are implemented
is that each directory is read, and its contents cached, exactly once
\&\*- when it is first encountered \*- so any changes to the
directories while PMake is running will not be noted when searching
for implicit sources, nor will they be found when PMake attempts to
discover when the file was last modified, unless the file was created in the
current directory. While people have suggested that PMake 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 PMake's attempts to apply transformation
rules through non-existent files \*- the number of extra file-system
searches is truly staggering, especially if many files without
suffixes are used and the null suffix isn't changed from
.CW .out .
.xH 2 Archives and Libraries
.LP
.UX
and Sprite allow you to merge files into an archive using the
.CW ar
command. Further, if the files are relocatable object files, you can
run
.CW ranlib
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's one copy in
the archive and one copy out by itself. The problem with libraries is
you usually think of them as
.CW -lm
rather than
.CW /usr/lib/libm.a
and the linker thinks they're out-of-date if you so much as look at
them.
.LP
PMake 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, PMake adds an additional way of deciding if a library
is out-of-date:
.IP \(bu 2
If the table of contents is older than the library, or is missing, the
library is out-of-date.
.LP
A library is any target that looks like
.CW \-l name'' ``
or that ends in a suffix that was marked as a library using the
.CW .LIBS
target.
.CW .a
is so marked in the system makefile.
.LP
Members of an archive are specified as
``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''.
Thus
.CW libdix.a(window.o) '' ``'
specifies the file
.CW window.o
in the archive
.CW libdix.a .
You may also use wildcards to specify the members of the archive. Just
remember that most the wildcard characters will only find 
.I existing
files.
.LP
A file that is a member of an archive is treated specially. If the
file doesn't 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 \*- the
\fIarchive\fP(\fImember\fP) target), special care is
taken with the transformation rules, as follows:
.IP \(bu 2
\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP.
.IP \(bu 2
The transformation from the \fImember\fP's suffix to the
\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target.
.IP \(bu 2
The \fIarchive\fP(\fImember\fP)'s
.CW .TARGET
variable is set to the name of the \fImember\fP if \fImember\fP is
actually a target, or the path to the member file if \fImember\fP is
only a source.
.IP \(bu 2
The
.CW .ARCHIVE
variable for the \fIarchive\fP(\fImember\fP) target is set to the name
of the \fIarchive\fP.
.Ix 0 def variable local .ARCHIVE
.Ix 0 def .ARCHIVE
.IP \(bu 2
The
.CW .MEMBER
variable is set to the actual string inside the parentheses. In most
cases, this will be the same as the
.CW .TARGET
variable.
.Ix 0 def variable local .MEMBER
.Ix 0 def .MEMBER
.IP \(bu 2
The \fIarchive\fP(\fImember\fP)'s place in the local variables of the
targets that depend on it is taken by the value of its
.CW .TARGET
variable.
.LP
Thus, a program library could be created with the following makefile:
.DS
\&.o.a            :
        ...
        rm -f $(.TARGET:T)
OBJS            = obj1.o obj2.o obj3.o
libprog.a       : libprog.a($(OBJS))
        ar cru $(.TARGET) $(.OODATE)
        ranlib $(.TARGET)
.DE
This will cause the three object files to be compiled (if the
corresponding source files were modified after the object file or, if
that doesn't exist, the archived object file), the out-of-date ones
archived in
.CW libprog.a ,
a table of contents placed in the archive and the newly-archived
object files to be removed.
.LP
All this is used in the 
.CW makelib.mk
system makefile to create a single library with ease. This makefile
looks like this:
.DS
.SM
#
# 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 = <files in the library>
#
# fish.a: fish.a($(OBJECTS)) MAKELIB
#
#

#ifndef _MAKELIB_MK
_MAKELIB_MK	=

#include	<po.mk>

\&.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
.DE
.xH 2 On the Condition...
.Rd 1
.LP
Like the C compiler before it, PMake allows you to configure the makefile,
based on the current environment, using conditional statements. A
conditional looks like this:
.DS
#if \fIboolean expression\fP
\fIlines\fP
#elif \fIanother boolean expression\fP
\fImore lines\fP
#else
\fIstill more lines\fP
#endif
.DE
They may be nested to a maximum depth of 30 and may occur anywhere
(except in a comment, of course). The
.CW # '' ``
must the very first character on the line.
.LP
Each
.I "boolean expression"
is made up of terms that look like function calls, the standard C
boolean operators
.CW && ,
.CW || ,
and
.CW ! ,
and the standard relational operators
.CW == ,
.CW != ,
.CW > ,
.CW >= ,
.CW < ,
and
.CW <= ,
with
.CW ==
and
.CW !=
being overloaded to allow string comparisons as well.
.CW &&
represents logical AND;
.CW ||
is logical OR and
.CW !
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:
.nr pw 9
.Ix 0 def make
.Ix 0 def conditional make
.Ix 0 def if make
.IP make \n(pw
The syntax is
.CW make( \fItarget\fP\c
.CW )
where
.I 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
.CW .MAIN
target (note that the sources for
.CW .MAIN
are only used if no targets were given on the command line).
.IP defined \n(pw
.Ix 0 def defined
.Ix 0 def conditional defined
.Ix 0 def if defined
The syntax is
.CW defined( \fIvariable\fP\c
.CW )
and is true if
.I variable
is defined. Certain variables are defined in the system makefile that
identify the system on which PMake is being run.
.IP exists \n(pw
.Ix 0 def exists
.Ix 0 def conditional exists
.Ix 0 def if exists
The syntax is
.CW exists( \fIfile\fP\c
.CW )
and is true if the file can be found on the global search path (i.e.
that defined by
.CW .PATH
targets, not by
.CW .PATH \fIsuffix\fP
targets).
.IP empty \n(pw
.Ix 0 def empty
.Ix 0 def conditional empty
.Ix 0 def if empty
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 (NOTE: 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
.CW !defined( \fIvar\fP\c ``
.CW ") || empty(" \fIvar\fP\c
.CW ) ''
as the definition of
.CW ||
will prevent the
.CW empty()
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:
.DS
#if !empty(\fIvar\fP:M\fIword\fP)
.DE
.LP
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.
.DS
#if $(OS) == 4.3

#if $(MACHINE) == "sun3"

#if $(LOAD_ADDR) < 0xc000
.DE
are all valid conditionals. In addition, the numeric value of a
variable can be tested as a boolean as follows:
.DS
#if $(LOAD)
.DE
would see if
.CW LOAD
contains a non-zero value and
.DS
#if !$(LOAD)
.DE
would test if
.CW LOAD
contains a zero value.
.LP
In addition to the bare
.CW #if ,'' ``
there are other forms that apply one of the first two functions to each
term. They are as follows:
.DS
	ifdef	\fRdefined\fP
	ifndef	\fR!defined\fP
	ifmake	\fRmake\fP
	ifnmake	\fR!make\fP
.DE
There are also the ``else if'' forms:
.CW elif ,
.CW elifdef ,
.CW elifndef ,
.CW elifmake ,
and
.CW elifnmake .
.LP
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
.CW \-g
flag for the compilation, while the other uses the
.CW \-O
flag, or you can use another target (call it
.CW 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
.CW DEBUG
(say with
.CW "pmake -D DEBUG" )
will also cause the debug version to be made.
.DS
#if defined(DEBUG) || make(debug)
CFLAGS		+= -g
#else
CFLAGS		+= -O
#endif
.DE
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 ``conflict'' because of some conditionals in the
makefile. For instance
.DS
#if make(print)
FORMATTER	= ditroff -Plaser_printer
#endif
#if make(draft)
FORMATTER	= nroff -Pdot_matrix_printer
#endif
.DE
would wreak havoc if you tried
.CW "pmake draft print" '' ``
since you would use the same formatter for each target. As I said,
this all gets somewhat complicated.
.xH 2 A Shell is a Shell is a Shell
.Rd 7
.LP
In normal operation, the Bourne Shell (better known as
.CW sh '') ``
is used to execute the commands to re-create targets. PMake also allows you
to specify a different shell for it to use when executing these
commands. There are several things PMake must know about the shell you
wish to use. These things are specified as the sources for the
.CW .SHELL
.Ix 0 ref .SHELL
.Ix 0 ref target .SHELL
target by keyword, as follows:
.IP "\fBpath=\fP\fIpath\fP"
PMake needs to know where the shell actually resides, so it can
execute it. If you specify this and nothing else, PMake 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 Bourne or C Shell (yes, PMake knows
how to use the C Shell too).
.IP "\fBname=\fP\fIname\fP"
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
.B path ),
it is the name by which PMake 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 Bourne Shell
.CW ".SHELL: name=csh" '' (``
will do it).
.IP "\fBquiet=\fP\fIecho-off command\fP"
As mentioned before, PMake 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
.B quiet
keyword is the command used to turn echoing off. Once it is turned
off, echoing is expected to remain off until the echo-on command is given.
.IP "\fBecho=\fP\fIecho-on command\fP"
The command PMake should give to turn echoing back on again.
.IP "\fBfilter=\fP\fIprinted echo-off command\fP"
Many shells will echo the echo-off command when it is given. This
keyword tells PMake in what format the shell actually prints the
echo-off command. Wherever PMake 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.
.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP"
Unless a target has been marked
.CW .SILENT ,
PMake 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 `\-', 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).
.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP"
Likewise, unless a target is marked
.CW .IGNORE ,
PMake 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 `\-' apply as for the
.B echoFlag .
.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP"
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 doesn't have error-control as commands, but I'll 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.
.IP "\fBignore=\fP\fIcommand to turn error checking off\fP"
This is the command PMake uses to turn error checking off. It has
another use if the shell doesn't do error-control, but I'll tell you
about that.\|.\|.\|now.
.IP "\fBhasErrCtl=\fP\fIyes or no\fP"
This takes a value that is either
.B yes
or
.B no .
Now you might think that the existence of the
.B check
and
.B ignore
keywords would be enough to tell PMake if the shell can do
error-control, but you'd be wrong. If
.B hasErrCtl
is
.B yes ,
PMake uses the check and ignore commands in a straight-forward manner.
If this is
.B no ,
however, their use is rather different. In this case, the check
command is used as a template, in which the string
.B %s
is replaced by the command that's 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
.B %s
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)
.CW \en '') (``
in the appropriate place(s).
.LP
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 (\en is newline, \er is return, \eb is
backspace, \e' escapes a single-quote inside single-quotes, \e"
escapes a double-quote inside double-quotes). Now for an example.
.LP
This is actually the contents of the
.CW <shx.mk>
system makefile, and causes PMake to use the Bourne Shell 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:
.DS
#
# 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 \e
	quiet="set -" \e
	echo="set -x" \e
	filter="+ set - " \e
	echoFlag=x \e
	errFlag=e \e
	hasErrCtl=yes \e
	check="set -e" \e
	ignore="set +e"
.DE
.LP
It tells PMake the following:
.Bp
The shell is located in the file
.CW /bin/sh .
It need not tell PMake that the name of the shell is
.CW sh 
as PMake can figure that out for itself (it's the last component of
the path).
.Bp
The command to stop echoing is
.CW "set -" .
.Bp
The command to start echoing is
.CW "set -x" .
.Bp
When the echo off command is executed, the shell will print
.CW "+ set - " 
(The `+' comes from using the
.CW \-x
flag (rather than the
.CW \-v
flag PMake usually uses)). PMake will remove all occurrences of this
string from the output, so you don't notice extra commands you didn't
put there.
.Bp
The flag the Bourne Shell will take to start echoing in this way is
the
.CW \-x
flag. The Bourne Shell will only take its flag arguments concatenated
as its first argument, so neither this nor the
.B errFlag
specification begins with a \-.
.Bp
The flag to use to turn error-checking on from the start is
.CW \-e .
.Bp
The shell can turn error-checking on and off, and the commands to do
so are
.CW "set +e"
and
.CW "set -e" ,
respectively.
.LP
I should note that this specification is for Bourne Shells that are
not part of Berkeley
.UX ,
as shells from Berkeley don't do error control. You can get a similar
effect, however, by changing the last three lines to be:
.DS
	hasErrCtl=no \e
	check="echo \e"+ %s\e"\en" \e
	ignore="sh -c '%s || exit 0\en"
.DE
.LP
This will cause PMake to execute the two commands
.DS
echo "+ \fIcmd\fP"
sh -c '\fIcmd\fP || true'
.DE
for each command for which errors are to be ignored. (In case you are
wondering, the thing for
.CW ignore
tells the shell to execute another shell without error checking on and
always exit 0, since the
.B ||
causes the
.CW "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's
the last command it executed).
.xH 2 Compatibility
.Ix 0 ref compatibility
.LP
There are three (well, 3 \(12) levels of backwards-compatibility built
into PMake.  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.
.B \-B
(one shell per command) implies
.B \-V )
The three levels are described in the following three sections.
.xH 3 DEFCON 3 \*- Variable Expansion
.Ix 0 ref compatibility
.LP
As noted before, PMake 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
.CW ${PRINTER} ''), (``
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.
.LP
You have two choices: change the makefile to define the variables
(their values can be overridden on the command line, since that's
where they would have been set if you used Make, anyway) or always give the
.B \-V
flag (this can be done with the
.CW .MAKEFLAGS
target, if you want).
.xH 3 DEFCON 2 \*- The Number of the Beast
.Ix 0 ref compatibility
.LP
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
.B \-B
flag forces PMake 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:
.DS
install :: .MAKE
	(cd src; $(.PMAKE) install)
	(cd lib; $(.PMAKE) install)
	(cd man; $(.PMAKE) install)
.DE
.Ix 0 ref operator double-colon
.Ix 0 ref variable global .PMAKE
.Ix 0 ref .PMAKE
.Ix 0 ref .MAKE
.Ix 0 ref attribute .MAKE
This will always execute the three makes (even if the
.B \-n
flag was given) because of the combination of the ``::'' operator and
the
.CW .MAKE
attribute. Each command will change to the proper directory to perform
the install, leaving the main shell in the directory in which it started.
.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery"
.Ix 0 ref compatibility
.LP
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 PMake. If you are the conservative sort, this is
the compatibility mode for you. It is entered either by giving PMake
the
.B \-M
flag (for Make), or by executing PMake as
.CW make .'' ``
In either case, PMake performs things exactly like Make (while still
supporting most of the nice new features PMake provides). This
includes:
.IP \(bu 2
No parallel execution.
.IP \(bu 2
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.
.IP \(bu 2
A single Bourne shell is used to execute each command, thus the
shell's
.CW $$
variable is useless, changing directories doesn't work across command
lines, etc.
.IP \(bu 2
If no special characters exist in a command line, PMake will break the
command into words itself and execute the command directly, without
executing a shell first. The characters that cause PMake to execute a
shell are:
.CW # ,
.CW = ,
.CW | ,
.CW ^ ,
.CW ( ,
.CW ) ,
.CW { ,
.CW } ,
.CW ; ,
.CW & ,
.CW < ,
.CW > ,
.CW * ,
.CW ? ,
.CW [ ,
.CW ] ,
.CW : ,
.CW $ ,
.CW ` ,
and
.CW \e .
You should notice that these are all the characters that are given
special meaning by the shell (except
.CW '
and
.CW " ,
which PMake deals with all by its lonesome).
.IP \(bu 2
The use of the null suffix is turned off.
.Ix 0 ref "null suffix"
.Ix 0 ref suffix null
.xH 2 The Way Things Work
.LP
When PMake reads the makefile, it parses sources and targets into
nodes in a graph. The graph is directed only in the sense that PMake
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.
.LP
The most important thing to know about how PMake uses this graph is
that the traversal is breadth-first and occurs in two passes.
.LP
After PMake has parsed the makefile, it begins with the nodes the user
has told it to make (either on the command line, or via a 
.CW .MAIN
target, or by the target being the first in the file not labeled with
the
.CW .NOTMAIN
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 
.CW Suff_FindDeps
(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
.CW .USE
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
.CW .USE
node has been processed). You will note that this allows a
.CW .USE
node to have children that are
.CW .USE
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.
.LP
At this point, all the leaves of the graph are in the examination
queue. PMake 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 PMake 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, PMake is finished.
.LP
Once all targets have been processed, PMake executes the commands
attached to the
.CW .END
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 (PMake keeps a running count of how many
targets are left to be made), there is a cycle in the graph. PMake does
a depth-first traversal of the graph to find all the targets that
weren't made and prints them out one by one.
.xH 1 Answers to Exercises
.IP (3.1)
This is something of a trick question, for which I apologize. The
trick comes from the UNIX definition of a suffix, which PMake doesn't
necessarily share. You will have noticed that all the suffixes used in
this tutorial (and in UNIX in general) begin with a period
.CW .ms , (
.CW .c ,
etc.). Now, PMake's idea of a suffix is more like English's: it's the
characters at the end of a word. With this in mind, one possible
.Ix 0 def suffix
solution to this problem goes as follows:
.DS I
\&.SUFFIXES       : ec.exe .exe ec.obj .obj .asm
ec.objec.exe .obj.exe :
        link -o $(.TARGET) $(.IMPSRC)
\&.asmec.obj      :
        asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC)
\&.asm.obj        :
        asm -o $(.TARGET) $(.IMPSRC)
.DE
.IP (3.2)
The trick to this one lies in the ``:='' variable-assignment operator
and the ``:S'' variable-expansion modifier. 
.Ix 0 ref variable assignment expanded
.Ix 0 ref variable expansion modified
.Ix 0 ref modifier substitute
.Ix 0 ref :S
.Ix 0 ref :=
Basically what you want is to take the pointer variable, so to speak,
and transform it into an invocation of the variable at which it
points. You might try something like
.DS I
$(PTR:S/^/\e$(/:S/$/))
.DE
which places
.CW $( '' ``
at the front of the variable name and
.CW ) '' ``
at the end, thus transforming
.CW VAR ,'' ``
for example, into
.CW $(VAR) ,'' ``
which is just what we want. Unfortunately (as you know if you've tried
it), since, as it says in the hint, PMake does no further substitution
on the result of a modified expansion, that's \fIall\fP you get. The
solution is to make use of ``:='' to place that string into yet
another variable, then invoke the other variable directly:
.DS I
*PTR            := $(PTR:S/^/\e$(/:S/$/)/)
.DE
You can then use
.CW $(*PTR) '' ``
to your heart's content.
.de Gp
.XP
\&\fB\\$1:\fP
..
.xH 1 Glossary of Jargon
.Gp "attribute"
A property given to a target that causes PMake to treat it differently.
.Gp "command script"
The lines immediately following a dependency line that specify
commands to execute to create each of the targets on the dependency
line. Each line in the command script must begin with a tab.
.Gp "command-line variable"
A variable defined in an argument when PMake is first executed.
Overrides all assignments to the same variable name in the makefile.
.Gp "conditional"
A construct much like that used in C that allows a makefile to be
configured on the fly based on the local environment, or on what is being
made by that invocation of PMake.
.Gp "creation script"
Commands used to create a target. See ``command script.''
.Gp "dependency"
The relationship between a source and a target. This comes in three
flavors, as indicated by the operator between the target and the
source. `:' gives a straight time-wise dependency (if the target is
older than the source, the target is out-of-date), while `!' provides
simply an ordering and always considers the target out-of-date. `::'
is much like `:', save it creates multiple instances of a target each
of which depends on its own list of sources.
.Gp "dynamic source"
This refers to a source that has a local variable invocation in it. It
allows a single dependency line to specify a different source for each
target on the line.
.Gp "global variable"
Any variable defined in a makefile. Takes precedence over variables
defined in the environment, but not over command-line or local variables.
.Gp "input graph"
What PMake constructs from a makefile. Consists of nodes made of the
targets in the makefile, and the links between them (the
dependencies). The links are directed (from source to target) and
there may not be any cycles (loops) in the graph.
.Gp "local variable"
A variable defined by PMake visible only in a target's shell script.
There are seven local variables, not all of which are defined for
every target:
.CW .TARGET ,
.CW .ALLSRC ,
.CW .OODATE ,
.CW .PREFIX ,
.CW .IMPSRC ,
.CW .ARCHIVE ,
and
.CW .MEMBER .
.CW .TARGET ,
.CW .PREFIX ,
.CW .ARCHIVE ,
and 
.CW .MEMBER
may be used on dependency lines to create ``dynamic sources.''
.Gp "makefile"
A file that describes how a system is built. If you don't know what it
is after reading this tutorial.\|.\|.\|.
.Gp "modifier"
A letter, following a colon, used to alter how a variable is expanded.
It has no effect on the variable itself.
.Gp "operator"
What separates a source from a target (on a dependency line) and specifies
the relationship between the two. There are three:
.CW : ', `
.CW :: ', `
and
.CW ! '. `
.Gp "search path"
A list of directories in which a file should be sought. PMake's view
of the contents of directories in a search path does not change once
the makefile has been read. A file is sought on a search path only if
it is exclusively a source.
.Gp "shell"
A program to which commands are passed in order to create targets.
.Gp "source"
Anything to the right of an operator on a dependency line. Targets on
the dependency line are usually created from the sources.
.Gp "special target"
A target that causes PMake to do special things when it's encountered.
.Gp "suffix"
The tail end of a file name. Usually begins with a period,
.CW .c
or
.CW .ms ,
e.g.
.Gp "target"
A word to the left of the operator on a dependency line. More
generally, any file that PMake might create. A file may be (and often
is) both a target and a source (what it is depends on how PMake is
looking at it at the time \*- sort of like the wave/particle duality
of light, you know).
.Gp "transformation rule"
A special construct in a makefile that specifies how to create a file
of one type from a file of another, as indicated by their suffixes.
.Gp "variable expansion"
The process of substituting the value of a variable for a reference to
it. Expansion may be altered by means of modifiers.
.Gp "variable"
A place in which to store text that may be retrieved later. Also used
to define the local environment. Conditionals exist that test whether
a variable is defined or not.
.bp
.\" Output table of contents last, with an entry for the index, making
.\" sure to save and restore the last real page number for the index...
.nr @n \n(PN+1
.\" We are not generating an index
.\" .XS \n(@n
.\" Index
.\" .XE
.nr %% \n%
.PX
.nr % \n(%%