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
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
|
// file : doc/packaging.cli
// license : MIT; see accompanying LICENSE file
"\name=build2-packaging-guide"
"\subject=toolchain"
"\title=Packaging Guide"
// NOTES
//
// - Maximum <pre> line is 70 characters.
//
// - In guideline titles (do/don't) omit a/the.
// TODO:
//
// @@ Update pinned repositories (libevent, libasio, xxd, libzstd).
//
// @@ Update build2-packaging front page with link to this guide (add
// organization README?)
//
// @@ Enforce continous versioning with pre-commit hook -- maybe add a note?
//
// @@ Add example of propagating config.libfoo.debug to macro on build options?
//
// @@ Add link from metadata HOWTO to "Modifying upstream source code with
// preprocessor".
//
// @@ Mapping to system package manager names/versions.
//
// @@ Are upstream tests well behaved? -- add never exit/take unreasonable time
//
"
\h0#preface|Preface|
This document provides guidelines for converting third-party C/C++ projects to
the \c{build2} build system and making them available as packages from
\l{https://cppget.org cppget.org}, the \c{build2} community's central package
repository. For additional information, including documentation for individual
\c{build2} toolchain components, man pages, HOWTOs, etc., refer to the project
\l{https://build2.org/doc.xhtml Documentation} page.
\h1#intro|Introduction|
The aim of this guide is to ease the conversion of third-party C/C++ projects
to the \c{build2} build system and publishing them to the
\l{https://cppget.org cppget.org} package repository by codifying the best
practices and techniques. By following the presented guidelines you will also
make it easier for others to review your work and help with ongoing
maintenance.
\N|A \c{build2}-based project can only consume packages that use the
\c{build2} build system (with the exception of
\l{https://build2.org/faq.xhtml#why-syspkg system-installed packages}). In
other words, there is no support for \"wrapping\" or otherwise adapting
third-party projects' existing build systems. While replacing the build system
unquestionably requires more work upfront, the \c{build2} project's experience
is that the long-term benefits of this effort are well justified (see
\l{https://build2.org/faq.xhtml#why-package-managers How does \c{build2}
compare to other package managers?} for details).|
The primary focus of this guide is existing C/C++ projects that use a
different build system and that are maintained by a third-party, which we will
refer to as \i{upstream}. Unless upstream is willing to incorporate support
for \c{build2} directly into their repository, such projects are normally
packaged for \c{build2} in a separate \c{git} repository under the
\l{https://github.com/build2-packaging github.com/build2-packaging}
organization. Note, however, that many of the presented guidelines are also
applicable when converting your own projects (that is, where you are the
upstream) as well as projects that use languages other than C or C++.
Most C/C++ packages that are published to \l{https://cppget.org cppget.org}
are either libraries or executables (projects that provide both are normally
split into several packages) with libraries being in the strong majority.
Libraries are also generally more difficult to build correctly. As a result,
this guide uses libraries as a baseline. In most cases, a library-specific
step is easily distinguished as such and can be skipped when dealing with
executables. And in cases where a more nuanced change is required, a note will
be provided.
At the high-level, packaging a third-party project involves the following
steps:
\ol|
\li|Create the \c{git} repository and import upstream source code.|
\li|Generate \c{buildfile} templates that match upstream layout.|
\li|Tweak the generated \c{buildfiles} to match upstream build.|
\li|Test locally and using the \l{https://ci.cppget.org \c{build2} CI service}.|
\li|Release and publish the package to \l{https://cppget.org cppget.org}.|
|
Once this process is completed and the package is published, new releases
normally require a small amount of work provided there are no drastic changes
in the upstream layout or build. The sequence of steps for a new release would
typical look like this:
\ol|
\li|Add new and/or remove old upstream source code, if any.|
\li|Tweak \c{buildfiles} to match changes to upstream build, if any.|
\li|Test locally and using the \l{https://ci.cppget.org \c{build2} CI service}.|
\li|Release and publish the package to \l{https://cppget.org cppget.org}.|
|
While packaging a simple library or executable is relatively straightforward,
the C and C++ languages and their ecosystems are infamous for a large amount
of variability in the platforms, compilers, source code layouts, and build
systems used. This leads to what looks like an endless list of special
considerations that are only applicable in certain, more complex cases.
As result, the presented guidelines are divided into four chapters: \l{#core
Common Guidelines} cover steps that are applicable to most packaging
efforts. As mentioned earlier, these steps will assume packaging a library but
they should be easy to adapt to executables. This chapter is followed by
\l{#dont-do What Not to Do} which covers the common packaging mistakes and
omissions. These are unfortunately relatively common because experience with
other build systems often does not translate directly to \c{build2} and some
techniques (such as header-only libraries) are discouraged. The last two
chapters are \l{#howto HOWTO} and \l{#faq FAQ}. The former covers the
above-mentioned long list of special considerations that are only applicable
in certain cases while the latter answer frequent packaging-related questions.
Besides the presented guidelines, you may also find the existing packages
found in \l{https://github.com/build2-packaging github.com/build2-packaging} a
good source of example material. The repositories pinned to the front page are
the recommended starting point.
This guide assumes familiarity with the \c{build2} toolchain. At the minimum
you should have read through \l{intro The \c{build2} Toolchain Introduction}
and the \l{b#intro Introduction} chapter in the build system manual. Ideally,
you should also have some experience using \c{build2} in your own projects.
In this guide we will only show the UNIX version of the commands. In most
cases making a Windows version is a simple matter of adjusting paths and, if
used, line continuations. And where this is not the case a note will be
provided showing the equivalent Windows command.
\h#intro-term|Terminology|
We use the term \i{upstream} to refer collectively to the third-party project
as well as to its authors. For example, we may say, \"upstream does not use
semver\" meaning that the upstream project does not use semver for
versioning. Or we may say, \"upstream released a new version\" meaning that
the upstream project's authors released a new version.
We will often use \i{upstream} as a qualifier to refer to a specific part of
the upstream project. Commonly used qualified terms like this include:
\dl|
\li|\i{upstream repository}
The version control (normally \c{git}) repository of the third-party project.|
\li|\i{upstream source code}
The C/C++ source code that constitutes the third-party project.
|
\li|\i{upstream layout}
The directory structure and location of source code in the third-party
project.
|
\li|\i{upstream build system}
The equivalents of \c{buildfiles} that are used by the third-party project to
build its source code, run tests, etc. For example, if upstream uses CMake,
then all the \c{CMakeLists.txt}, \c{*.cmake}, etc., files will constitute its
build system.||
To avoid confusion, in this guide we will always use the term \i{project} to
refer to upstream and \i{package} to refer to its \c{build2} conversion, even
though we would normally call our own \c{build2}-based work a project, not a
package (see \l{b#intro-proj-struct Project Structure} for details on the
\c{build2} terminology in this area). Some commonly used \c{build2}-side terms
in this guide include:
\dl|
\li|\i{package \c{git} repository}
The \c{git} repository that hosts the package of the upstream project.|
\li|\i{multi-package repository}
Sometimes it makes sense to split the upstream project into multiple
\c{build2} packages (for example, a library and an executable). In this case
the package repository structure must become multi-package.||
\h1#core|Common Guidelines|
This chapter describes the recommended sequence of steps for packaging a
third-party project for \c{build2} with the end-goal of publishing it to the
\l{https://cppget.org cppget.org} package repository.
\h#core-repo|Setup the package repository|
This section covers the creation of the package \c{git} repository and
the importation of the upstream source code.
\h2#core-repo-exists|Check if package repository already exists|
Before deciding to package a third-party project you have presumably checked
on \l{https://cppget.org cppget.org} if someone has already packaged it. There
are several other places that make sense to check as well:
\ul|
\li|\l{https://queue.cppget.org queue.cppget.org} contains packages that
have been submitted but not yet published.|
\li|\l{https://queue.stage.build2.org queue.stage.build2.org} contains
packages that have been submitted but can only be published after the next
release of the \c{build2} toolchain (see \l{#faq-publish-stage Where to
publish if package requires staged toolchain?} for background).|
\li|\l{https://github.com/build2-packaging github.com/build2-packaging}
contains all the third-party package repositories. Someone could already be
working on the package but haven't yet published it.|
\li|\l{https://github.com/build2-packaging/WISHLIST/issues
github.com/build2-packaging/WISHLIST} contains as issues projects that people
wish were packaged. These may contain offers to collaborate or announcements
of ongoing work.||
In all these cases you should be able to locate the package \c{git} repository
and/or connect with others in order to collaborate on the packaging work. If
the existing effort looks abandoned (for example, there hasn't been any
progress for a while and the existing maintainer doesn't respond) and you
would like to take over the package,
\l{https://build2.org/community.xhtml#help get in touch}.
\h2#core-repo-name|Use upstream repository name as package repository name|
It is almost always best to use the upstream repository name as the package
repository name. If there is no upstream repository (for example, because the
project doesn't use a version control system), the name used in the source
archive distribution would be the natural fallback choice.
\N|See \l{#core-package-name Decide on the package name} for the complete
picture on choosing names.|
\h2#core-repo-create|Create package repository in personal workspace|
For a third-party project, the end result that we are aiming for is a package
repository under the \l{https://github.com/build2-packaging
github.com/build2-packaging} organization.
\N|We require all the third-party projects that are published to
\l{https://cppget.org cppget.org} to be under the
\l{https://github.com/build2-packaging github.com/build2-packaging}
organization in order to ensure some continuity in case the original
maintainer loses interest, etc. You will still be the owner of the repository
and by hosting your packaging efforts under this organization (as opposed to,
say, your personal workspace) you make it easier for others to discover your
work and to contribute to the package maintenance.
Note that this requirement does not apply to your own projects (that is, where
you are the upstream) and where the \c{build2} support is normally part of the
upstream repository.
Finally, a note on the use of \c{git} and GitHub: if for some reason you are
unable to use either, \l{https://build2.org/community.xhtml#help get in touch}
to discuss alternatives.|
However, the recommended approach is to start with a repository in your
personal workspace and then, when it is ready or in a reasonably complete
state, transfer it to \l{https://github.com/build2-packaging
github.com/build2-packaging}. This gives you the freedom to make destructive
changes to the repository (including deleting it and starting over) during the
initial packaging work. It also removes the pressure to perform: you can give
it a try and if things turn out more difficult than you expected, you can just
drop the repository.
\N|For repositories under \l{https://github.com/build2-packaging
github.com/build2-packaging} the \c{master}/\c{main} branch is protected: it
cannot be deleted and its commit history cannot be overwritten with a forced
push.|
\N|While you can use any name for a repository under the personal workspace,
under \l{https://github.com/build2-packaging github.com/build2-packaging} it
should follow the \l{#core-repo-name Use upstream repository name as package
repository name} guideline. In particular, there should be no prefixes like
\c{build2-} or suffixes like \c{-package}. If the repository under your
personal workspace does not follow this guideline, you will need to rename it
before transferring it to the \l{https://github.com/build2-packaging
github.com/build2-packaging} organization.|
There is one potential problem with this approach: it is possible that
several people will start working on the same third-party project without
being aware of each other's efforts. If the project you are packaging is
relatively small and you don't expect it to take more than a day or two, then
this is probably not worth worrying about. For bigger projects, however, it
makes sense to announce your work by creating (or updating) the corresponding
issue in \l{https://github.com/build2-packaging/WISHLIST
github.com/build2-packaging/WISHLIST}.
To put it all together, the recommended sequence of actions for this step:
\ol|
\li|Create a new empty repository under your personal workspace from the
GitHub UI.|
\li|Set the repository description to \c{build2 package for <name>}, where
\c{<name>} is the third-party project name.|
\li|Make the repository public (otherwise you won't be able to CI it).|
\li|Don't automatically add any files (\c{README}, \c{LICENSE}, etc).|
\li|Clone the empty repository to your machine (using the SSH protocol).||
\N|Since this is your personal repository, you can do the initial work
directly in \c{master}/\c{main} or in a separate branch, it's up to you.|
As a running example, let's assume we want to package a library called \c{foo}
whose upstream repository is at \c{https://github.com/<upstream>/foo.git}. We
have created its package repository at
\c{https://github.com/<personal>/foo.git} (with the \c{build2 package for foo}
description) and can now clone it:
\
$ git clone git@github.com:<personal>/foo.git
\
\h2#core-repo-init|Initialize package repository with \c{bdep new}|
Change to the root directory of the package repository that you have cloned
in the previous step and run (continuing with our \c{foo} example):
\
$ cd foo/ # Change to the package repository root.
$ bdep new --type empty,third-party
$ tree -a .
./
├── .bdep/
│ └── ...
├── .git/
│ └── ...
├── .gitattributes
├── .gitignore
├── README.md
└── repositories.manifest
\
\N|We use the special \c{third-party} sub-option which is meant for converting
third-party projects to \c{build2}. See \l{bdep-new(1)} for details.|
This command creates a number of files in the root of the repository:
\dl|
\li|\n\c{README.md}\n
This is the repository \c{README.md}. We will discuss the recommended content
for this file later.|
\li|\n\c{repositories.manifest}\n
This file specifies the repositories from which this project will obtain its
dependencies (see \l{intro#guide-add-remove-deps Adding and Removing
Dependencies}). If the project you are packaging has no dependencies, then you
can safely remove this file (it's easy to add later if this changes). And for
projects that do have dependencies we will discuss the appropriate changes to
this file later.|
\li|\n\c{.gitattributes} and \c{.gitignore}\n
These are the \c{git} infrastructure files for the repository. You shouldn't
normally need to change anything in them at this stage (see the comments
inside for details).||
Next add and commit these files:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Initialize package repository\"
\
\N|In these guidelines we will be using the package repository setup that is
capable of having multiple packages (referred to as \i{multi-package
repository}). This is recommended even for upstream projects that only
provides a single package because it gives us the flexibility of adding new
packages at a later stage without having to perform a major restructuring of
our repository.
Note also that upstream providing multiple packages is not the only reason we
may end up having multiple \c{build2} packages. Another common reason is
factoring tests into a separate package due to a dependency on a testing
framework
(see \l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for background and
details). While upstream adding new packages may not be very common, upstream
deciding to use a testing framework is a lot more plausible.
The only notable drawback of using a multi-package setup with a single package
is the extra subdirectory for the package and a few extra files (such as
\c{packages.manifest} that lists the packages) in the root of the repository.
If you are certain that the project that you are converting is unlikely to
have multiple packages (for example, because you are the upstream) and won't
need extra dependencies for its tests (a reasonable assumption for a C
project), then you could instead go with the single-package repository where
the repository root is the package root. See \l{bdep-new(1)} for details on
how to initialize such a repository. In this guide, however, we will continue
to assume a multi-package repository setup.|
\h2#core-repo-submodule|Add upstream repository as \c{git} submodule|
If the third-party project is available from a \c{git} repository, then the
recommended approach is to use the \c{git} submodule mechanism to make the
upstream source code available inside the package repository, customarily in a
subdirectory called \c{upstream/}.
\N|While \c{git} submodules receive much criticism, in our case we use them
exactly as intended: to select and track specific (release) commits of an
external project. As a result, there is nothing tricky about their use for our
purpose and all the relevant commands will be provided and explained, in case
you are not familiar with this \c{git} mechanism.|
Given the upstream repository URL, to add it as a submodule, run the following
command from the package repository root (continuing with our \c{foo} example):
\
$ cd foo/ # Change to the package repository root.
$ git submodule add https://github.com/<upstream>/foo.git upstream
\
\N|You should prefer \c{https://} over \c{git://} for the upstream repository
URL since the \c{git://} protocol may not be accessible from all networks.
Naturally, never use a URL that requires authentication, for example, SSH
(SSH URLs start with \c{git@github.com} for GitHub).|
Besides the repository URL, you also need the commit of the upstream release
which you will be packaging. It is common practice to tag releases so the
upstream tags would be the first place to check. Failing that, you can always
use the commit id.
Assuming the upstream release tag you are interested in is called \c{vX.Y.Z},
to update the \c{upstream} submodule to point to this release commit, run the
following commands:
\
$ cd upstream/
$ git checkout vX.Y.Z
$ cd ../
\
Then add and commit these changes:
\
$ git add .
$ git status
$ git commit -m \"Add upstream submodule, vX.Y.Z\"
\
Now we have all the upstream source code for the version that we are
packaging available in the \c{upstream/} subdirectory of our repository.
The plan is to then use symbolic links (symlinks) to non-invasively overlay
the \c{build2}-related files (\c{buildfile}, \c{manifest}, etc) with the
upstream source code, if necessary adjusting upstream structure to split it
into multiple packages and/or to better align with the source/output layouts
recommended by \c{build2} (see \l{https://build2.org/article/symlinks.xhtml
Using Symlinks in \c{build2} Projects} for background and rationale). But
before we can start adding symlinks to the upstream source (and other files
like \c{README}, \c{LICENSE}, etc), we need to generate the \c{buildfile}
templates that match the upstream source code layout. This is the subject of
the next section.
\N|While on UNIX-like operating systems symlinks are in widespread use, on
Windows it's a niche feature that unfortunately could be cumbersome to use
(see \l{https://build2.org/article/symlinks.xhtml#windows Symlinks and
Windows} for details). However, the flexibility afforded by symlinks when
packaging third-party projects is unmatched by any other mechanism and we
therefore use them despite potentially sub-optimal packaging experience
on Windows.|
\h#core-package|Create package and generate \c{buildfile} templates|
This section covers the addition of the package to the repository we have
prepared in the previous steps and the generation of the \c{buildfile}
templates that match the upstream source code layout.
\h2#core-package-name|Decide on the package name|
While choosing the package repository name was pretty straightforward, things
get less clear cut when it comes to the package name.
\N|If you need a refresher on the distinction between projects and packages,
see \l{#intro-term Terminology}.|
Picking a name for a package that provides an executable is still relatively
straightforward: you should use the upstream name (which is usually the same
as the upstream project name) unless there is a good reason to deviate. One
recommended place to check before deciding on a name is the
\l{https://packages.debian.org Debian package repository}. If their package
name differs from upstream, then there is likely a good reason for that and
it is worth trying to understand what it is.
\N|Tip: when trying to find the corresponding Debian package, search for the
executable file name in the package contents if you cannot fine the package by
its upstream name. Also consider searching in the \c{unstable} distribution in
addition to \c{stable} for newer packages.|
Picking a name for a package that provides a library is where things can get
more complicated. While all the recommendations that have been listed for
executables apply equally to libraries, there are additional considerations.
In \c{build2} we recommend (but not require) that new library projects use a
name that starts with \c{lib} in order to easily distinguish them from
executables and avoid any clashes, potential in the future (see
\l{intro#proj-struct Canonical Project Structure} for details). To illustrate
the problem, consider the \c{zstd} project which provides a library and an
executable. In upstream repository both are part of the same codebase that
doesn't try to separate them into packages so that, for example, library could
be used without downloading and building the executable. In \c{build2},
however, we do need to split them into two separate packages and both packages
cannot be called \c{zstd}. So we call them \c{zstd} and \c{libzstd}.
\N|If you are familiar with the Debian package naming policy, you will
undoubtedly recognize this approach. In Debian all the library packages (with
very few exceptions) start with the \c{lib} prefix. So when searching for an
upstream name in the \l{https://packages.debian.org Debian package repository}
make sure to prefix it with \c{lib} (unless it already starts with this
prefix, of course).|
This brings the question of what to do about third-party libraries: should we
add the \c{lib} prefix to the package name if it's not already there?
Unfortunately, there is no clear cut answer and whichever decision you make,
there will be drawbacks. Specifically, if you add the \c{lib} prefix, the main
drawback is that the package name now deviates from the upstream name and if
the project maintainer ever decides to add \c{build2} support to the upstream
repository, there could be substantial friction. On the other hand, if you
don't add the \c{lib} prefix, then you will always run the risk of a future
clash with an executable name. And, as was illustrated with the \c{zstd}
example, a late addition of an executable won't necessarily cause any issues
to upstream. As a result, we don't have a hard requirement for the \c{lib}
prefix unless there is already an executable that would cause the clash (this
applies even if it's not being packaged yet or is provided by an unrelated
project). If you don't have a strong preference, we recommend that you add the
\c{lib} prefix (unless it is already there). In particular, this will free you
from having to check for any potential clashes. See
\l{https://github.com/build2/HOWTO/blob/master/entries/name-packages-in-project.md
How should I name packages when packaging third-party projects?} for
additional background and details.
To build some intuition for choosing package names, let's consider several
real examples. We start with executables:
\
upstream | upstream | Debian | build2 package| build2
project name|executable name|package name|repository name|package name
------------+---------------+------------+---------------+------------
byacc byacc byacc byacc byacc
sqlite sqlite3 sqlite3 sqlite sqlite3
vim xxd xxd xxd xxd
OpenBSD m4 - openbsd-m4 openbsd-m4
qtbase 5 moc qtbase5-\ Qt5 Qt5Moc
dev-tools
qtbase 6 moc qt6-base-\ Qt6 Qt6Moc
dev-tools
\
The examples are arranged from the most straightforward naming to the
least. The last two examples show that sometimes, after carefully considering
upstream naming, you nevertheless have no choice but to ignore it and forge
your own path.
Next let's look at library examples. Notice that some use the same \c{build2}
package repository name as the executables above. This means they are part of
the same multi-package repository.
\
upstream | upstream | Debian | build2 package| build2
project name|library name |package name|repository name|package name
------------+---------------+------------+---------------+------------
libevent libevent libevent libevent libevent
brotli brotli libbrotli brotli libbrotli
zlib zlib zlib zlib libz
sqlite libsqlite3 libsqlite3 sqlite libsqlite3
libsig\ libsigc++ libsigc++ libsig\ libsigc++
cplusplus cplusplus
qtbase 5 QtCore qtbase5-dev Qt5 libQt5Core
qtbase 6 QtCore qt6-base-dev Qt6 libQt6Core
\
If an upstream project is just a single library, then the project name is
normally the same as the library name (but there are exceptions, like
\c{libsigcplusplus} in the above table). However, when looking at the upstream
repository that contains multiple components (libraries and/or executables,
like \c{qtcore} in the above example), it may not be immediately obvious what
the upstream's library names are. In such cases, the corresponding Debian
packages can really help clarify the situation. Failing that, look into the
existing build system. In particular, if it generates the \c{pkg-config} file,
then the name of this file is usually the upstream library name.
\N|Looking at the names of the library binaries is less helpful because on
UNIX-like systems they must start with the \c{lib} prefix. And on Windows the
names of library binaries often embed extra information (static/import,
debug/release, etc) and may not correspond directly to the library name.|
And, speaking of multiple components, if you realize the upstream project
provides multiple libraries and/or executables, then you need to decide
whether to split them into separate \c{build2} packages and if so, how. Here,
again, the corresponding Debian packages can be a good reference point. Note,
however, that we often deviate from Debian's splits, especially when it comes
to libraries. Such differences are usually due to Debian focusing on binary
packages while in \c{build2} we are focusing on source packages.
To give a few example, \c{libevent} shown in the above table provides several
libraries (\c{libevent-core}, \c{libevent-extra}, etc) and in Debian it is
actually split into several binary packages along these lines. In \c{build2},
however, there is a single source package that provides all these libraries
with everything except \c{libevent-core} being optional. An example which
shows the decision made in a different direction would be the Boost libraries:
in Debian all the header-only Boost libraries are bundled into a single
package while in \c{build2} they are all separate packages.
The overall criteria here can be stated as follows: if a small family of
libraries provide complimentary functionality (like \c{libevent}), then we put
them all into a single package, usually making the additional functionality
optional. However, if the libraries are independent (like Boost) or provide
alternative rather than complimentary functionality (for example, like
different backends in \c{imgui}), then we make them separate packages. Note
that we never bundle an executable and a (public) library in a single package
(because, when consumed, they usually require different
\l{bpkg#manifest-package-depends dependency types}: build-time vs run-time).
Note also that while it's a good idea to decide on the package split and all
the package names upfront to avoid surprises later, you don't have to actually
provide all the packages right away. For example, if upstream provides a
library and an executable (like \c{zstd}), you can start with the library and
the executable package can be added later (potentially by someone else).
\N|In the \"library and executable\" case, if you plan to package both, the
sensible strategy is to first completely package the library stopping short of
releasing and publishing, then repeat the same process to package the
executable, and finally release and publish both.|
Admittedly, the recommendations in this section are all a bit fuzzy and one
can choose different names or different package splits that could all seem
reasonable. If you are unsure how to split the upstream project or what names
to use, \l{https://build2.org/community.xhtml#help get in touch} to discuss
the alternatives. It can be quite painful to change these things after you
have completed the remaining packaging steps.
Continuing with our \c{foo} example, we will follow the above recommendation
and call the library package \c{libfoo}.
\h2#core-package-struct|Decide on the package source code layout|
Another aspect we need to decide on is the source code layout inside the
package. Here we want to stay as close to the upstream layout as possible
unless there are valid reasons to deviate. Staying close has the best chance
of giving us a build without any compile errors since the header inclusion in
the project can be sensitive to this layout. This also makes it easier for
upstream to adopt the \c{build2} build.
Sometimes, however, there are good reasons for deviating from upstream,
especially in cases where upstream is clearly following bad practices, for
example including generically-named public headers without the library name as
a subdirectory prefix. If you do decide to change the layout, it's usually
less disruptive (to the build) to rearrange things at the outer levels than at
the inner. For example, it should normally be possible to move/rename the
top-level \c{tests/} directory or to place the library source files into a
subdirectory.
Our overall plan is to create the initial layout and \c{buildfile} templates
automatically using \l{bdep-new(1)} in the \c{--package} mode, then \"fill\"
the package with upstream source code using symlinks, and finish off with
tweaking the generated \c{buildfiles} to match the upstream build.
The main rationale for using \l{bdep-new(1)} instead of doing everything by
hand is that there are many nuances in getting the build right and
auto-generated \c{buildfiles} had years of refinement and fine-tuning. The
familiar structure also makes it easier for others to understand your build,
for example while reviewing your package submission or helping out with
maintenance.
The \l{bdep-new(1)} command supports a wide variety of
\l{bdep-new.xhtml#src-layout source layouts}. While it may take a bit of time
to understand the customization points necessary to achieve the desired layout
for your first package, this experience will pay off in spades when you work
on converting subsequent packages.
And so the focus of the following several steps is to iteratively discover the
\l{bdep-new(1)} command line that best approximates the upstream layout. The
recommended procedure is as follows:
\ol|
\li|\nStudy the upstream source layout and existing build system.|
\li|\nCraft and execute the \l{bdep-new(1)} command line necessary to achieve
the upstream layout.|
\li|\nStudy the auto-generated \c{buildfiles} for things that don't fit and
need to change. But don't rush to start manually editing the result. First get
an overview of the required changes and then check if it's possible to achieve
these changes automatically using one of \l{bdep-new(1)} sub-options. If
that's the case, delete the package, and restart from step 2.||
This and the following two sections discuss each of these steps in more detail
and also look at some examples.
The first step above is to study the upstream project in order to understand
where the various parts are (headers, sources, etc) and how they are built.
Things that can help here include:
\ul|
\li|Read through the existing build system definitions.|
\li|Try to build the project using the existing build system.|
\li|Try to install the project using the existing build system.|
\li|Look into the Debian package contents to see if there are any differences
with regards to the installation locations.||
\N|If while studying the upstream build system you notice other requirements,
for example, the need to compile source files other than C/C++ (such as
Objective-C/C++, assembler, etc) or the need to generate files from \c{.in}
templates (or their \c{.cmake}/\c{.meson} equivalents), and are wondering how
they would be handled in the \c{build2} build, see the
\l{#core-adjust-build-src-source-ext Adjust source \c{buildfile}: extra
requirements} step for a collection of pointers.|
For libraries, the first key pieces of information we need to find is how the
public headers are included and where they are installed. The two common
\i{good} practices is to either include the public headers with a library name
as a subdirectory, for example, \c{#include\ <foo/util.h>}, or to include the
library name into each public header name, for example, \c{#include\
<foo-util.h>} or \c{#include\ <foo.h>} (in the last example the header name is
the library name itself, which is also fairly common). Unfortunately, there is
also a fairly common \i{bad} practice: having generically named headers (such
as \c{util.h}) included without the library name as a subdirectory.
\N|The reason this is a bad practice is that libraries that have such headers
cannot coexist, neither in the same build nor when installed. See
\l{#howto-bad-inclusion-practice How do I deal with bad header inclusion
practice?} if you encounter such a case. See \l{intro#proj-struct Canonical
Project Structure} for additional background and details.|
Where should we look to get this information? While the library source files
sound like a natural place, oftentimes they include own headers with the
\c{\"\"} style inclusion, either because the headers are in the same directory
or because the library build arranges for them to be found this way with
additional header search paths. As a result, a better place to look could be
the library's examples and/or tests. Some libraries also describe which
headers they provide and how to include them in their documentation.
The way public headers are included normally determines where they are
installed. If they are included with a subdirectory, then they are normally
installed into the same subdirectory in, say, \c{/usr/include/}. Continuing
with the above example, a header that is included as \c{<foo/util.h>} would
normally be installed as \c{/usr/include/foo/util.h}. On the other hand, if
the library name is part of the header name, then the headers are usually (but
not always) installed directly into, say, \c{/usr/include/}, for example as
\c{/usr/include/foo-util.h}.
\N|While these are the commonly used installation schemes, there are
deviations. In particular, in both cases upstream may choose to add an
additional subdirectory when installing (so the above examples will instead
end up with, say, \c{/usr/include/foo-v1/foo/util.h} and
\c{/usr/include/foo-v1/sub/foo-util.h}). See
\l{#howto-extra-header-install-subdir How do I handle extra header
installation subdirectory?} if you encounter such a case.|
The inclusion scheme would normally also be recreated in the upstream source
code layout. In particular, if upstream includes public headers with a
subdirectory prefix, then this subdirectory would normally also be present in
the upstream layout so that such a header can be included from the upstream
codebase directly. As an example, let's say we determined that public headers
of \c{libfoo} are included with the \c{foo/} subdirectory, such as
\c{<foo/util.hpp>}. One of the typical upstream layouts for such a library
would look like this:
\
$ tree upstream/
upstream/
├── include/
│ └── foo/
│ └── util.hpp
└── src/
├── priv.hpp
└── util.cpp
\
Notice how the \c{util.hpp} header is in the \c{foo/} subdirectory rather
than in \c{include/} directly.
The second key piece of information we need to find is whether and, if so,
how the public headers and sources are split. For instance, in the above
example, we can see that public headers go into \c{include/} while sources and
private headers go into \c{src/}. But they could also be combined in the same
directory, for example, as in the following layout:
\
upstream/
└── foo/
├── priv.hpp
├── util.cpp
└── util.hpp
\
\N|In multi-package projects, for example, those that provide both a library
and an executable, you would also want to understand how the sources are split
between the packages.|
If the headers and sources are split into different directories, then the
source directory may or may not have the inclusion subdirectory, similar to
the header directory. In the above split layout the \c{src/} directory doesn't
contain the inclusion subdirectory (\c{foo/}) while the following layout does:
\
upstream/
├── include/
│ └── foo/
│ └── util.hpp
└── src/
└── foo/
├── priv.hpp
└── util.cpp
\
With the understanding of these key properties of upstream layout you should
be in a good position to start crafting the \l{bdep-new(1)} command line that
recreates it.
\N|The \c{bdep-new} documentation uses slightly more general terminology
compared to what we used in the previous section in order to also be
applicable to projects that use modules instead of headers.
Specifically, the inclusion subdirectory (\c{foo/}) is called \i{source
subdirectory} while the header directory (\c{include/}) and source directory
(\c{src/}) are called \i{header prefix} and \i{source prefix}, respectively.|
\h2#core-package-craft-cmd|Craft \c{bdep new} command line to create package|
The recommended procedure for this step is to read through the \c{bdep-new}'s
\l{bdep-new.xhtml#src-layout SOURCE LAYOUT} section (which contains a large
number of examples) while experimenting with various options in an attempt to
create the desired layout. If the layout you've got isn't quite right yet,
simply remove the package directory along with the \c{packages.manifest} file
and try again.
\N|Next to \c{packages.manifest}, \c{bdep-new} will also create the \"glue\"
\c{buildfile} that allows building all the packages from the repository
root. You don't need to remove it when re-creating the package.|
Let's illustrate this approach on the first split layout from the previous
section:
\
upstream/
├── include/
│ └── foo/
│ └── util.hpp
└── src/
├── priv.hpp
└── util.cpp
\
We know it's split, so let's start with that and see what we get. Remember,
our \c{foo} package repository that we have cloned and initialized earlier
looks like this:
\
$ tree foo/
foo/
├── upstream/
├── .gitattributes
├── .gitignore
├── README.md
└── repositories.manifest
\
Now we create the \c{libfoo} package inside:
\
$ cd foo/
$ bdep new --package --lang c++ --type lib,split libfoo
$ tree libfoo/
libfoo/
├── include/
│ └── libfoo/
│ └── foo.hxx
└── src/
└── libfoo/
└── foo.cxx
\
The outer structure looks right, but inside \c{include/} and \c{src/} things
are a bit off. Specifically, the source subdirectory should be \c{foo/}, not
\c{libfoo/}, there shouldn't be one inside \c{src/}, and the file extensions
don't match upstream. All this can be easily tweaked, however:
\
$ rm -r libfoo/ packages.manifest
$ bdep new --package \
--lang c++,cpp \
--type lib,split,subdir=foo,no-subdir-source \
libfoo
$ tree libfoo/
libfoo/
├── include/
│ └── foo/
│ └── foo.hpp
└── src/
└── foo.cpp
\
The other \c{bdep-new} sub-options (see the \l{bdep-new(1)} man page for the
complete list) that you will likely want to use when packaging a third-party
project include:
\dl|
\li|\n\cb{no-version}
Omit the auto-generated version header. Usually upstream will provide its own
equivalent of this functionality.
\N|Note that even if upstream doesn't provide any version information, it's
not a good idea to try to rectify this by providing your own version header
since upstream may add it in a future version and you may end up with a
conflict. Instead, work with the project authors to rectify this upstream.||
\li|\n\cb{no-symexport}\n\cb{auto-symexport}
The \c{no-symexport} sub-option suppresses the generation of the DLL symbol
exporting header. This is an appropriate option if upstream provides its
own symbol exporting arrangements.
The \c{auto-symexport} sub-option enables automatic DLL symbol exporting
support (see \l{b#cc-auto-symexport Automatic DLL Symbol Exporting} for
background). This is an appropriate option if upstream relies on similar
support in the existing build system. It is also recommended that you give
this functionality a try even if upstream does not support building
shared libraries on Windows.|
\li|\n\cb{binless}
Create a header-only library. See \l{#dont-header-only Don't make
library header-only if it can be compiled} and
\l{https://github.com/build2/HOWTO/blob/master/entries/make-header-only-library.md
How do I make a header-only C/C++ library?}|
\li|\n\cb{buildfile-in-prefix}
Place header/source \c{buildfiles} into the header/source prefix directory
instead of source subdirectory. To illustrate the difference, compare these
two auto-generated layouts paying attention to the location of \c{buildfiles}:
\
$ bdep new ... --type lib,split,subdir=foo libfoo
$ tree libfoo/
libfoo/
├── include/
│ └── foo/
│ ├── buildfile
│ └── foo.hpp
└── src/
└── foo/
├── buildfile
└── foo.cpp
\
\
$ bdep new ... --type lib,split,subdir=foo,buildfile-in-prefix libfoo
$ tree libfoo/
libfoo/
├── include/
│ ├── foo/
│ │ └── foo.hpp
│ └── buildfile
└── src/
├── foo/
│ └── foo.cpp
└── buildfile
\
Note that this sub-option only makes sense if we have the header and/or source
prefixes (\c{include/} and \c{src/} in our case) as well as the source
subdirectory (\c{foo/} in our case).
Why would we want to do this? The main reason is to be able to symlink the
entire upstream directories rather than individual files. In the first
listing, the generated \c{buildfiles} are inside the \c{foo/} subdirectories
which mean we cannot just symlink \c{foo/} from upstream.
With a large number of files to symlink, this can be such a strong motivation
that it may make sense to invent a source subdirectory in the source prefix
even if upstream doesn't have one. See \l{#dont-main-target-root-buildfile
Don't build your main targets in the root \c{buildfile}} for details on this
technique.
Another reason we may want to move \c{buildfiles} to prefix is to be able to
handle upstream projects that have multiple source subdirectories. While this
situation is not very common in the header prefix, it can be encountered in the
source prefix of more complex projects, where upstream wishes to organize the
source files into components.||
\N|If upstream uses a mixture of C and C++, then it's recommended to set this
up using the \c{--lang} sub-option of \c{bdep-new}. For example:
\
$ bdep new --lang c++,c ...
\
|
Continuing with our \c{libfoo} example, assuming upstream provides its own
symbol exporting, the final \c{bdep-new} command line would be:
\
$ bdep new --package \
--lang c++,cpp \
--type lib,split,subdir=foo,no-subdir-source,no-version,no-symexport \
libfoo
\
When packaging an executable, things are usually quite a bit simpler: there is
no version header, symbol exporting, and the layout is normally combined
(since there are no public headers). Typically the only potentially tricky
decision you will need to make is whether to use \i{prefix} or \i{source
subdirectory}. Most likely it will be \i{prefix} since most executable
projects will use the \c{\"\"} style inclusion for own headers. For example:
\
$ bdep new --package \
--lang c++ \
--type exe,no-subdir,prefix=foo,export-stub \
foo
\
\N|The \c{export-stub} sub-option causes the generation of
\c{build/export.build}, an export stub that facilitates the importation of
targets from our package (see \l{b#intro-import Target Importation} for
details). The generation of this file for a library is the default since it
will normally be used by other projects and thus imported. An executable,
however, will only need an export stub if it can plausibly be used during the
build (see \l{intro#guide-build-time-linked Build-Time Dependencies and Linked
Configurations} for background). Source code generators are an obvious example
of such executables. A less obvious example would be compression utilities
such as \c{gzip} or \c{zstd}. If you are unsure, it's best to provide an
export stub.|
\h2#core-package-review|Review and test auto-generated \c{buildfile} templates|
Let's get a more complete view of what got generated by the final \c{bdep-new}
command line from the previous section:
\
$ tree libfoo/
libfoo/
├── build/
│ └── ...
├── include/
│ └── foo/
│ ├── buildfile
│ └── foo.hpp
├── src/
│ ├── buildfile
│ └── foo.cpp
├── tests/
│ ├── build/
│ │ └── ...
│ ├── basics/
│ │ ├── buildfile
│ │ └── driver.cpp
│ └── buildfile
├── buildfile
├── manifest
└── README.md
\
Once the overall layout looks right, the next step is to take a closer look at
the generated \c{buildfiles} to make sure that overall they match the upstream
build. Of particular interest are the header and source directory
\c{buildfiles} (\c{libfoo/include/foo/buildifle} and \c{libfoo/src/buildifle}
in the above listing) which define how the library is built and installed.
Here we are focusing on the macro-level differences that are easier to change
by tweaking the \c{bdep-new} command line rather than manually. For example,
if we look at the generated source directory \c{buildfile} and realize it
builds a \i{binful} library (that is, a library that includes source files and
therefore produces library binaries) while the upstream library is header-only,
it is much easier to fix this by re-running \c{bdep-new} with the \c{binless}
sub-option than by changing the \c{buildfiles} manually.
\N|Don't be tempted to start making manual changes at this stage even if you
cannot see anything else that can be fixed with a \c{bdep-new} re-run. This
is still a dry-run and we will recreate the package one more time in the
following section before starting manual adjustments.|
Besides examining the generated \c{buildfiles}, it's also a good idea to
build, test, and install the generated package to make sure everything ends up
where you expected and matches upstream where necessary. In particular, make
sure public headers are installed into the same location as upstream (unless
you have decided to deviate, of course) or at least it's clear how to tweak
the generated \c{buildfiles} to achieve this.
\N|The \c{bdep-new}-generated library is a simple \"Hello, World!\" example
that can nevertheless be built, tested, and installed. The idea here is to
verify it matches upstream using the generated source files before replacing
them with the upstream source file symlinks.
If you are using Windows, then you will need to temporarily replace the
\c{no-symexport} sub-option with \c{auto-symexport} in order to make the
generated library buildable. But do not forget to drop this sub-option in
the next step.|
Note that at this stage it's easiest to build, test, and install in the source
directory, skipping the \c{bdep} initialization of the package (which we would
have to de-initialize before we can re-run \c{bdep-new}). Continue with the
above example, the recommended sequence of commands would be:
\
$ cd libfoo/
$ b update
$ b test
$ rm -rf /tmp/install
$ b install config.install.root=/tmp/install
$ b clean
\
\N|One relatively common case where the installation location may not match
upstream are libraries that include their headers without the subdirectory
prefix (for example, \c{<foo_util.h>} instead of \c{<foo/util.h>}). In such
cases, in the \c{bdep-new} command, you want to use \i{prefix} rather that
\i{source subdirectory} (with the latter being the default). For example:
\
$ bdep new --lib,no-subdir,prefix=foo ...
\
See \l{bdep-new.xhtml#src-layout SOURCE LAYOUT} for details.|
Let's also briefly discuss other subdirectories and files found in the
\c{bdep-new}-generated \c{libfoo} package.
The \c{build/} subdirectory is the standard \c{build2} place for project-wide
build system information (see \l{b#intro-proj-struct Project Structure} for
details). We will look closer at its contents in the following sections.
In the root directory of our package we find the root \c{buildfile} and
package \c{manifest}. We will be tweaking both in the following steps. There
is also \c{README.md} which we will replace with the upstream symlink.
The \c{tests/} subdirectory is the standard \c{build2} tests subproject (see
\l{b#intro-operations-test Testing} for background and details). While you can
suppress its generation with the \c{no-tests} \c{bdep-new} sub-option, we
recommend that you keep it and use it as a starting point for porting upstream
tests or, if upstream doesn't provide any, for a basic \"smoke test\".
\N|You can easily add/remove/rename this \c{tests/} subproject. The only place
where it is mentioned explicitly and where you will need to make changes is
the root \c{buildfile}. In particular, if upstream provides examples that you
wish to port, it is recommended that you use a copy of the generated
\c{tests/} subproject as a starting point (not forgetting to add the
corresponding entry in the root \c{buildfile}).|
\h2#core-package-create|Create final package|
If you are satisfied with the \c{bdep-new} command line and there are no more
automatic adjustments you can squeeze out of it, then it's time to re-run
\c{bdep-new} one last time to create the final package.
\N|While redoing this step later will require more effort, especially if
you've made manual modifications to \c{buildfile} and \c{manifest}, nothing is
set in stone and it can be done again by simply removing the package
directory, removing (or editing, if you have multiple packages and only want
to redo some of them) \c{packages.manifest}, and starting over.|
This time, however, we will do things a bit differently in order to take
advantage of some additional automation offered by \c{bdep-new}.
Firstly, we will use the special \c{third-party} sub-option which is meant for
converting third-party projects to \c{build2}. Specifically, this sub-option
automatically enables \c{no-version} and \c{no-symexport} (unless
\c{auto-symexport} is specified). It also adds a number of values to
\c{manifest} that makes sense to specify in a package of a third-party
project. Finally, it generates the \c{PACKAGE-README.md} template which
describes how to use the package from a \c{build2}-based project (see the
\l{bpkg#manifest-package-description \c{package-description}} \c{manifest}
value for background).
Secondly, if the package directory already exists and contains certain files,
\c{bdep-new} can take this into account when generating the root \c{buildfile}
and package \c{manifest}. In particular, it will try to guess the license from
the \c{LICENSE} file and extract the summary from \c{README.md} and use this
information in \c{manifest} as well as generated \c{PACKAGE-README.md}.
\N|If the file names or formats used by upstream don't match those recognized
by \c{bdep-new}, then for now simply omit the corresponding files from the
package directory and add them later manually. Similarly, if an attempt to
extract the information is unsuccessful, we will have a chance to adjust it in
\c{manifest} later.
Specifically, for \c{README}, \c{bdep-new} recognizes \c{README.md},
\c{README.txt} and \c{README} but will only attempt to extract the summary
from \c{README.md}.
For license files, \c{bdep-new} recognizes \c{LICENSE}, \c{LICENSE.txt}
\c{LICENSE.md}, \c{COPYING}, and \c{UNLICENSE}.
For changes-related files, \c{bdep-new} recognizes \c{NEWS}, \c{CHANGES}, and
\c{CHANGELOG} in various cases as well as with the \c{.md}, \c{.txt}
extensions.|
Continuing with our \c{libfoo} example and assuming upstream provides the
\c{README.md}, \c{LICENSE}, and \c{NEWS} files, we first manually create the
package directory, then add the symlinks, and finally run \c{bdep-new} (notice
that we have replaced \c{no-version} and \c{no-symexport} with \c{third-party}
and omitted the package name from the \c{bdep-new} command line since we are
running from inside the package directory):
\
$ cd foo/ # Change to the package repository root.
$ rm -r libfoo/ packages.manifest
$ mkdir libfoo/
$ cd libfoo/ # Change to the package root.
$ ln -s ../upstream/README.md ./
$ ln -s ../upstream/LICENSE ./
$ ln -s ../upstream/NEWS ./
$ bdep new --package \
--lang c++,cpp \
--type lib,split,subdir=foo,no-subdir-source,third-party
\
The final contents of our package will look like this (\c{->} denotes a
symlink):
\
$ cd ../
$ tree libfoo/
libfoo/
├── build/
│ └── ...
├── include/
│ └── foo/
│ ├── buildfile
│ └── foo.hpp
├── src/
│ ├── buildfile
│ └── foo.cpp
├── tests/
│ ├── build/
│ │ └── ...
│ ├── basics/
│ │ ├── buildfile
│ │ └── driver.cpp
│ └── buildfile
├── buildfile
├── manifest
├── NEWS -> ../upstream/NEWS
├── LICENSE -> ../upstream/LICENSE
├── README.md -> ../upstream/README.md
└── PACKAGE-README.md
\
If auto-detection of \c{README}, \c{LICENSE}, and \c{NEWS} succeeds, then you
should see the \c{summary} and \c{license} values automatically populated in
\c{manifest} and the symlinked files listed in the root \c{buildfile}.
\h2#core-package-adjust-version|Adjust package version|
While adjusting the \c{bdep-new}-generated code is the subject of the
following sections, one tweak that we want to make right away is to change the
package version in the \c{manifest} file.
In this guide we will assume the upstream package uses semver (semantic
version) or semver-like (that is, has three version components) and will rely
on the \i{continuous versioning} feature of \c{build2} to make sure that each
commit in our package repository has a distinct version (see
\l{intro#guide-versioning-releasing Versioning and Release Management} for
background).
\N|If upstream does not use semver, then see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-semver.md
How do I handle projects that don't use semantic versioning?} and
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-version.md
How do I handle projects that don't use versions at all?} for available
options. If you decide to use the non-semver upstream version as is, then you
will have to forgo \i{continuous versioning} as well as the use of
\l{bdep-release(1)} for release management. The rest of the guide, however,
will still apply. In particular, you will still be able to use \l{bdep-ci(1)}
and \l{bdep-publish(1)} with a bit of extra effort.|
The overall plan to implement continuous versioning is to start with a
pre-release snapshot of the upstream version, keep it like that while we are
adjusting the \c{bdep-new}-generated package and committing our changes (at
which point we get distinct snapshot versions), and finally, when the package
is ready to publish, change to the final upstream version with the help of
\l{bdep-release(1)}. Specifically, if the upstream version is
\c{\i{X}.\i{Y}.\i{Z}}, then we start with the \c{\i{X}.\i{Y}.\i{Z}-a.0.z}
pre-release snapshot.
\N|In continuous versioning \c{\i{X}.\i{Y}.\i{Z}-a.0.z} means a snapshot after
the (non-existent) \c{0}th alpha pre-release of the \c{\i{X}.\i{Y}.\i{Z}}
version. See \l{intro#guide-versioning-releasing Versioning and Release
Management} for a more detailed explanation and examples.|
Let's see how this works for our \c{libfoo} example. Say, the upstream version
that we are packaging is \c{2.1.0}. This means we start with \c{2.1.0-a.0.z}.
\N|Naturally, the upstream version that we are using should correspond to the
commit of the \c{upstream} submodule we have added in the
\l{#core-repo-submodule Add upstream repository as \c{git} submodule} step.|
Next we edit the \c{manifest} file in the \c{libfoo} package and change the
\c{version} value to read:
\
version: 2.1.0-a.0.z
\
Let's also commit this initial state of the package for easier rollbacks:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Initialize package\"
\
\h#core-fill|Fill package with source code and add dependencies|
With the package skeleton ready, the next steps are to fill it with upstream
source code, add dependencies, and make any necessary manual adjustments to
the generated \c{buildfiles}, \c{manifest}, etc. If we do this all at once,
however, it can be hard to pin-point the cause of build failures. For example,
if we convert both the library and its tests right away and something doesn't
work, it can be hard to determine whether the mistake is in the library or in
the tests. As a result, we are going to split this work into a sequence or
smaller steps that incrementally replace the \c{bdep-new}-generated code with
upstream while allowing us to test each change individually. We will also
commit the changes on each step for easy rollbacks. Specifically, the overall
plan is as follows:
\ol|
\li|Initialize (\c{bdep-init}) the package in one or more build configurations.|
\li|Add dependencies, if any.|
\li|Fill the library with upstream source code.|
\li|Adjust project-wide and source subdirectory \c{buildfiles}.|
\li|Make a smoke test for the library.|
\li|Replace the smoke test with upstream tests.|
\li|Tweak root \c{buildfile} and \c{manifest}.||
The first three steps are the subject of this section with the following
sections covering the rest of the plan.
\N|As you become more experienced with packaging third-party projects for
\c{build2}, it may make sense to start combining or omitting some steps,
especially for simpler libraries. For example, if you see that a library
comes with a simple test that shouldn't cause any complications, then you
could omit the smoke test.|
\h2#core-fill-init|Initialize package in build configurations|
Before we start making any changes to the \c{bdep-new}-generated files, let's
initialize the package in at least one build configuration so that we are able
to build and test our changes (see \l{intro#guide Getting Started Guide} for
background on \c{bdep}-based development workflow). Continuing with our
\c{libfoo} example from the earlier steps:
\
$ cd foo/ # Change to the package repository root.
$ bdep init -C ../foo-gcc @gcc cc config.cxx=g++
\
\N|If you are initializing subsequent packages in the already created
configuration, then the command line will be just:
\
$ bdep init @gcc
\
|
Let's build and test the \c{bdep-new}-generated package to make sure
everything is in order:
\
$ bdep update
$ bdep test
$ bdep clean
\
You can create additional configurations, for example, if you have access to
several compilers. For instance, to create a build configuration for Clang:
\
$ bdep init -C ../foo-clang @clang cc config.cxx=clang++
\
If you would like to perform a certain operation on all the build
configurations, pass the \c{-a|--all} flag to \c{bdep}:
\
$ bdep update -a
$ bdep test -a
$ bdep clean -a
\
Let's also verify that the resulting package repository is clean (doesn't have
any uncommitted or untracked files):
\
$ git status
\
\h2#core-fill-depend|Add dependencies|
If the upstream project has any dependencies, now is a good time to specify
them so that when we attempt to build the upstream source code, they are
already present.
Identifying whether the upstream project has dependencies is not always easy.
The natural first places to check are the documentation and the existing build
system. Sometimes projects also bundle their dependencies with the project
source code (also called vendoring). So it makes sense to look around the
upstream repository for anything that looks like bundled dependencies.
Normally we would need to \"unbundle\" such dependencies when converting to
\c{build2} by instead specifying a dependency on an external package (see
\l{#dont-bundle Don't bundle dependencies} for background).
\N|While there are several reasons we insist on unbundling of dependencies,
the main one is that bundling can cause multiple, potentially conflicting
copies of the same dependency to exist in the build. This can cause subtle
build failures that are hard to understand and track down.|
One particularly common case to check for is bundling of the testing
framework, such as \l{https://cppget.org/catch2 \c{catch2}}, by C++
projects. If you have identified that the upstream tests depend on a testing
framework (whether bundled or not), see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for the recommended
way to deal with that.
\N|One special type of dependency which is easy to overlook is between
packages in the same package repository. For example, if we were packaging
both \c{libfoo} as well as the \c{foo} executable that depends on it, then the
\c{foo} package has a dependency on \c{libfoo} and it must be specified. In
this case we don't need to add anything to \c{repositories.manifest} and in
the \c{depends} entry (see below) in \c{foo}'s \c{manifest} we will normally
use the special \c{==\ $} version constraint, meaning \c{libfoo} should have
the same version as \c{foo} (see the \l{bpkg#manifest-package-depends
\c{depends} package \c{manifest} value} for details). For example:
\
depends: libfoo == $
\
|
If you have concluded that the upstream project doesn't have any dependencies,
then you can remove \c{repositories.manifest} from the package repository root
(unless you have already done so), commit this change, and skip the rest of
this section.
And if you are still reading, then we assume you have a list of dependencies
you need to add, preferably with their minimum required versions. If you could
not identify the minimum required version for a dependency, then you can
fallback to the latest available version, as will be described in a moment.
With the list of dependencies in hand, the next step is to determine whether
they are already available as \c{build2} packages. For that, head over to
\l{https://cppget.org cppget.org} and search for each dependency.
If you are unable to find a package for a dependency, then it means it hasn't
been packaged for \c{build2} yet. Check the places mentioned in the
\l{#core-repo-exists Check if package repository already exists} step to see
if perhaps someone is already working on the package. If not and the
dependency is not optional, then the only way forward is to first package
the dependency.
If you do find a package for a dependency, then note the section of the
repository (\c{stable}, \c{testing}, etc; see \l{intro#guide-repositories
Package Repositories} for background) from which the minimum required version
of the package is available. If you were unable to identify the minimum
required version, then note the latest version available from the \c{stable}
section.
Given the list of repository sections, edit the \c{repositories.manifest} file
in the package repository root and uncomment the entry for \c{cppget.org}:
\
:
role: prerequisite
location: https://pkg.cppget.org/1/stable
#trust: ...
\
Next, replace \c{stable} at the end of the \c{location} value with the least
stable section from your list. For example, if your list contains \c{stable},
\c{testing}, and \c{beta}, then you need \c{beta} (the sections form a
hierarchy and so \c{beta} includes \c{testing} which in turn includes
\c{stable}).
\N|If you wish, you can also uncomment the \c{trust} value and replace \c{...}
with the \l{https://cppget.org/?about repository fingerprint}. This way you
won't be prompted to confirm the repository authenticity on the first
fetch. See \l{intro#guide-add-remove-deps Adding and Removing Dependencies}
for details.|
Once this is done, edit \c{manifest} in package root and add the \c{depends}
value for each dependency. See \l{intro#guide-add-remove-deps Adding and
Removing Dependencies} for background. In particular, here you will use the
minimum required version (or the latest available) to form a version
constraint. Which constraint operator to use will depend on the dependency's
versioning policies. If the dependency uses semver, then a \c{^}-based
constraint is a sensible default.
As an example, let's say our \c{libfoo} depends on \c{libz}, \c{libasio}, and
\c{libsqlite3}. To specify these dependencies we would add the following
entries to its \c{manifest}:
\
depends: libz ^1.2.0
depends: libasio ^1.28.0
depends: libsqlite3 ^3.39.4
\
With all the dependencies specified, let's now synchronize the state of the
build configurations with our changes by running \l{bdep-sync(1)} from the
package repository root:
\
$ bdep sync -a
\
This command should first fetch the metadata for the repository we specified
in \c{repositories.manifest} and then fetch, unpack and configure each
dependency that we specified in \c{manifest}.
\N|If you have any build-time dependencies (see
\l{intro#guide-build-time-linked Build-Time Dependencies and Linked
Configurations} for background), then you will get a warning about the
corresponding \c{config.import.*} variable being unused and therefore
dropped. This is because we haven't yet added the corresponding \c{import}
directives to our \c{buildfiles}. For now you can ignore this warning, which
we will fix later, when we adjust the generated \c{buildfiles}.|
We can examine the resulting state, including the version of each dependency,
with \l{bdep-status(1)}:
\
$ bdep status -ai
\
The last step for this section is to commit our changes:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Add dependencies\"
\
\h2#core-fill-source|Fill with upstream source code|
Now we are ready to begin replacing the \c{bdep-new}-generated files with
upstream source code symlinks. We start with the library's header and source
files. Continuing with our \c{libfoo} example, this is what we currently have
(notice that \c{LICENSE}, \c{README.md}, and \c{NEWS} are already symlinks to
upstream):
\
$ cd foo/ # Change to the package repository root.
$ tree libfoo/
libfoo/
├── build/
│ └── ...
├── include/
│ └── foo/
│ ├── buildfile
│ └── foo.hpp
├── src/
│ ├── buildfile
│ └── foo.cpp
├── tests/
│ └── ...
├── buildfile
├── manifest
├── NEWS -> ../upstream/NEWS
├── LICENSE -> ../upstream/LICENSE
├── README.md -> ../upstream/README.md
└── PACKAGE-README.md
\
Now we replace generated \c{include/foo/foo.hpp} with the library's real
headers and \c{src/foo.cpp} with its real source files:
\
$ cd libfoo/ # Change to the package root.
$ cd include/foo/
$ rm foo.hpp
$ ln -s ../../../upstream/include/foo/*.hpp ./
$ cd -
$ cd src/
$ rm foo.cpp
$ ln -s ../../upstream/src/*.hpp ./
$ ln -s ../../upstream/src/*.cpp ./
$ cd -
$ tree libfoo/
libfoo/
├── build/
│ └── ...
├── include/
│ └── foo/
│ ├── buildfile
│ ├── core.hpp -> ../../../upstream/include/foo/core.hpp
│ └── util.hpp -> ../../../upstream/include/foo/util.hpp
├── src/
│ ├── buildfile
│ ├── impl.hpp -> ../../upstream/src/impl.hpp
│ ├── core.cpp -> ../../upstream/src/core.cpp
│ └── util.cpp -> ../../upstream/src/util.cpp
├── tests/
│ └── ...
└── ...
\
Note that the wildcards used above may not be enough in all situations and
it's a good idea to manually examine the relevant upstream directories and
make sure nothing is missing. Specifically, look out for:
\ul|
\li|Header/sources with other extensions, for example, C, Objective-C, etc.|
\li|Other files that may be needed, for example, \c{.def}, \c{config.h.in}, etc.|
\li|Subdirectories that contain more header/source files.||
If upstream contains subdirectories with additional header/source files, then
you can symlink entire subdirectories instead of doing it file by file. For
example, let's say \c{libfoo}'s upstream source directory contains the
\c{impl/} subdirectory with additional source files:
\
$ cd src/
$ ln -s ../../upstream/impl ./
$ cd -
$ tree libfoo/
libfoo/
├── build/
│ └── ...
├── include/
│ └── ...
├── src/
│ ├── impl/ -> ../../upstream/src/impl/
│ │ ├── bar.cpp
│ │ └── baz.cpp
│ ├── buildfile
│ ├── impl.hpp -> ../../upstream/src/impl.hpp
│ ├── core.cpp -> ../../upstream/src/core.cpp
│ └── util.cpp -> ../../upstream/src/util.cpp
├── tests/
│ └── ...
└── ...
\
Wouldn't it be nice if we could symlink the entire top-level subdirectories
(\c{include/foo/} and \c{src/} in our case) instead of symlinking individual
files? As discussed in \l{#core-package-craft-cmd Craft \c{bdep new} command
line to create package}, we can, but we will need to change the package
layout. Specifically, we will need to move the \c{buildfiles} out of the
source subdirectories with the help of the \c{buildfile-in-prefix} sub-option
of \c{bdep-new}. In the above case, we will also need to invent a source
subdirectory in \c{src/}. Whether this is a worthwhile change largely depends
on how many files you have to symlink individually. If it's just a handful,
then it's probably not worth the complication, especially if you have to
invent source subdirectories. On the other hand, if you are looking at
symlinking hundreds of files, changing the layout makes perfect sense.
\N|One minor drawback of symlinking entire directories is that you cannot
easily patch individual upstream files (see \l{#howto-patch-upstream-source
How do I patch upstream source code?}).
You will also need to explicitly list such directories as symlinks in
\c{.gitattributes} if you want your package to be usable from the \c{git}
repository directly on Windows. See
\l{https://build2.org/article/symlinks.xhtml#windows Symlinks and Windows} for
details.|
We won't be able to test this change yet because to make things build we will
most likely also need to tweak the generated \c{buildfiles}, which is the
subject of the next section. However, it still makes sense to commit our
changes to make rollbacks easier:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Add upstream source symlinks\"
\
\h#core-adjust-build|Adjust project-wide and source \c{buildfiles}|
With source code and dependencies added, the next step is to adjust the
regenerated \c{buildfiles} that build the library. This involves two places:
the project-wide build system files in \c{build/} and the source subdirectory
\c{buildfiles} (in \c{include/} and \c{src/} for our \c{libfoo} example).
\h2#core-adjust-build-wide|Adjust project-wide build system files in \c{build/}|
We start with reviewing and adjusting the files in the \c{build/} subdirectory
of our package, where you will find three files:
\
$ cd foo/ # Change to the package repository root.
$ tree libfoo/
libfoo/
├── build/
│ ├── bootstrap.build
│ ├── root.build
│ └── export.build
└── ...
\
To recap, the first two contain the project-wide build system setup (see
\l{b#intro-proj-struct Project Structure} for details) while the last is an
export stub that facilitates the importation of targets from our package (see
\l{b#intro-import Target Importation} for details).
Normally you don't need to change anything in \c{bootstrap.build} \- all it
does is specify the build system project name and load a standard set of core
build system modules. Likewise, \c{export.build} is ok as generated unless you
need to do something special, like exporting targets from different
subdirectories of your package.
While \c{root.build} is also often good as is, situations where you may
need to tweak it are not uncommon and include:
\ul|
\li|Loading an additional build system module.
For example, if your package makes use of Objective-C/C++ (see \l{b#c-objc
Objective-C Compilation} and \l{b#cxx-objcxx Objective-C++ Compilation})
or Assembler (see \l{b#c-as-cpp Assembler with C Preprocessor
Compilation}), then \c{root.build} would be the natural place to load the
corresponding modules.
\N|If your package uses a mixture of C and C++, then it's recommended to
set this up using the \c{--lang} sub-option of \c{bdep-new} rather
than manually. For example:
\
$ bdep new --lang c++,c ...
\
||
\li|Specifying package configuration variables.
If upstream provides the ability to configure their code, for example to
enable optional features, then you may want to translate this to
\c{build2} configuration variables, which are specified in \c{root.build}
(see \l{b#proj-config Project Configuration} for background and details).
Note that you don't need to add all the configuration variables right
away. Instead, you could first handle the \"core\" functionality which
doesn't require any configuration and then add the configuration variables
one by one while also making the corresponding changes in \c{buildfiles}.
\N|One type of configuration that you should normally not expose when
packaging for \c{build2} is support for both header-only and compiled
modes. See \l{b#dont-header-only Don't make library header-only if it
can be compiled} for details.|||
Also, in C++ projects, if you don't have any inline or template files, then
you can drop the assignment of the file extension for the \c{ixx{\}} and
\c{txx{\}} target types, respectively.
If you have added any configuration variables and would like to use
non-default values for some of them in your build, then you will need to
reconfigure the package. For example, let's say we have added the
\c{config.libfoo.debug} variable to our \c{libfoo} package which enables
additional debugging facilities in the library. This is how we can reconfigure
all our builds to enable this functionality:
\
$ bdep sync -a config.libfoo.debug=true
\
If you have made any changes, commit them (similar to the previous step, we
cannot test things just yet):
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust project-wide build system files\"
\
\h2#core-adjust-build-src|Adjust source subdirectory \c{buildfiles}|
The last step we need to perform before we can try to build our library is to
adjust its \c{buildfiles}. These \c{buildfiles} are found in the source
subdirectory or, if we used the \c{buildfile-in-prefix} \c{bdep-new}
sub-option, in the prefix directory. There will be two \c{buildfiles} if we
use the split layout (\c{split} sub-option) or a single \c{buildfile} in the
combined layout. The single \c{buildfile} in the combined layout contains
essentially the same definitions as the split \c{buildfiles} but combined into
one and with some minor simplifications that this allows. Here we will assume
the split layout and continue with our \c{libfoo} from the previous
sections. To recap, here is the layout we've got with the \c{buildfiles} of
interest found in \c{include/foo/} and in \c{src/}:
\
libfoo/
├── build/
│ └── ...
├── include/
│ └── foo/
│ ├── buildfile
│ ├── core.hpp -> ../../../upstream/include/foo/core.hpp
│ └── util.hpp -> ../../../upstream/include/foo/util.hpp
├── src/
│ ├── buildfile
│ ├── impl.hpp -> ../../upstream/src/impl.hpp
│ ├── core.cpp -> ../../upstream/src/core.cpp
│ └── util.cpp -> ../../upstream/src/util.cpp
├── tests/
│ └── ...
└── ...
\
\N|If instead of a library you are packaging an executable, you can skip
directly to \l{#core-adjust-build-src-source-exe Adjust source \c{buildfile}:
executables}.|
\h2#core-adjust-build-src-header|Adjust header \c{buildfile}|
The \c{buildfile} in \c{include/foo/} is pretty simple:
\N|The \c{buildfile} in your package may look slightly different, depending on
the exact \c{bdep-new} sub-options used. However, all the relevant definitions
discussed below should still be easily recognizable.|
\
pub_hdrs = {hxx ixx txx}{**}
./: $pub_hdrs
# Install into the foo/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/foo/
install.subdirs = true
}
\
Normally, the only change that you would make to this \c{buildfile} is to
adjust the installation location of headers (see \l{b#intro-operations-install
Installing} for background). In particular, if our headers were included
without the \c{<foo/...>} prefix but instead contained the library name in
their names (for example, \c{foo-util.hpp}), then the installation setup would
instead look like this:
\
# Install directly into say, /usr/include/ recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/
install.subdirs = true
}
\
If the library doesn't have any headers in nested subdirectories (for example,
\c{<foo/util/string.hpp>}), you can drop the \c{install.subdirs} variable:
\
# Install into the foo/ subdirectory of, say, /usr/include/.
#
{hxx ixx txx}{*}: install = include/foo/
\
\N|In the combined layout, the installation-related definitions are at the end
of the combined \c{buildfile}.
Compared to the split layout where the public and private headers are
separated physically, in the combined layout you may need to achieve the same
result (that is, avoid installing private headers) at the build system
level. If the library provides only a handful of public headers and this set
is unlikely to change often, then listing them explicitly is the most
straightforward approach. For example (the \c{@./} qualifier tells \c{build2}
they are in the source directory):
\
# Only install public headers into, say, /usr/include/.
#
h{foo}@./ h{foo_version}@./: install = include/
h{*}: install = false
\
|
See also \l{#howto-extra-header-install-subdir How do I handle extra header
installation subdirectory?}
\h2#core-adjust-build-src-source|Adjust source \c{buildfile}: overview|
Next is the \c{buildfile} in \c{src/}:
\N|Again, the \c{buildfile} in your package may look slightly different,
depending on the exact \c{bdep-new} sub-options used. However, all the
relevant definitions discussed below should still be easily recognizable.
For a binless (header-only) library, this \c{buildfile} will contain only a
small subset of the definitions shown below. See
\l{https://github.com/build2/HOWTO/blob/master/entries/make-header-only-library.md
How do I make a header-only C/C++ library?} for additional considerations when
packaging header-only libraries.|
\
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
#import xxxx_libs += libhello%lib{hello}
# Public headers.
#
pub = [dir_path] ../include/foo/
include $pub
pub_hdrs = $($pub/ pub_hdrs)
lib{foo}: $pub/{$pub_hdrs}
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs
# Build options.
#
out_pfx_inc = [dir_path] $out_root/include/
src_pfx_inc = [dir_path] $src_root/include/
out_pfx_src = [dir_path] $out_root/src/
src_pfx_src = [dir_path] $src_root/src/
cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \
\"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD
#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
cxx.export.libs = $intf_libs
}
#liba{foo}: cxx.export.poptions += -DFOO_STATIC
#libs{foo}: cxx.export.poptions += -DFOO_SHARED
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{foo}: bin.lib.version = \"-$version.project_id\"
else
lib{foo}: bin.lib.version = \"-$version.major.$version.minor\"
# Don't install private headers.
#
{hxx ixx txx}{*}: install = false
\
\h2#core-adjust-build-src-source-clean|Adjust source \c{buildfile}: cleanup|
As a first step, let's remove all the definitions that we don't need in our
library. The two common pieces of functionality that are often not needed
are support for auto-generated headers (such as \c{config.h} generated from
\c{config.h.in}) and dependencies on other libraries.
If you don't have any auto-generated headers, then remove all the assignments
and expansions of the \c{out_pfx_inc} and \c{out_pfx_src} variables. Here
is what the relevant lines in the above \c{buildfile} should look like after
this change:
\
# Build options.
#
src_pfx_inc = [dir_path] $src_root/include/
src_pfx_src = [dir_path] $src_root/src/
cxx.poptions =+ \"-I$src_pfx_src\" \"-I$src_pfx_inc\"
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$src_pfx_inc\"
}
\
\N|If you do have auto-generated headers, then in the split layout you can
remove \c{out_pfx_inc} if you only have private auto-generated headers and
\c{out_pfx_src} if you only have public ones.|
\N|In the combined layout the single \c{buildfile} does not set the
\c{*_pfx_*} variables. Instead it uses the \c{src_root} and \c{out_root}
variables directly. For example:
\
# Build options.
#
cxx.poptions =+ \"-I$out_root\" \"-I$src_root\"
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_root\" \"-I$src_root\"
}
\
To remove support for auto-generated headers in the combined \c{buildfile},
simply remove the corresponding \c{out_root} expansions:
\
# Build options.
#
cxx.poptions =+ \"-I$src_root\"
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$src_root\"
}
\
If you only have private auto-generated headers, then only remove the
expansion from \c{cxx.export.poptions}.|
If you don't have any dependencies, then remove all the assignments and
expansions of the \c{intf_libs} and \c{intf_libs} variables. That is,
the following lines in the original \c{buildfile}:
\
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
#import xxxx_libs += libhello%lib{hello}
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
cxx.export.libs = $intf_libs
}
\
Become just these:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx ixx txx cxx}{**}
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
}
\
\h2#core-adjust-build-src-source-dep|Adjust source \c{buildfile}: dependencies|
If you do have dependencies, then let's handle them now.
\N|Here we will assume dependencies on other libraries, which is the common
case. If you have dependencies on executables, for example, source code
generators, see \l{intro#guide-build-time-linked Build-Time Dependencies and
Linked Configurations} on how to handle that. In this case you will also need
to reconfigure your package after adding the corresponding \c{import}
directives in order to re-acquire the previously dropped \c{config.import.*}
values. Make sure to also pass any configuration variables you specified in
\l{#core-adjust-build-wide Adjust project-wide build system files in
\c{build/}}. For example:
\
$ bdep sync -a --disfigure config.libfoo.debug=true
\
|
For each library that your package depends on (and which you have added
to \c{manifest} in the \l{#core-fill-depend Add dependencies} step),
you need to first determine whether it's an interface or implementation
dependency and then import it either into the \c{intf_libs} or \c{impl_libs}
variable, respectively.
See \l{b#intro-lib Library Exportation and Versioning} for background on the
interface vs implementation distinction. But as a quick rule of thumb, if the
library you are packaging includes a header from the dependency library in one
of its public headers, then it's an interface dependency. Otherwise, it's an
implementation dependency.
Continuing with our \c{libfoo} example, as we have established in
\l{#core-fill-depend Add dependencies}, it depends on \c{libasio}, \c{libz},
and \c{libsqlite3} and let's say we've determined that \c{libasio} is an
interface dependency because it's included from \c{include/foo/core.hpp} while
the other two are implementation dependencies because they are only included
from \c{src/}. Here is how we would change our \c{buildfile} to import them:
\
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
import intf_libs += libasio%lib{asio}
import impl_libs += libz%lib{z}
import impl_libs += libsqlite3%lib{sqlite3}
\
You can tidy this a bit further if you would like:
\
import intf_libs = libasio%lib{asio}
import impl_libs = libz%lib{z}
import impl_libs += libsqlite3%lib{sqlite3}
\
\N|If you don't have any implementation or interface dependencies, you can
remove the assignment and all the expansion of the corresponding \c{*_libs}
variable.|
Note also that system libraries like \c{-lm}, \c{-ldl} on UNIX or
\c{advapi32.lib}, \c{ws2_32.lib} on Windows should not be imported. Instead,
they should be listed in the \c{c.libs} or \c{cxx.libs} variables. See
\l{https://github.com/build2/HOWTO/blob/master/entries/link-system-library.md
How do I link a system library} for details.
\h2#core-adjust-build-src-source-pub|Adjust source \c{buildfile}: public headers|
With the unnecessary parts of the \c{buildfile} cleaned up and dependencies
handled, let's discuss the common changes to the remaining definitions, going
from top to bottom. We start with the public headers block:
\
# Public headers.
#
pub = [dir_path] ../include/foo/
include $pub
pub_hdrs = $($pub/ pub_hdrs)
lib{foo}: $pub/{$pub_hdrs}
\
This block gets hold of the list of public headers and makes them
prerequisites of the library. Normally you shouldn't need to make any changes
here. If you need to exclude some headers, it should be done in the
header \c{buildfile} in the \c{include/} directory.
\N|In the combined layout the single \c{buildfile} does not have such code.
Instead, all the headers are covered by the wildcard pattern in the following
block.|
\h2#core-adjust-build-src-source-src|Adjust source \c{buildfile}: sources, private headers|
The next block deals with sources, private headers, and dependencies, if any:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs
\
By default it will list all the relevant files as prerequisites of the
library, starting from the directory of the \c{buildfile} and including all
the subdirectories, recursively (see \l{b#name-patterns Name Patterns} for
background on wildcard patterns).
If your C++ package doesn't have any inline or template files, then you can
remove the \c{ixx} and \c{txx} target types, respectively (which would be
parallel to the change made in \c{root.build}; see \l{#core-adjust-build-wide
Adjust project-wide build system files in \c{build/}}). For example:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx cxx}{**} $impl_libs $intf_libs
\
\N|Source files other than C/C++ (for example, Assembler, Objective-C/C++) are
dealt with in \l{#core-adjust-build-src-source-ext Adjust source
\c{buildfile}: extra requirements} below.|
The other common change to this block is the exclusion of certain files or
making them conditionally included. As an example, let's say in our \c{libfoo}
the source subdirectory contains a bunch of \c{*-test.cpp} files which are
unit tests and should not be listed as prerequisites of a library. Here is how
we can exclude them:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx cxx}{** -**-test} $impl_libs $intf_libs
\
Let's also assume our \c{libfoo} contains \c{impl-win32.cpp} and
\c{impl-posix.cpp} which provide alternative implementations of the same
functionality for Windows and POSIX and therefore should only be included as
prerequisites on the respective platforms. Here is how we can handle that:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx cxx}{** -impl-win32 -impl-posix -**-test}
lib{foo}: cxx{impl-win32}: include = ($cxx.target.class == 'windows')
lib{foo}: cxx{impl-posix}: include = ($cxx.target.class != 'windows')
lib{foo}: $impl_libs $intf_libs
\
There are two nuances in the above example worth highlighting. Firstly, we
have to exclude the files from the wildcard pattern before we can
conditionally include them. Secondly, we have to always link libraries
last. In particular, the following is a shorter but an incorrect version of the
above:
\
lib{foo}: {hxx cxx}{** -impl-win32 -impl-posix -**-test} \
$impl_libs $intf_libs
lib{foo}: cxx{impl-win32}: include = ($cxx.target.class == 'windows')
lib{foo}: cxx{impl-posix}: include = ($cxx.target.class != 'windows')
\
\N|You may also be tempted to use the \c{if} directive instead of the
\c{include} variable for conditional prerequisites. For example:
\
if ($cxx.target.class == 'windows')
lib{foo}: cxx{impl-win32}
else
lib{foo}: cxx{impl-posix}
\
This would also be incorrect. For background and details, see
\l{https://github.com/build2/HOWTO/blob/master/entries/keep-build-graph-config-independent.md
How do I keep the build graph configuration-independent?}|
\h2#core-adjust-build-src-source-opt|Adjust source \c{buildfile}: build and export options|
The next two blocks are the build and export options, which we will discuss
together:
\
# Build options.
#
out_pfx_inc = [dir_path] $out_root/include/
src_pfx_inc = [dir_path] $src_root/include/
out_pfx_src = [dir_path] $out_root/src/
src_pfx_src = [dir_path] $src_root/src/
cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \
\"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD
#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
cxx.export.libs = $intf_libs
}
#liba{foo}: cxx.export.poptions += -DFOO_STATIC
#libs{foo}: cxx.export.poptions += -DFOO_SHARED
\
The build options are in effect when the library itself is being built and the
exported options are propagated to the library consumers (see \l{b#intro-lib
Library Exportation and Versioning} for background on exported options). For
now we will ignore the commented out lines that add \c{-DFOO_STATIC*} and
\c{-DFOO_SHARED*} macros \- they are for symbol exporting and we will discuss
this topic separately.
If the library you are packaging only relied on platform-independent APIs,
then chances are you won't need to change anything here. On the other hand, if
it does anything platform-specific, then you will most likely need to add some
options.
As discussed in the \l{b#intro-dirs-scopes Output Directories and Scopes}
section of the build system introduction, there is a number of variables that
are used to specify compilation and linking options, such as \c{*.poptions}
(\c{cxx.poptions} in the above example), \c{*.coptions}, etc. The below table
shows all of them with their rough \c{make} equivalents in the third column:
\
*.poptions preprocess CPPFLAGS
*.coptions compile CFLAGS/CXXFLAGS
*.loptions link LDFLAGS
*.aoptions archive ARFLAGS
*.libs system libraries LIBS/LDLIBS
\
The recommended approach here is to study the upstream build system and copy
custom compile/link options to the appropriate \c{build2} variables. Note,
however, that doing it thoughtlessly/faithfully by copying all the options may
not always be a good idea. See
\l{https://github.com/build2/HOWTO/blob/master/entries/compile-options-in-buildfile.md
Which C/C++ compile/link options are OK to specify in a project's buildfile?}
for the guidelines.
\N|If you are packaging a library that includes a large number of optional
features, it may be unclear which of them would make sense to enable by
default. The notorious example of this situation is
\l{https://github.com/build2-packaging/sqlite \c{libsqlite3}} which provides
hundreds of preprocessor macros to enable or tune various aspects of its
functionality.
The recommended approach in cases like this is to study the configuration of
such a library in distributions like Debian and Fedora and use the same
defaults. In particular, this will allow us to substitute the \c{build2}
package with the system-installed version.|
Oftentimes, custom options must only be specified for certain target platforms
or when using a certain compiler. While \c{build2} provides a large amount of
information to identify the build configuration as well as more advanced
\c{buildfile} language mechanisms (such as \l{b#intro-switch Pattern
Matching}) to make sense of it, this is a large topic for which we refer you
to \l{b The \c{build2} Build System} manual. Additionally,
\l{https://github.com/build2-packaging github.com/build2-packaging} now
contains a large number of packages that you can study and search for
examples.
\N|While exporting preprocessor macros to communicate configuration is a
fairly common technique, it has a number of drawbacks and limitations.
Specifically, a large number of such macros will add a lot of noise to the
consumer's compilation command lines (especially if multiple libraries indulge
in this). Plus, the information conveyed by such macros is limited to simple
values and is not easily accessible in consumer \c{buildfiles}.
To overcome these drawbacks and limitations, \c{build2} provides a mechanism
for conveying metadata with C/C++ libraries (and executables). See,
\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md
How do I convey additional information (metadata) with executables and C/C++
libraries?} for details.
Note that outright replacing the preprocessor macros with metadata can be done
if this information is only used by the library consumers. In other words, if
the library's public headers rely on the presence of such macros, then we have
no choice but to export them, potentially also providing the metadata so that
this information is easily accessible from \c{buildfiles}.|
Let's consider a representative example based on our \c{libfoo} to get a sense
of what this normally looks like as well as to highlight a few nuances. We
will assume our \c{libfoo} requires either the \c{FOO_POSIX} or \c{FOO_WIN32}
macro to be defined during the build in order to identify the target
platform. Additionally, extra features can be enabled by defining
\c{FOO_EXTRAS}, which should be done both during the build and for consumption
(so this macro must also be exported). Next, this library requires the
\c{-fno-strict-aliasing} compile option for the GCC-class compilers (GCC,
Clang, etc). Finally, we need to link \c{pthread} on POSIX and \c{ws2_32.lib}
on Windows. This is how we would work all this into the above fragment:
\
# Build options.
#
out_pfx_inc = [dir_path] $out_root/include/
src_pfx_inc = [dir_path] $src_root/include/
out_pfx_src = [dir_path] $out_root/src/
src_pfx_src = [dir_path] $src_root/src/
cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \
\"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
cxx.poptions += -DFOO_EXTRAS
if ($cxx.target.class == 'windows')
cxx.poptions += -DFOO_WIN32
else
cxx.poptions += -DFOO_POSIX
#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD
#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD
if ($cxx.class == 'gcc')
cxx.coptions += -fno-strict-aliasing
switch $cxx.target.class, $cxx.target.system
{
case 'windows', 'mingw32'
cxx.libs += -lws2_32
case 'windows'
cxx.libs += ws2_32.lib
default
cxx.libs += -pthread
}
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" -DFOO_EXTRAS
cxx.export.libs = $intf_libs
}
#liba{foo}: cxx.export.poptions += -DFOO_STATIC
#libs{foo}: cxx.export.poptions += -DFOO_SHARED
\
There are a few nuances in the above code worth keeping in mind. Firstly,
notice that we append (rather than assign) to all the non-export variables
(\c{*.poptions}, \c{*.coptions}, \c{*.libs}). This is because they may already
contain some values specified by the user with their \c{config.*.*}
counterparts. On the other hand, the \c{*.export.*} variables are assigned.
Secondly, the order in which we append to the variables is important for the
value to accumulate correctly. You want to first append all the scope-level
values, then target type/pattern-specific, and finally any target-specific;
that is, from more general to more specific (see \l{b#intro-lang Buildfile
Language} for background). To illustrate this point, let's say in our
\c{libfoo}, the \c{FOO_POSIX} or \c{FOO_WIN32} macro are only necessary when
compiling \c{util.cpp}. Below would be the correct order of assigning to
\c{cxx.poptions}:
\
cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \
\"-I$out_pfx_inc\" \"-I$src_pfx_inc\"
cxx.poptions += -DFOO_EXTRAS
#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD
#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD
if ($cxx.target.class == 'windows')
{obja objs}{util}: cxx.poptions += -DFOO_WIN32
else
{obja objs}{util}: cxx.poptions += -DFOO_POSIX
\
\N|Note that target-specific \c{*.poptions} and \c{*.coptions} must be
specified on the object file targets while \c{*.loptions} and \c{*.libs} \- on
the library or executable targets.|
\h2#core-adjust-build-src-source-sym|Adjust source \c{buildfile}: symbol exporting|
Let's now turn to a special sub-topic of the build and export options that
relates to the shared library symbol exporting. To recap, a shared library on
Windows must explicitly specify the symbols (functions and global data) that
it wishes to make accessible by its consumers (executables and other shared
libraries). This can be achieved in three different ways: The library can
explicitly mark in its source code the names whose symbols should be
exported. Alternatively, the library can provide a \c{.def} file to the linker
that lists the symbols to be exported. Finally, the library can request
the automatic exporting of all symbols, which is the default semantics on
non-Windows platforms. Note that the last two approaches only work for
exporting functions, not data, unless special extra steps are taken by the
library consumers. Let's discuss each of these approaches in the reverse
order, that is, starting with the automatic symbol exporting.
The automatic symbol exporting is implemented in \c{build2} by generating a
\c{.def} file that exports all the relevant symbols. It requires a few
additional definitions in our \c{buildfile} as described in
\l{b#cc-auto-symexport Automatic DLL Symbol Exporting}. You can automatically
generate the necessary setup with the \c{auto-symexport} \c{bdep-new}
sub-option.
Using a custom \c{.def} file to export symbols is fairly straightforward:
simply list it as a prerequisite of the library and it will be automatically
passed to the linker when necessary. For example:
\
# Private headers and sources as well as dependencies.
#
lib{foo}: {hxx cxx}{**} $impl_libs $intf_libs def{foo}
\
\N|Some third-party projects automatically generate their \c{.def} file. In
this case you can try to re-create the same generation in the \c{buildfile}
using an ad hoc recipe (or the \l{b#module-in \c{in}} or
\l{https://github.com/build2/libbuild2-autoconf \c{autoconf}} build system
modules). If that doesn't look possible (for example, if the generation logic
is complex and is implemented in something like Perl or Python), then you can
try your luck with automatic symbol exporting. Failing that, the only
remaining option is to use a pre-generated \c{.def} file in the \c{build2}
build.|
The last approach is to explicitly specify in the source code which symbols
must be exported by marking the corresponding declarations with
\c{__declspec(dllexport)} during the library build and
\c{__declspec(dllimport)} during the library use. This is commonly achieved
with a macro, customarily called \c{*_EXPORT} or \c{*_API}, which is defined
to one of the above specifiers based on whether static or shared library is
being built or is being consumed, which, in turn, is also normally signaled
with a few more macros, such as \c{*_BUILD_DLL} and \c{*_USE_STATIC}.
\N|Because this approach requires extensive changes to the source code, you
will normally only use it in your \c{build2} build if it is already used in
the upstream build.|
In \c{build2} you can explicitly signal any of the four situations
(shared/static, built/consumed) by uncommenting and adjusting the following
four lines in the build and export options blocks:
\
# Build options.
#
...
#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD
#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD
# Export options.
#
...
#liba{foo}: cxx.export.poptions += -DFOO_STATIC
#libs{foo}: cxx.export.poptions += -DFOO_SHARED
\
As an example, let's assume our \c{libfoo} defines in one of its headers the
\c{FOO_EXPORT} macro based on the \c{FOO_BUILD_DLL} (shared library is being
build) and \c{FOO_USE_STATIC} (static library is being used) macros that it
expects to be appropriately defined by the build system. This is how we would
modify the above fragment to handle this setup:
\
# Build options.
#
...
objs{*}: cxx.poptions += -DFOO_BUILD_DLL
# Export options.
#
...
liba{foo}: cxx.export.poptions += -DFOO_USE_STATIC
\
\h2#core-adjust-build-src-source-ver|Adjust source \c{buildfile}: shared library version|
The final few lines in the above \c{buildfile} deal with shared library binary
(ABI) versioning:
\
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{foo}: bin.lib.version = \"-$version.project_id\"
else
lib{foo}: bin.lib.version = \"-$version.major.$version.minor\"
\
The \c{bdep-new}-generated setup arranges for the platform-independent
versioning where the package's major and minor version components are embedded
into the shared library binary name (and \c{soname}) under the assumption
that only patch versions are ABI-compatible.
The two situations where you would want to change this are when the above
assumption does not hold and/or when upstream provides platform-specific
shared library versions which you would like to re-create in your \c{build2}
build. See \l{b#intro-lib Library Exportation and Versioning} for background
and details.
\h2#core-adjust-build-src-source-exe|Adjust source \c{buildfile}: executables|
If instead of a library you are packaging an executable, then, as mentioned
earlier, it will most likely be a combined layout with a single \c{buildfile}.
This \c{buildfile} will also be much simpler compared to the library's. For
example, give the following \c{bdep-new} command:
\
$ bdep new --package \
--lang c++ \
--type exe,no-subdir,prefix=foo,export-stub \
foo
\
The resulting source \c{buildfile} will look like this:
\
libs =
#import libs += libhello%lib{hello}
exe{foo}: {hxx ixx txx cxx}{**} $libs testscript
out_pfx = [dir_path] $out_root/foo/
src_pfx = [dir_path] $src_root/foo/
cxx.poptions =+ \"-I$out_pfx\" \"-I$src_pfx\"
\
If the executable doesn't have any inline/template/header files, then you can
remove the \c{ixx}/\c{txx}/\c{hxx} target types, respectively (which would be
parallel to the change made in \c{root.build}; see \l{#core-adjust-build-wide
Adjust project-wide build system files in \c{build/}}). For example:
\
exe{foo}: {hxx cxx}{**} $libs testscript
\
If the source code includes its own headers with the \c{\"\"} style inclusion
(or doesn't have any headers), then we can also get rid of \c{out_pfx} and
\c{src_pfx}. For example:
\
libs =
#import libs += libhello%lib{hello}
exe{foo}: {hxx ixx txx cxx}{**} $libs testscript
\
\N|Unfortunately, it's not uncommon for projects that provide both a library
and an executable, for the executable source code to include public and/or
private library headers with the relative \c{\"\"} style inclusion. For
example:
\
#include \"../../libfoo/include/foo/util.hpp\"
#include \"../../libfoo/src/impl.hpp\"
\
This approach won't work in \c{build2} since the two packages may end up in
different directories or the library could even be installed. There are two
techniques that can be used to work around this issue (other than patching the
upstream source code).
For public headers we can provide, in the appropriate places within the
executable package, \"thunk headers\" with the same names as public headers
that simply include the corresponding public header from the library using the
\c{<>} style inclusion.
For private headers we can provide, again in the appropriate places within the
executable package, our own symlinks for a subset of private headers. Note
that this will only work if the use of private headers within the executable
does not depend on any symbols that are not exported by the library (failing
that, the executable will have to always link to the static variant of the
library).
For a real example of both of these techniques, see the
\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository.|
Dealing with dependencies in executables is similar to libraries except that
here we don't have the interface/implementation distinction; see the
\l{#core-adjust-build-src-source-dep Adjust source \c{buildfile}:
dependencies} step. For example:
\
import libs = libfoo%lib{foo}
exe{foo}: {hxx ixx txx cxx}{**} $libs testscript
\
Likewise, dealing with build options in executables is similar to libraries
except that here we have no export options; see the
\l{#core-adjust-build-src-source-opt Adjust source \c{buildfile}: build and
export options} step.
If the executable can plausibly be used in a build, then it's recommended to
add \c{build2} metadata as describe in
\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md
How do I convey additional information (metadata) with executables and C/C++
libraries?} See also \l{#howto-patch-upstream-source-preproc Modifying
upstream source code with C/C++ preprocessor} on how to do it without
physically modifying upstream source code. \N{See the
\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository for a
real example of doing this.}
\N|We will discuss the \c{testscript} prerequisite in the
\l{#core-test-smoke-exe Make smoke test: executables} step below.|
\h2#core-adjust-build-src-source-ext|Adjust source \c{buildfile}: extra requirements|
The changes discussed so far should be sufficient to handle a typical library
or executable that is written in C and/or C++ and is able to handle platform
differences with the preprocessor and compile/link options. However, sooner or
later you will run into a more complex library that may use additional
languages, require more elaborate platform detection, or use additional
functionality, such as support for source code generators. The below list
provides pointers to resources that cover the more commonly encountered
additional requirements.
\ul|
\li|\l{b#module-in The \c{in} build system module}
Use to process \c{config.h.in} (or other \c{.in} files) that don't require
Autoconf-style platform probing (\c{HAVE_*} options).|
\li|\l{https://github.com/build2/libbuild2-autoconf The \c{autoconf} build
system module}
Use to process \c{config.h.in} (or their CMake/Meson variants) that require
Autoconf-style platform probing (\c{HAVE_*} options) or CMake/Meson-specific
substitution syntax (\c{#cmakedefine}, etc).|
\li|\l{b#c-objc Objective-C Compilation} and
\l{b#cxx-objcxx Objective-C++ Compilation}
Use to compile Objective-C (\c{.m}) or Objective-C++ (\c{.mm}) source files.|
\li|\l{b#c-as-cpp Assembler with C Preprocessor Compilation}
Use to compile Assembler with C Preprocessor (\c{.S}) source files.|
\li|\l{b#intro-unit-test Implementing Unit Testing}
Use if upstream has tests (normally unit tests) in the source subdirectory.|
\li|\l{intro#guide-build-time-linked Build-Time Dependencies and Linked
Configurations}
Use if upstream relies on source code generators, such as
\l{https://cppget.org/reflex \c{lex}} and \l{https://cppget.org/byacc
\c{yacc}}.|
\li|\l{https://github.com/build2/HOWTO/ The \c{build2} HOWTO}
See the \c{build2} HOWTO article collection for more unusual requirements.||
\h2#core-adjust-build-test|Test library build|
At this point our library should be ready to build, at least in theory. While
we cannot build and test the entire package before adjusting the generated
\c{tests/} subproject (the subject of the next step), we can try to build just
the library and, if it has any unit tests in the source subdirectory, even run
some tests.
\N|If the library is header only, there won't be anything to build unless
there are unit tests. Still, you may want to continue with this exercise to
detect any syntactic mistakes in the \c{buildfiles}, etc.|
To build only a specific subdirectory of our package, we use the build system
directly (continuing with our \c{libfoo} example):
\
$ cd libfoo/src/ # Change to the source subdirectory.
$ b update
\
If there are any issues, try to fix them and then build again. Once the
library builds and if it has unit tests, you can try to run them:
\
$ b test
\
It also makes sense to test the installation and see if anything is off (such
as private headers being installed):
\
$ rm -rf /tmp/install
$ b install config.install.root=/tmp/install
\
Once the library builds, it makes sense to commit our changes for easier
rollbacks:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust source subdirectory buildfiles\"
\
\h#core-test-smoke|Make smoke test|
With the library build sorted, we need tests to make sure the result is
actually functional. As \l{#core-fill discussed earlier}, it is recommended to
start with a simple \"smoke test\", make sure that works, and then replace it
with upstream tests. However, if upstream tests look simple enough, you can
skip the smoke test. For example, if upstream has all its tests in a single
source file and the way it is built doesn't look too complicated, then you can
just use that source file in place of the smoke test.
\N|If upstream has no tests, then the smoke test will have to stay. A library
can only be published if it has at least one test.
It is also recommended to have the smoke test if upstream tests are in a
separate package. See
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for background and
details.|
\N|If instead of a library you are packaging an executable, you can skip
directly to \l{#core-test-smoke-exe Make smoke test: executables}.|
To recap, the \c{bdep-new}-generated \c{tests/} subdirectory looks like this
(continuing with our \c{libfoo} example):
\
libfoo/
├── ...
└── tests/
├── build/
│ ├── bootstrap.build
│ └── root.build
├── basics/
│ ├── driver.cpp
│ └── buildfile
└── buildfile
\
The \c{tests/} subdirectory is a build system subproject, meaning that it can
be built independently, for example, to test the installed version of the
library (see \l{b#intro-operations-test Testing} for background). In
particular, this means it has the \c{build/} subdirectory with project-wide
build system files, the same as the library. The \c{basics/} subdirectory
contains the generated test, which is what we will be turning into a smoke
test. The subproject root \c{buildfile} rarely needs changing.
\h2#core-test-smoke-build-wide|Adjust project-wide build system files in \c{tests/build/}|
Review and adjust the generated \c{bootstrap.build} and \c{root.build} (there
will be no \c{export.build}) similar to the \l{#core-adjust-build-wide Adjust
project-wide build system files in \c{build/}} step.
Here the only change you would normally make is in \c{root.build} and which is
to drop the assignment of extensions for target types that are not used in
tests.
\h2#core-test-smoke-adjust|Convert generated test to library smoke test|
The \c{basics/} subdirectory contains the \c{driver.cpp} source file that
implements the test and \c{buildfile} that builds it. You can rename both the
test subdirectory (\c{basics/}) and the source file \c{driver.cpp}, for
example, if you are going with the upstream tests directly. You can also add
more tests by simply copying \c{basics/}.
The purpose of a smoke test is to make sure the library's public headers can
be included (including in the installed case, no pun intended), it can be
linked, and its basic functionality works.
To achieve this, we modify \c{driver.cpp} to include the library's main
headers and call a few functions. For example, if the library has the
initialize/deinitialize type of functions, those are good candidates to
call. If the library is not header-only, make sure that the smoke test calls
at least one non-inline/template function to test symbol exporting.
\N|Make sure that your test includes the library's public headers the same way
as would be done by the library consumers.|
Continuing with our \c{libfoo} example, this is what its smoke test might look
like:
\
#include <foo/core.hpp>
#include <foo/util.hpp>
#undef NDEBUG
#include <cassert>
int main ()
{
foo::context* c (foo::init (0 /* flags */));
assert (c != nullptr);
foo::deinit (c);
}
\
\N|The C/C++ \c{assert()} macro is often adequate for simple tests and does
not require extra dependencies. But see
\l{https://github.com/build2/HOWTO/blob/master/entries/use-assert-in-tests.md
How do I correctly use C/C++ assert() in tests?}|
The test \c{buildfile} is pretty simple:
\
import libs = libfoo%lib{foo}
exe{driver}: {hxx ixx txx cxx}{**} $libs testscript{**}
\
If you have adjusted the library target name (\c{lib{foo\}}) in the source
subdirectory \c{buildfile}, then you will need to make the corresponding
change in the \c{import} directive here. You may also want to tidy it up by
removing unused prerequisite types. For example:
\
import libs = libfoo%lib{foo}
exe{driver}: {hxx cxx}{**} $libs
\
\h2#core-test-smoke-exe|Make smoke test: executables|
If instead of a library we are packaging an executable, then instead of the
\c{tests/} subproject we get the \c{testscript} file in the source
subdirectory (see \l{#core-adjust-build-src-source-exe Adjust source
\c{buildfile}: executables} for a refresher). This file can be used to write
one or more tests that exercise our executable (see \l{b#intro-operations-test
Testing} for background).
How exactly to test any given executable depends on its functionality. For
instance, for a compression utility we could write a roundtrip test that first
compresses some input, then decompresses it, and finally compares the result
to the original. For example (taken from the
\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository):
\
: roundtrip
:
echo 'test content' | $* -zc | $* -dc >'test content'
\
On the other hand, for an executable that is a source code generator, proper
testing would involve a separate tests package that has a build-time
dependency on the executable and that exercises the generated code (see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for background and
details). \N{See the \l{https://github.com/build2-packaging/thrift/tree/0.17
\c{thrift}} package repository for an example of this setup.}
If the executable provides a way to query its version, one test that you
should always be able to write, and which can serve as a last resort smoke
test, is the version check. For example:
\
: version
:
$* --version >>~\"/EOO/\"
/.*$(version.major)\.$(version.minor)\.$(version.patch).*/
EOO
\
See also
\l{https://github.com/build2/HOWTO/blob/master/entries/sanitize-test-execution.md
How do I sanitize the execution of my tests?}
\h2#core-test-smoke-locally|Test locally|
With the smoke test ready, we can finally do some end-to-end testing of our
library build. We will start with doing some local testing to catch basic
mistakes and then do the full CI to detect any platform/compiler-specific
issues.
First let's run the test in the default build configuration by invoking the
build system directly (see \l{intro#guide Getting Started Guide} for
background on default configurations):
\
$ cd libfoo/tests/ # Change to the tests/ subproject.
$ b test
\
If there are any issues (compile/link errors, test failures), try to address
them and re-run the test.
Once the library builds in the default configuration and the result passes the
tests, you can do the same for all the build configurations, in case you have
\l{#core-fill-init initialized} your library in several:
\
$ bdep test -a
\
\h2#core-test-smoke-locally-install|Test locally: installation|
Once the development build works, let's also test the installed version of the
library. In particular, this makes sure that the public headers are installed
in a way that is compatible with how they are included by our test (and would
be included by the library consumers). To test this we first install the
library into a temporary directory:
\
$ cd libfoo/ # Change to the package root.
$ rm -rf /tmp/install
$ b install config.install.root=/tmp/install
\
Next we build just the \c{tests/} subproject out of source and arranging for
it to find the installed library (see \l{b#intro-dirs-scopes Output
Directories and Scopes} for background on the out of source build syntax):
\
$ cd libfoo/ # Change to the package root.
$ b test: tests/@/tmp/libfoo-tests-out/ \
config.cc.loptions=-L/tmp/install/lib \
config.bin.rpath=/tmp/install/lib
\
\N|The equivalent MSVC command line would be:
\
> b install config.install.root=c:\tmp\install
> set \"PATH=c:\tmp\install\bin;%PATH%\"
> b test: tests\@c:\tmp\libfoo-tests-out\^
config.cc.loptions=/LIBPATH:c:\tmp\install\lib
\
|
It is a good idea to look over the installed files manually and make sure
there is nothing unexpected, for example, missing or extraneous files.
Once done testing the installed case, let's clean things up:
\
$ rm -r /tmp/install /tmp/libfoo-tests-out
\
\h2#core-test-smoke-locally-dist|Test locally: distribution|
Another special case worth testing is the preparation of the source
distribution (see \l{b#intro-operations-dist Distributing} for
background). This, in particular, is how your package will be turned into the
source archive for publishing to \l{https://cppget.org cppget.org}. Here we
are primarily looking for missing files. As a bonus, this will also allow us
to test the in source build. First we distribute our package to a temporary
directory (again using the default configuration and the build system
directly):
\
$ cd libfoo/ # Change to the package root.
$ b dist config.dist.root=/tmp/dist config.dist.uncommitted=true
\
The result will be in the \c{/tmp/dist/libfoo-<version>/} directory which
should resemble our \c{libfoo/} package but without files like \c{.gitignore}.
Next we build and test the distribution in source:
\
$ cd /tmp/dist/libfoo-<version>/
$ b configure config.cxx=g++
$ b update
$ b test
\
\N|If your package has dependencies that you import in your \c{buildfile},
then the above \c{configure} operation will most likely fail because such
dependencies cannot be found (it may succeed if they are available as
system-installed). The error message will suggest specifying the location of
each dependency with the \c{config.import.*} variable. You can fix this by
setting each such \c{config.import.*} to the location of the default build
configuration (created in the \l{#core-fill-init Initialize package in build
configurations} step) which should contain all the necessary
dependencies. Simply re-run the \c{configure} operation until you have
discovered and specified all the necessary \c{config.import.*} variables, for
example:
\
$ b configure config.cxx=g++ \
config.import.libz=.../foo-gcc \
config.import.libasio=.../foo-gcc \
config.import.libsqlite3=.../foo-gcc
\
|
It is a good idea to look over the distributed files manually and make sure
there is nothing missing or extraneous.
Once done testing the distribution, let's clean things up:
\
$ rm -r /tmp/dist
\
\h2#core-test-smoke-ci|Commit and test with CI|
With local testing complete, let's commit our changes and submit a remote CI
job to test our library on all the major platforms and with all the major
compilers:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Add smoke test\"
$ git push -u
$ bdep ci
\
The result of the \l{bdep-ci(1)} command is a link where you can see the
status of the builds.
\N|Make sure to wait until there are no more unbuilt configurations (that is,
the number of entries with the \c{<unbuilt>} or \c{building} result is
\c{0}).|
If any builds fail, view the logs to determine the cause, try to fix it,
commit your fix, and CI again.
\N|It is possible that upstream does not support some platforms or compilers.
For example, it's common for smaller projects not to bother with supporting
\"secondary\" compilers, such as MinGW GCC on Windows or Homebrew GCC on Mac
OS.
If upstream expressly does not support some platform or compiler, it's probably
not worth spending time and energy trying to support it in the package. Most
likely it will require changes to upstream source code and that is best done
upstream rather than in the package (see \l{#dont-fix-upstream Don't try to
fix upstream issues in the package} for background). In this case you would
want to exclude these platforms/compilers from the CI builds using the
\l{bpkg#manifest-package-builds \c{builds} package \c{manifest} value}.
The other common cause of a failed build is a newer version of a compiler or
platform that breaks upstream. In this case there are three options: Ideally
you would want to fix this in upstream and have a new version released.
Failing that, you may want to patch the upstream code to fix the issues,
especially if this is one of the major platforms and/or primary compilers (see
\l{#howto-patch-upstream-source How do I patch upstream source code?} for
details). Finally, you can just leave the build failing with the expectation
that it will be fixed in the next upstream version. Note that in this case you
should not exclude the failing build from CI.|
\h#core-test-upstream|Replace smoke test with upstream tests|
With the smoke test working we can now proceed with replacing it with the
upstream tests.
\h2#core-test-upstream-understand|Understand how upstream tests work|
While there are some commonalities in how C/C++ libraries are typically built,
when it comes to tests there is unfortunately little common ground in how they
are arranged, built, and executed. As a result, the first step in dealing with
upstream tests is to study the existing build system and try to understand how
they work.
\N|If upstream tests prove incomprehensible (which is unfortunately not
uncommon) and the only options you see are to go with just the smoke test or
to give up, then go with just the smoke test. In this case it's a good idea to
create an issue in the package repository mentioning that upstream tests are
still a TODO.|
\N|If instead of a library you are packaging an executable, then whether the
below steps will apply depends on the functionality of the executable.
In particular, testing source code generators would normally involve
exercising the generated code, in which case the following will largely apply,
though in this case the tests would need to be placed into a separate tests
package that has a build-time dependency on the executable (see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for background and
details). In fact, if a source code generator is accompanied by a runtime
library, then the tests will normally exercise them together (though a runtime
library might also have its own tests). See the
\l{https://github.com/build2-packaging/thrift/tree/0.17 \c{thrift}} package
repository for an example of this setup.|
To get you started with analyzing the upstream tests, below are some of the
questions you would likely need answered before you can proceed with the
conversion:
\ul|
\li|\b{Are upstream tests unit tests or integration tests?}
While the distinction is often fuzzy, for our purposes the key differentiator
between unit and integration tests is which API they use: integration tests
only use the library's public API while unit tests need access to the
implementation details.
Normally (but not always), unit tests will reside next to the library source
code since they need access to more than just the public headers and the
library binary (private headers, individual object files, utility libraries,
etc). While integration tests are normally (but again not always) placed into
a separate subdirectory, usually called \c{tests} or \c{test}.
If the library has unit tests, then refer to \l{b#intro-unit-test Implementing
Unit Testing} for background on how to handle them in \c{build2}.
If the library has integration tests, then use them to replace (or complement)
the smoke test.
If the library has unit tests but no integration tests, then it is recommended
to keep the smoke test since that's the only way the library will be tested
via its public API.|
\li|\b{Do upstream tests use an external testing framework?}
Oftentimes a C++ library will use an external testing framework to implement
tests. Popular choices include \l{https://cppget.org/catch2 \c{catch2}},
\l{https://cppget.org/gtest \c{gtest}}, \l{https://cppget.org/doctest
\c{doctest}}, and \l{https://cppget.org/libboost-test \c{libboost-test}}.
If a library uses such an external testing framework, then it is recommended
to factor tests into a separate package in order to avoid making the library
package depend on the testing framework (which is only required during
testing). See
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for details.
\N|Sometimes you will find that upstream bundles the source code of the
testing framework with their tests. This is especially common with
\c{catch2}. If that's the case, it is strongly recommended that you
\"unbundle\" it by making it a proper external dependency. See \l{#dont-bundle
Don't bundle dependencies} for background.||
\li|\b{Are upstream tests in a single or multiple executables?}
It's not unusual for libraries to have a single test executable that runs all
the test cases. This is especially common if a C++ testing framework is used.
In this case it is natural to replace the contents of the smoke test with the
upstream source code, potentially renaming the test subdirectory (\c{basics/})
to better match upstream naming.
If upstream has multiple test executables, then they could all be in a single
test subdirectory (potentially reusing some common bits) or spread over
multiple subdirectories. In both cases it's a good idea to follow the upstream
structure unless you have good reasons to deviate. In the former case (all
executables in the same subdirectory), you can re-purpose the smoke test
subdirectory. In the latter case (each executable in a separate subdirectory)
you can make copies of the smoke test subdirectory.|
\li|\b{Do upstream tests use an internal utility library?}
If there are multiple test executables and they need to share some common
functionality, then it's not unusual for upstream to place such functionality
into a static library and then link it to each test executable. In \c{build2}
such an internal library is best represented with a utility library (see
\l{b#intro-unit-test Implementing Unit Testing} for details). See the
following section for an example.|
\li|\b{Are upstream tests well behaved?}
Unfortunately, it's not uncommon for upstream tests not to behave well, such
as to write diagnostics to \c{stdout} instead of \c{stderr}, create temporary
files without cleaning them up, or assume presence of input files in the
current working directory. For details on how to deal with such situations see
\l{https://github.com/build2/HOWTO/blob/master/entries/sanitize-test-execution.md
How do I sanitize the execution of my tests?}||
\h2#core-test-upstream-convert|Convert smoke test to upstream tests|
Once you have a good grasp of how upstream tests work, convert or replace the
smoke test with the upstream tests. If upstream has multiple test executables,
you may want to deal with one test at a time, making sure that it passes
before moving to the next one.
It's normally a good idea to use the smoke test \c{buildfile} as a starting
point for upstream tests. To recap, the smoke test \c{buildfile} for our
\c{libfoo} example ended up looking like this:
\
import libs = libfoo%lib{foo}
exe{driver}: {hxx cxx}{**} $libs
\
At a minimum you will most likely need to change the name of the executable to
match upstream. If you need to build multiple executables in the same
directory, then it's probably best to get rid of the name pattern for the
source files and specify the prerequisite names explicitly, for example:
\
import libs = libfoo%lib{foo}
./: exe{test1}: cxx{test1} $libs
./: exe{test2}: cxx{test2} $libs
\
If you have a large number of such test executables, then a \l{b#intro-for
\c{for}-loop} might be a more scalable option:
\
import libs = libfoo%lib{foo}
for src: cxx{test*}
./: exe{$name($src)}: $src $libs
\
If the upstream tests have some common functionality that is used by all the
test executables, then it is best placed into a utility library. For example:
\
import libs = libfoo%lib{foo}
./: exe{test1}: cxx{test1} libue{common}
./: exe{test2}: cxx{test2} libue{common}
libue{common}: {hxx cxx}{common} $libs
\
\h2#core-test-upstream-locally|Test locally|
With the upstream tests ready, we re-do the same end-to-end testing as we did
with the smoke test:
\l{#core-test-smoke-locally Test locally}\n
\l{#core-test-smoke-locally-install Test locally: installation}\n
\l{#core-test-smoke-locally-dist Test locally: distribution}\n
\h2#core-test-upstream-ci|Commit and test with CI|
With local testing complete, we commit our changes and submit a remote CI
job. This step is similar to what \l{#core-test-smoke-ci we did for the smoke
test} but this time we are using the upstream tests:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Add upstream tests\"
$ git push
$ bdep ci
\
\h#core-examples-benchmarks|Add upstream examples, benchmarks, if any|
If the upstream project provides examples and/or benchmarks and you wish to
add them to the \c{build2} build (which is not strictly necessary for the
\c{build2} package to be usable), then now is a good time to do that.
As was mentioned in \l{#core-package-review Review and test auto-generated
\c{buildfile} templates}, the recommended approach is to copy the \c{tests/}
subproject (potentially from the commit history before the smoke test was
replaced with the upstream tests) and use that as a starting point for
examples and/or benchmarks. Do not forget to add the corresponding entry in
the root \c{buildfile}.
Once that is done, follow the same steps as in \l{#core-test-upstream Replace
smoke test with upstream tests} to add upstream examples/benchmarks and test
the result.
\h#core-root|Adjust root files (\c{buildfile}, \c{manifest}, etc)|
The last few files that we need to review and potentially adjust are
the root \c{buildfile}, package \c{manifest}, and \c{PACKAGE-README.md}.
\h2#core-root-buildfile|Adjust root \c{buildfile}|
The main function of the root \c{buildfile} is to pull in all the
subdirectories that need building plus list targets that are usually found in
the root directory of a project, typically \c{README.md}, \c{LICENSE},
etc. This is what the generated root \c{buildfile} looks like for our
\c{libfoo} project assuming we have symlinked \c{README.md}, \c{LICENSE}, and
\c{NEWS} from upstream in the \l{#core-package-create Create final package}
step:
\
./: {*/ -build/} \
doc{README.md PACKAGE-README.md NEWS} \
legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false
\
If the upstream project provides any other documentation (detailed change
logs, contributing guidelines, etc) or legal files (alternative licenses, list
of authors, code of conduct, etc), then you may want to symlink and list them
as the \c{doc{\}} and \c{legal{\}} prerequisites, respectively.
\N|If you are packaging an executable and it provides a man page, then it can
also be listed in the root \c{buildfile}. For example, if the man page file is
called \c{foo.1}:
\
./: ... man1{foo}
\
|
\N|One file you don't need to list is \c{INSTALL} (or equivalent) which
normally contains the installation instructions for the upstream build
system. In the \c{build2} package of a third-party project the
\c{PACKAGE-README.md} file serves this purpose (see
\l{#core-root-package-readme Adjust \c{PACKAGE-README.md}} for details).|
\h2#core-root-buildfile-doc|Adjust root \c{buildfile}: other subdirectories|
If the upstream project has other subdirectories that makes sense to include
into the \c{build2} package, then now is a good time to take care of that.
The most common such case will be extra documentation (besides the root
\c{README}), typically in a subdirectory called \c{doc/}, \c{docs/}, or
\c{documentation/}.
The standard procedure for handling such subdirectories will be to symlink the
relevant files (or the entire subdirectory) and then list the files as
prerequisites. For this last step, there are two options: we can list the
files directly in the root \c{buildfile} or we can create a separate
\c{buildfile} in the subdirectory.
\N|If symlinking entire subdirectories, don't forget to also list them in
\c{.gitattributes} if you want your package to be usable from the \c{git}
repository directly on Windows. See
\l{https://build2.org/article/symlinks.xhtml#windows Symlinks and Windows} for
details.|
Let's examine each approach using our \c{libfoo} as an example. We will
assume that the upstream project contains the \c{docs/} subdirectory with
additional \c{*.md} files that document the library's API. It would make sense
to include them into the \c{build2} package.
Listing the subdirectory files directly in the root \c{buildfile} works best
for simple cases, where you have a bunch of static files that don't require
any special provisions, such as customizations to their installation
locations. In this case we can symlink the entire \c{docs/} subdirectory:
\
$ cd libfoo/ # Change to the package root.
$ ln -s ../upstream/docs ./
\
The adjustments to the root \c{buildfile} are pretty straightforward: we
exclude the \c{docs/} subdirectory (since it has no \c{buildfile}) and list
the \c{*.md} files as prerequisites using the \c{doc{\}} target type (which,
in particular, makes sure they are installed into the appropriate location):
\
./: {*/ -build/ -docs/} \
doc{README.md PACKAGE-README.md NEWS} \
docs/doc{*.md} \
legal{LICENSE} manifest
\
The alternative approach (create a separate \c{buildfile}) is a good choice if
things are more complicated than that. Let's say we need to adjust the
installation location of the files in \c{docs/} because there is another
\c{README.md} inside and that would conflict with the root one when installed
into the same location. This time we cannot symlink the top-level \c{docs/}
subdirectory (because we need to place a \c{buildfile} there). The two options
here are to either symlink the individual files or introduce another
subdirectory level inside \c{docs/} (which is the same approach as discussed
in \l{#dont-main-target-root-buildfile Don't build your main targets in the
root \c{buildfile}}). Let's illustrate both sub-cases.
Symlinking individual files works best when you don't expect the set of files
to change often. For example, if \c{docs/} contains a man page and its HTML
rendering, then it's unlikely this set will change. On the other hand, if
\c{docs/} contains a manual split into an \c{.md} file per chapter, then there
is a good chance this set of files will fluctuate between releases.
Continuing with our \c{libfoo} example, this is how we symlink the individual
\c{*.md} files in \c{docs/}:
\
$ cd libfoo/ # Change to the package root.
$ mkdir docs
$ cd docs/
$ ln -s ../../upstream/docs/*.md ./
\
Then write a new \c{buildfile} in \c{docs/}:
\
./: doc{*.md}
# Install the documentation in docs/ into the manual/ subdirectory of,
# say, /usr/share/doc/libfoo/ since we cannot install both its and root
# README.md into the same location.
#
doc{*.md}: install = doc/manual/
\
Note that we don't need to make any changes to the root \c{buildfile} since
this subdirectory will automatically get picked up by the \c{{*/\ -build/\}}
name pattern that we have there.
Let's now look at the alternative arrangement with another subdirectory level
inside \c{docs/}. Here we achieve the same result but in a slightly different
way. Specifically, we call the subdirectory \c{manual/} and install recreating
subdirectories (see \l{b#intro-operations-install Installing} for background):
\
$ cd libfoo/ # Change to the package root.
$ mkdir -p docs/manual
$ cd docs/manual/
$ ln -s ../../../upstream/docs/*.md ./
\
And the corresponding \c{buildfile} in \c{docs/}:
\
./: doc{**.md}
# Install the documentation in docs/ into, say, /usr/share/doc/libfoo/
# recreating subdirectories.
#
doc{*}:
{
install = doc/
install.subdirs = true
}
\
\N|Yet another option would be to open a scope for the \c{docs/} subdirectory
directly in the root \c{buildfile} (see \l{b#intro-dirs-scopes Output
Directories and Scopes} for background). For example:
\
$ cd libfoo/ # Change to the package root.
$ ln -s ../upstream/docs ./
\
And then add the following to the root \c{buildfile}:
\
docs/
{
./: doc{*.md}
# Install the documentation in docs/ into the manual/ subdirectory
# of, say, /usr/share/doc/libfoo/ since we cannot install both its
# and root README.md into the same location.
#
doc{*.md}: install = doc/manual/
}
\
However, this approach should be used sparingly since it can quickly make the
root \c{buildfile} hard to comprehend. Note also that it cannot be used for
main targets since an export stub requires a \c{buildfile} to load (see
\l{#dont-main-target-root-buildfile Don't build your main targets in the
root \c{buildfile}} for details).|
\h2#core-root-buildfile-commit|Adjust root \c{buildfile}: commit and test|
Once all the adjustments to the root \c{buildfile} are made, it makes sense to
test it locally (this time from the root of the package), commit our changes,
and test with CI:
\
$ cd libfoo/ # Change to the package root.
$ b test
$ bdep test -a
\
If you had to add any extra files to the root \c{buildfile} (or add
\c{buildfiles} in extra subdirectories), then it also makes sense to test the
installation (\l{#core-test-smoke-locally-install Test locally: installation})
and the preparation of the source distribution
(\l{#core-test-smoke-locally-dist Test locally: distribution}) to make sure
the extra files end up in the right places.
Then commit our changes and CI:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust root buildfile\"
$ git push
$ bdep ci
\
\h2#core-root-manifest|Adjust \c{manifest}|
The next file we need to look over is the package's \c{manifest}. Here is what
it will look like, using our \c{libfoo} as an example:
\
: 1
name: libfoo
version: 2.1.0-a.0.z
language: c++
project: foo
summary: C++ library implementing secure Foo protocol
license: MIT ; MIT License.
description-file: README.md
package-description-file: PACKAGE-README.md
changes-file: NEWS
url: https://example.org/upstream
email: upstream@example.org
package-url: https://github.com/build2-packaging/foo
package-email: packaging@build2.org ; Mailing list.
depends: * build2 >= 0.16.0
depends: * bpkg >= 0.16.0
\
You can find the description of these and other package \c{manifest} values in
\l{bpkg#manifest-package Package Manifest} (the manifest format is described
in \l{bpkg#manifest-format Manifest Format}).
In the above listing the values that we likely need to adjust are \c{summary}
and \c{license}, unless correctly auto-detected by \c{bdep-new} in the
\l{#core-package-create Create final package} step. See
\l{#core-root-manifest-summary Adjust \c{manifest}: \c{summary}} and
\l{#core-root-manifest-license Adjust \c{manifest}: \c{license}} below
for guidelines on changing these values.
\N|It is not uncommon for projects to be licensed under multiple licenses.
Note, however, that \c{bdep-new} will only detect one license and you will
need to specify any additional licenses manually.|
We will also need to change \c{url} and \c{email} with the upstream project's
homepage URL and e-mail, respectively. If upstream doesn't have a dedicated
website for the project, then use its repository URL on GitHub or equivalent.
For e-mail you would normally use a mailing list address. If upstream doesn't
have any e-mail contacts, then you can drop this value from the
\c{manifest}. The \c{package-url} and \c{package-email} values normally do not
need to be changed.
\N|\l{https://lists.build2.org packaging@build2.org} is a mailing list for
discussions related to the packaging efforts of third-party projects.|
Note also that while you may be tempted to adjust the \c{version} value,
resist this temptation since this will be done automatically by
\l{bdep-release(1)} later.
You may also want to add the following value in certain cases:
\dl|
\li|\l{bpkg#manifest-package-changes \cb{changes-file}}
If you have added any extra news of changelog files to the root \c{buildfile}
(see \l{#core-root-buildfile Adjust root buildfile}), then it may also make
sense to list them in the \c{manifest}. For example:
\
changes-file: ChangeLog.txt
\
|
\li|\l{bpkg#manifest-package-topics \cb{topics}}
Package topics. For example:
\
topics: network protocol, network security
\
\N|If the upstream project is hosted on GitHub or similar, then you can
usually copy the topics from the upstream repository description.||
\li|\l{bpkg#manifest-package-doc-url \cb{doc-url}}\n
\l{bpkg#manifest-package-src-url \cb{src-url}}
Documentation and source code URLs. For example:
\
doc-url: https://example.org/foo/doc/
src-url: https://github.com/.../foo
\
||
\h2#core-root-manifest-summary|Adjust \c{manifest}: \c{summary}|
For \c{summary} use a brief description of the functionality provided by the
library or executable. Less than 70 characters is a good target to aim
for. Don't capitalize subsequent words unless proper nouns and omit the
trailing dot. For example:
\
summary: Vim xxd hexdump utility
\
Omit weasel words such as \"modern\", \"simple\", \"fast\", \"small\", etc.,
since they don't convey anything specific. Omit \"header-only\" or
\"single-header\" for C/C++ libraries since, at least in the context of
\c{build2}, it does not imply any benefit.
If upstream does not offer a sensible summary, the following template is
recommended for libraries:
\
summary: <functionality> C library
summary: <functionality> C++ library
\
For example:
\
summary: Event notification C library
summary: Validating XML parsing and serialization C++ library
\
If the project consists of multiple packages, it may be tempting to name each
package in terms of the overall project name, for example:
\
name: libigl-core
summary: libigl core module
\
This doesn't give the user any clue about what functionality is provided
unless they find out what \c{libigl} is about. Better:
\
summary: Geometry processing C++ library, core module
\
If you follow the above pattern, then to produce a summary for external tests
or examples packages simply add \"tests\" or \"examples\" at the end,
for example:
\
summary: Event notification C library tests
summary: Geometry processing C++ library, core module examples
\
\h2#core-root-manifest-license|Adjust \c{manifest}: \c{license}|
For \c{license}, use the \l{https://spdx.org/licenses/ SPDX license ID} if at
all possible. If multiple licenses are involved, use the SPDX License
expression. See the
\l{https://build2.org/bpkg/doc/build2-package-manager-manual.xhtml#manifest-package-license
\c{license} manifest value} documentation for details, including the list of
the SPDX IDs for the commonly used licenses.
\h2#core-root-manifest-commit|Adjust \c{manifest}: commit and test|
Once all the adjustments to the \c{manifest} are made, it makes sense to test
it locally, commit our changes, and test with CI:
\
$ cd libfoo/ # Change to the package root.
$ b test
$ bdep test -a
\
Then commit our changes and CI:
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust manifest\"
$ git push
$ bdep ci
\
\h2#core-root-package-readme|Adjust \c{PACKAGE-README.md}|
The last package file we need to adjust is \c{PACKAGE-README.md} which
describes how to use the package from a \c{build2}-based project. The template
generated by \c{bdep-new} establishes the recommended structure and includes a
number of placeholders enclosed in \c{<\ >}, such as \c{<UPSTREAM-NAME>} and
\c{<SUMMARY-OF-FUNCTIONALITY>}, that need to be replaced with the
package-specific content. While all the placeholders should be
self-explanatory, below are a couple of guidelines.
For \c{<SUMMARY-OF-FUNCTIONALITY>} it's best to copy a paragraph or two from
the upstream documentation, usually from \c{README.md} or the project's web
page.
\N|If the \c{bdep new} command was able to extract the summary from upstream
\c{README}, then the summary in the heading (first line) will contain that
information. Otherwise, you would need to adjust it manually, similar to
\c{manifest} above. In this case use the \c{summary} value form the
\c{manifest}, perhaps slightly shortened.|
If the package contains a single importable target, as is typical with
libraries, then it makes sense to drop the \"Importable targets\" section
since it won't add anything that hasn't already been said in the \"Usage\"
section.
Similarly, if the package has no configuration variables, then it makes sense
to drop the \"Configuration variables\" section.
For inspiration, see
\l{https://github.com/build2-packaging/zstd/blob/master/libzstd/PACKAGE-README.md
\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/zstd
\c{zstd}} and
\l{https://github.com/build2-packaging/libevent/blob/main/PACKAGE-README.md
\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/libevent
\c{libevent}} (libraries) as well as
\l{https://github.com/build2-packaging/zstd/blob/master/zstd/PACKAGE-README.md
\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/zstd
\c{zstd}} and
\l{https://github.com/build2-packaging/xxd/blob/master/xxd/README.md
\c{README.md}} in \l{https://github.com/build2-packaging/xxd \c{xxd}}
(executables).
\N|If upstream does not provide a \c{README} file, then it makes sense to
rename \c{PACKAGE-README.md} to just \c{README.md} in the \c{build2} package,
as was done in the \c{xxd} package mentioned above.|
Once \c{PACKAGE-README.md} is ready, commit and push the changes. You may also
want to view the result on GitHub to make sure everything is rendered
correctly.
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust PACKAGE-README.md\"
$ git push
\
\h#core-repo-readme|Adjust package repository \c{README.md}|
With all the package files taken care of, the last file we need to adjust is
\c{README.md} in the root of our package repository (it was created in the
\l{#core-repo-init Initialize package repository with \c{bdep new}} step).
\N|If you need to add additional packages and are doing this one package at a
time (for example, first library then executable in the \"library and
executable\" project), then this is the point where you would want to restart
from \l{#core-package Create package and generate \c{buildfile} templates} for
another iteration. Only once all the packages are added does it make sense
to continue with updating this \c{README.md}.|
The primary purpose of the package repository \c{README.md} is to provide
setup instructions as well as any other relevant information for the
development of the packages as opposed to their consumption. However, it's also
a good idea to give a brief summary of what this repository is about and to
point users interested in consumption to the \c{PACKAGE-README.md} files.
The template generated by \c{bdep new} establishes the recommended structure
to achieve these objectives. It includes a number of placeholders enclosed
in \c{<\ >}, such as \c{<UPSTREAM-URL>} and \c{<SUMMARY-OF-FUNCTIONALITY>},
that need to be replaced with the repository-specific content. While all the
placeholders should be self-explanatory, below are a couple of guidelines.
If there is a single package, then \c{<SUMMARY>} in the heading can be the
same as in \c{PACKAGE-README.md}. If there are multiple packages, then use
an overall summary of the upstream project.
For \c{<SUMMARY-OF-FUNCTIONALITY>} it's best to copy a paragraph or two from
the upstream documentation, usually from \c{README.md} or the project's web
page. Again, for a single package, this can be copied directly from
\c{PACKAGE-README.md}.
If there are multiple packages in the repository, then it's recommended to
replace a single link to \c{PACKAGE-README.md} with a list of links (this
also shows the available packages). For example:
\
... If you want to use `foo` in your `build2`-based project, then
instead see the accompanying `PACKAGE-README.md` files:
* [`libfoo/PACKAGE-README.md`](libfoo/PACKAGE-README.md)
* [`foo/PACKAGE-README.md`](foo/PACKAGE-README.md)
\
The remainder of the generated \c{README.md} file are the standard \c{bdep}
initialization instructions. Adjust them if your package repository requires
anything special (for example, a host configuration). This is also the place
to mention anything unusual, such as that upstream does not use semver (and
thus only a subset of \c{bdep} functionality is usable).
For inspiration, see
\l{https://github.com/build2-packaging/zstd/blob/master/README.md
\c{README.md}} in the \l{https://github.com/build2-packaging/zstd \c{zstd}}
package repository.
Once the repository \c{README.md} is ready, commit and push the changes. You
may also want to view the result on GitHub to make sure everything is rendered
correctly.
\
$ cd foo/ # Change to the package repository root.
$ git add .
$ git status
$ git commit -m \"Adjust package repository README.md\"
$ git push
\
\h#core-release-publish|Release and publish|
Once all the adjustments are in and everything is tested, we can release the
final version of the package and then publish it to \l{https://cppget.org
cppget.org}. Both of these steps are automated with the corresponding \c{bdep}
commands. But before performing these steps we need to transfer the package
repository to \l{https://github.com/build2-packaging
github.com/build2-packaging}.
\h2#core-release-publish-transfer|Transfer package repository|
If you have been doing your work in a repository in your personal workspace,
then now is the time to transfer it to the
\l{https://github.com/build2-packaging github.com/build2-packaging}
organization.
\N|It is important to transfer the repository before publishing the first
version of the package since the repository is used as a proxy for package
name ownership (see \l{bdep-publish(1)} for details). If you publish the
package from your personal workspace and then transfer the repository, the
ownership information will have to be adjusted manually, which we would
prefer to avoid.|
The first step is to become a member of this organization (unless you already
are). This will give you permissions to create new repositories, which is
required to perform a transfer (you will also have full read/write access to
the repository once transferred). To get an invite,
\l{https://build2.org/community.xhtml#help get in touch} not forgetting to
mention your GitHub user name.
If your repository has any prefixes, such as \c{build2-}, or suffixes
such as \c{-package}, then the next step is to rename it to follow the
\l{#core-repo-name Use upstream repository name as package repository name}
guideline. Go to the repository's Settings on GitHub where you should see the
Rename button.
Finally, to perform the transfer, go to the repository's Settings, Danger Zone
section, where you should see the Transfer button. Select \c{build2-packaging}
as the organization to transfer to, and complete the transfer.
Once transferred, you will be considered the maintainer of this package going
forward. If other members of the \c{build2-packaging} organization wish to
participate in the package maintenance, the correct etiquette is to do this
via pull requests. However, if you lose interest in maintaining a package or
otherwise become unresponsive, we may allow a new maintainer to take over
this role.
\N|In extraordinary circumstances the \c{build2-packaging} administrators may
make direct changes to the package, for example, to release a new revision in
order to address a critical issue. They will still try to coordinate the
changes with the maintainer but may not always be able to wait for a response
in time-sensitive cases.|
\h2#core-release-publish-release|Release final version|
As you may recall, our package currently has a pre-release snapshot version of
the upstream version (see \l{#core-package-adjust-version Adjust package
version}). Once all the changes are in, we can change to the final upstream
version, in a sense signaling that this package version is ready.
\N|If you are working in a branch, then now is also the time to merge it into
\c{master} (or equivalent).|
The recommended way to do this is with the \l{bdep-release(1)} command (see
\l{intro#guide-versioning-releasing Versioning and Release Management} for
background). Besides replacing the \c{version} value in the package
\c{manifest} file, it also commits this change, tags it with the
\c{v\i{X}.\i{Y}.\i{Z}} tag, and can be instructed to push the changes (or show
the \c{git} command to do so). This command also by default \"opens\" the next
development version, which is something that we normally want for our own
projects but not when we package a third-party one (since we cannot predict
which version upstream will release next). So we disable this functionality.
For example:
\
$ cd foo/ # Change to the package repository root.
$ bdep release --no-open --show-push
\
Then review the commit made by \c{bdep-release} and, if everything looks good,
push the changes by copying the command that it printed:
\
$ git diff HEAD~1
$ git push ...
\
\N|If something is wrong and you need to undo this commit, don't forget to
also remove the tag. Note also that once you have pushed your changes, you
cannot undo the commit. Instead, you will need to make a revision. See
\l{#core-version-management Version management} for background and
details.|
\h2#core-release-publish-publish|Publish released version|
Once the version is released we can publish the package to
\l{https://cppget.org cppget.org} with the \l{bdep-publish(1)} command (see
\l{intro#guide-versioning-releasing Versioning and Release Management} for
background):
\
$ cd foo/ # Change to the package repository root.
$ bdep publish
\
The \c{bdep-publish} command prepares the source distribution of your package,
uploads the resulting archive to the package repository, and prints a link to
the package submission in the queue. Open this link in the browser and check
that there are no surprises in the build results (they should match the
earlier CI results) or in the displayed package information
(\c{PACKAGE-README.md}, etc).
\N|While there should normally be no discrepancies in the build results
compared to our earlier CI submissions, the way the packages are built on CI
and in the package repository are not exactly the same. Specifically, CI
builds them from \c{git} while the package repository \- from the submitted
package archives. If there are differences, it's almost always due to issues
in the source distribution preparation (see \l{#core-test-smoke-locally-dist
Test locally: distribution}).|
If everything looks good, then you are done: the package submission will be
reviewed and, if there are no problems, moved to \l{https://cppget.org
cppget.org}. If there are problems, then an issue will be created in the
package repository with the review feedback. In this case you will need to
\l{#core-version-management-new-revision release and publish a version
revision} to address any problems. However, in both cases, you should first
read through the following \l{#core-version-management Package version
management} section to understand the recommended \"version lifecycle\" of a
third-party package.
Also, if there is an issue for this package in
\l{https://github.com/build2-packaging/WISHLIST
github.com/build2-packaging/WISHLIST}, then you would want to add a comment
and close it once the package has been moved to \l{https://cppget.org
cppget.org}.
\h#core-version-management|Package version management|
Once we have pushed the release commit, in order to preserve continuous
versioning (see \l{#core-package-adjust-version Adjust package version} for
background), no further changes should be made to the package without also
changing its version.
\N|More precisely, you can make and commit changes without changing the
version provided they don't affect the package. For example, you may keep a
\c{TODO} file in the root of your repository which is not part of any
package. Updating such a file without changing the version is ok since the
package remains unchanged.|
While in our own projects we can change the versions as we see fit, with
third-party projects the versions are dictated by upstream and as a result
we are limited to what we can use to fix issues in our packaging work
itself. It may be tempting (and perhaps even conceptually correct) to release
a patch version for our own fixes, however, we will be in trouble if later
upstream releases the same patch version but with a different set of changes
(plus the users of our package may wonder where did this version come
from). As a result, we should only change the major, minor, or patch
components of the package version in response to the corresponding upstream
releases. For fixes to the packaging work itself we should instead use version
revisions.
\N|Because a revision replaces the existing version, we should try to limit
revision changes to bug fixes and preferably only in the package
\"infrastructure\" (\c{buildfiles}, \c{manifest}, etc). Fixes to upstream
source code should be limited to critical bugs and be preferably backported
from upstream. To put it another way, changes in a revision should have an
even more limited scope than a patch release.|
Based on this, the recommended \"version lifecycle\" for a third-party
package is as follows:
\ol|
\li|After a release (the \l{#core-release-publish-release Release final
version} step above), for example, version \c{2.1.0}, the package enters a
\"revision phase\" where we can release revisions (\c{2.1.0+1}, \c{2.1.0+2},
etc) to address any issues in the packaging work. See
\l{#core-version-management-new-revision New revision} for the detailed
procedure.|
\li|When a new upstream version is released, for example version \c{2.2.0},
and we wish to upgrade our package to this version, we switch to its
pre-release snapshot version (\c{2.2.0-a.0.z}) the same way as we did in the
\l{#core-package-adjust-version Adjust package version} step initially. See
\l{#core-version-management-new-version New version} for the detailed
procedure.|
\li|Once we are done upgrading to the new upstream version, we release the
final version just like in the \l{#core-release-publish-release Release final
version} step initially. At this point the package enters another revision
phase.||
Note also that in the above example, once we have switched to \c{2.2.0-a.0.z},
we cannot go back and release another revision or patch version for \c{2.1.0}
on the current branch. Instead, we will need to create a separate branch for
the \c{2.1.Z} release series and make a revision or patch version there. See
\l{#core-version-management-old-series New version/revision in old release
series} for the detailed procedure.
\h2#core-version-management-new-revision|New revision|
As discussed in \l{#core-version-management Package version management}, we
release revisions to fix issues in the package \"infrastructure\"
(\c{buildfiles}, \c{manifest}, etc) as well as critical bugs in upstream
source code.
\N|Releasing a new revision is also a good opportunity to review and fix any
accumulated issues that didn't warrant a revision on their own. See
\l{#core-version-management-new-version-issues New version: review/fix
accumulated issues} for background.|
In the revision phase of the package version lifecycle (i.e., when the version
does not end with \c{-a.0.z}), every commit must be accompanied by the
revision increment to maintain continuous versioning. As a result, each
revision release commit necessarily also contains the changes in this
revision. Below is a typical workflow for releasing and publishing the
revision:
\
$ # make changes
$ # test locally
$ git add .
$ bdep release --revision --show-push
$ # review commit
$ git push ...
$ # test with CI
$ bdep publish
\
Customarily, the revision commit message has the \c{\"Release version
X.Y.Z+R\"} summary as generated by \c{bdep-release} followed by the
description of changes, organized in a list if there are several. For example:
\
Release version 2.1.0+1
- Don't compile port/strlcpy.c on Linux if GNU libc is 2.38 or newer
since it now provides the strl*() functions.
- Switch to using -pthread instead of -D_REENTRANT/-lpthread.
\
\N|The fact that all the changes must be in a single commit is another reason
to avoid substantial changes in revisions.|
Note also that you can make multiple commits while developing and testing the
changes for a revision in a separate branch. However, once they are ready for
a release, they need to be squashed into a single commit. The
\l{bdep-release(1)} command provides the \c{--amend} and \c{--squash} options
to automate this. For example, here is what a workflow with a separate branch
might look like:
\
$ git checkout -b wip-2.1.0+1
$ # make strl*() changes
$ # test locally
$ git commit -a -m \"Omit port/strlcpy.c if glibc 2.38 or newer\"
$ git push -u
$ # test with CI
$ # make pthread changes
$ # test locally
$ git commit -a -m \"Switch to using -pthread\"
$ git push
$ # test with CI
$ git checkout master
$ git merge --ff-only wip-2.1.0+1
$ bdep release --revision --show-push --amend --squash 2
$ # review commit
$ # test locally
$ git push ...
$ # test with CI
$ bdep publish
\
\h2#core-version-management-new-version|New version|
As discussed in \l{#core-version-management Package version management}, we
release new versions strictly in response to the corresponding upstream
releases.
The amount or work required to upgrade a package to a new upstream version
depends on the extend of changes in the new version.
On one extreme you may have a patch release which fixes a couple of bugs in
the upstream source code without any changes to the set of source files,
upstream build system, etc. In such cases, upgrading a package is a simple
matter of creating a new work branch, pointing the \c{upstream} \c{git}
submodule to the new release, running tests, and then merging, releasing, and
publishing a new package version.
On the other extreme you may have a new major upstream release which is
essentially a from-scratch rewrite with new source code layout, different
upstream build system, etc. In such cases it may be easier to likewise start
from scratch. Specifically, create a new work branch, point the \c{upstream}
\c{git} submodule to the new release, delete the existing package, and
continue from \l{#core-package Create package and generate \c{buildfile}
templates}.
Most of the time, however, it will be something in between where you may need
to tweak a few things here and there, such as adding symlinks to new source
files (or removing old ones), tweaking the \c{buildfiles} to reflect changes
in the upstream build system, etc.
The following sections provide a checklist-like sequence of steps that can be
used to review upstream changes with links to the relevant earlier sections in
case adjustments are required.
\h2#core-version-management-new-version-branch|New version: create new work branch|
When upgrading a package to a new upstream version it's recommended to do this
in a new work branch which, upon completion, is merged into \c{master} (or
equivalent). For example, if the new upstream version is \c{2.2.0}:
\
$ git checkout -b wip-2.2.0
\
\N|If you are not the maintainer of the package and would like to help with
preparing the new version, then, when everything is ready, use this branch
to create a pull request instead of merging it directly.|
\h2#core-version-management-new-version-open|New version: open new version|
This step corresponds to \l{#core-package-adjust-version Adjust package
version} during the initial packaging. Here we can make use of the
\c{bdep-release} command to automatically open the new version and make
the corresponding commit. For example, if the new upstream version is
\c{2.2.0}:
\
$ bdep release --open --no-push --open-base 2.2.0
\
\h2#core-version-management-new-version-submodule|New version: update \c{upstream} submodule|
This step corresponds to \l{#core-repo-submodule Add upstream repository as
\c{git} submodule} during the initial packaging. Here we need to update
the submodule to point to the upstream commit that corresponds to the
new version.
For example, if the upstream release tag we are interested in is called
\c{v2.2.0}, to update the \c{upstream} submodule to point to this release
commit, run the following commands:
\
$ cd upstream/
$ git checkout v2.2.0
$ cd ../
$ git add .
$ git status
$ git commit -m \"Update upstream submodule to 2.2.0\"
\
\h2#core-version-management-new-version-review|New version: review upstream changes|
At this point it's a good idea to get an overview of the upstream changes
between the two releases in order to determine which adjustments are likely to
be required in the \c{build2} package. We can use the \c{upstream} submodule
for that, which contains the change history we need.
One way to get an overview of changes between the releases is to use a
graphical repository browser such as \c{gitk} and view a cumulative \c{diff}
of changes between the two versions. For example, assuming the latest packaged
version is tagged \c{v2.1.0} and the new version is tagged \c{v2.2.0}:
\
$ cd upstream/
$ gitk v2.1.0..v2.2.0 &
\
Then click on the commit tagged \c{v2.2.0}, scroll down and right-click on the
commit tagged \c{v2.1.0}, and select the \"Diff this -> selected\" menu item.
This will display the cumulative set of changes between these two upstream
versions. Review them looking for the following types of changes in
particular (discussed in the following sections):
\ul|
\li|Changes to the source code layout.|
\li|New dependencies being added or old removed.|
\li|New source files being added or old removed (including in tests, etc).|
\li|Changes to the upstream build system.|
\li|Other new files/subdirectories being added or old removed.||
\h2#core-version-management-new-version-layout|New version: layout changes|
As mentioned earlier, for drastic layout changes it may make sense to start
from scratch and re-generate the package with the \c{bdep-new} command (use
\l{#core-package-struct Decide on the package source code layout} as a
starting point). On the other hand, if the changes are minor, then you can try
to adjust things manually. An in-between strategy is to generate the new
layout using \c{bdep-new} on the side and then retrofit the relevant changes
in \c{buildfiles} to the existing package. In a sense, this approach uses
\c{bdep-new} as a guide to figure out how to implement the new layout.
\h2#core-version-management-new-version-dependencies|New version: new/old dependencies|
If upstream added new or removed old dependencies, then you will need to
replicate these changes in your package as in the \l{#core-fill-depend Add
dependencies} and \l{#core-adjust-build-src-source-dep Adjust source
\c{buildfile}: dependencies} initial packaging steps.
\h2#core-version-management-new-version-sources|New version: new/old source files|
If upstream added new or removed old source files, then you will need to
replicate these changes in your package as in the \l{#core-fill-source Fill
with upstream source code} and possibly \l{#core-adjust-build-src-header
Adjust header \c{buildfile}} and \l{#core-adjust-build-src-source-src Adjust
source \c{buildfile}: sources, private headers} initial packaging steps.
Also don't forget about tests, examples, etc., which may also add new or
remove old source files (typically new tests). See
\l{#core-test-upstream-convert Convert smoke test to upstream tests}.
If there are any manual modifications to the upstream source code, then you
will also need to re-apply them to the new version as discussed in
\l{#howto-patch-upstream-source-manual Modifying upstream source code
manually}.
\h2#core-version-management-new-version-build|New version: changes to build system|
If upstream changed anything in the build system, then you may need to
replicate these changes in your package's \c{buildfiles}. The relevant initial
packaging steps are: \l{#core-adjust-build-wide Adjust project-wide build system files in
\c{build/}} and \l{#core-adjust-build-src-source-opt Adjust source
\c{buildfile}: build and export options}.
The corresponding steps for tests are: \l{#core-test-smoke-build-wide Adjust
project-wide build system files in \c{tests/build/}} and
\l{#core-test-upstream-convert Convert smoke test to upstream tests}.
\h2#core-version-management-new-version-other|New version: other new/old files/subdirectories|
If upstream added or removed any other files or subdirectories that are
relevant to our package (such as documentation), then adjust the package
similar to the \l{#core-root-buildfile Adjust root \c{buildfile}} and
\l{#core-root-buildfile-doc Adjust root \c{buildfile}: other subdirectories}
initial packaging steps.
\h2#core-version-management-new-version-root|New version: review \c{manifest} and \c{PACKAGE-README.md}|
It makes sense to review the package \c{manifest} (\l{#core-root-manifest
Adjust \c{manifest}}) and \c{PACKAGE-README.md} (\l{#core-root-package-readme
Adjust \c{PACKAGE-README.md}}) for any updates.
\h2#core-version-management-new-version-repo|New version: review repository \c{README.md}|
If any new packages were added in this version or if there are any changes to
the development workflow, then it makes sense to review and if necessary
update package repository \c{README.md} (\l{#core-repo-readme Adjust package
repository \c{README.md}}).
\h2#core-version-management-new-version-issues|New version: review/fix accumulated issues|
When a bug is identified in an already released package version, we may not
always be able to fix it immediately (for example, by
\l{#core-version-management-new-revision releasing a revision}). This could be
because the change is too extensive/risky for a revision or simply not
critical enough to warrant a release. In such cases it's recommended to file
an issue in the package repository with the view to fix it when the next
opportunity arises. Releasing a new upstream version is one such opportunity
and it makes sense to review any accumulated package issues and see if any
of them could be addressed.
\h2#core-version-management-new-version-test|New version: test locally and with CI|
Once all the adjustments are in, test the package both locally and with CI
similar to how we did it during the initial packaging after completing the
smoke test:
\l{#core-test-smoke-locally Test locally}\n
\l{#core-test-smoke-locally-install Test locally: installation}\n
\l{#core-test-smoke-locally-dist Test locally: distribution}\n
\l{#core-test-smoke-ci Commit and test with CI}
\h2#core-version-management-new-version-release|New version: merge, release, and publish|
When the new version of the package is ready to be released, merge the
work branch to \c{master} (or equivalent):
\
$ git checkout master
$ git merge --ff-only wip-2.2.0
\
Then release and publish using the same steps as after the initial packaging:
\l{#core-release-publish Release and publish}.
\h2#core-version-management-old-series|New version/revision in old release series|
As discussed in \l{#core-version-management Package version management}, if we
have already switched to the next upstream version in the \c{master} (or
equivalent) branch, we cannot go back and release a new version or revision
for an older release series on the same branch. Instead, we need to create a
separate, long-lived branch for this work.
As an example, let's say we need to release another revision or a patch
version for an already released \c{2.1.0} while our \c{master} branch has
already moved on to \c{2.2.0}. In this case we create a new branch, called
\c{2.1}, to continue with the \c{2.1.Z} release series. The starting point of
this branch should be the latest released version/revision in the \c{2.1}
series. Let's say in our case it is \c{2.1.0+2}, meaning we have released two
revisions for \c{2.1.0} on the \c{master} branch before upgrading to
\c{2.2.0}. Therefore we use the \c{v2.1.0+2} release tag to start the
\c{2.1} branch:
\
$ git checkout -b 2.1 v2.1.0+2
\
Once this is done, we continue with the same steps as in
\l{#core-version-management-new-revision New revision} or
\l{#core-version-management-new-version New version} except that we never
merge this branch to \c{master}. If we ever need to release another revision
or version in this release series, then we continue using this branch. In a
sense, this branch becomes the equivalent of the \c{master} branch for this
release series and you should treat it as such (once published, never delete,
rewrite its history, etc).
\N|It is less likely but possible that you may need to release a new minor
version in an old release series. For example, the master branch may have
moved on to \c{3.0.0} and you want to release \c{2.2.0} after the already
released \c{2.1.0}. In this case it makes sense to call the branch \c{2} since
it corresponds to the \c{2.Y.Z} release series. If you already have the
\c{2.1} branch, then it makes sense to rename it to \c{2}.|
\h1#dont-do|What Not to Do|
This chapter describes the common anti-patterns along with the recommended
alternative approaches.
\h#dont-from-scratch|Don't write \c{buildfiles} from scratch, use \c{bdep-new}|
Unless you have good reasons not to, create the initial project layout
automatically using \l{bdep-new(1)}, then tweak it if necessary and fill with
upstream source code.
The main rationale here is that there are many nuances in getting the build
right and auto-generated \c{buildfiles} had years of refinement and
fine-tuning. The familiar structure also makes it easier for others to
understand your build, for example while reviewing your package submission
or helping out with the package maintenance.
The \l{bdep-new(1)} command supports a wide variety of
\l{bdep-new.xhtml#src-layout source layouts}. While it may take a bit of time
to understand the customization points necessary to achieve the desired layout
for your first package, this will pay off in spades when you work on
converting subsequent packages.
See \l{#core-package-craft-cmd Craft \c{bdep\ new} command line to create
package} for details.
\h#dont-fix-upstream|Avoid fixing upstream issues in the \c{build2} package|
Any deviations from upstream makes the \c{build2} package more difficult to
maintain. In particular, if you make a large number of changes to the upstream
source code, releasing a new version will require a lot of work. As a result,
it is recommended to avoid fixing upstream issues in the \c{build2} package.
Instead, try to have the issues fixed upstream and wait for them to be
released as a new version.
Sometimes, however, you may have no choice. For example, upstream is inactive
or has no plans to release a new version with your fixes any time soon. Or you
may want to add support for a platform/compiler that upstream is not willing
or capable of supporting.
Note that even if you do fix some issues in the \c{build2} package directly,
try hard to also incorporate them upstream so that you don't need to maintain
the patches forever.
See also \l{#dont-change-upstream Avoid changing upstream source code layout}
and \l{#howto-patch-upstream-source How do I patch upstream source code?}
\h#dont-change-upstream|Avoid changing upstream source code layout|
It's a good idea to stay as close to the upstream's source code layout as
possible. For background and rationale, see \l{#core-package-struct Decide on
the package source code layout}.
\h#dont-header-only|Don't make library header-only if it can be compiled|
Some libraries offer two alternative modes: header-only and compiled. Unless
there are good reasons not to, a \c{build2} build of such a library should use
the compiled mode.
\N|Some libraries use the \i{precompiled} term to describe the non-header-only
mode. We don't recommend using this term in the \c{build2} package since it
has a strong association with precompiled headers and can therefore be
confusing. Instead, use the \i{compiled} term.|
The main rationale here is that a library would not be offering a compiled
mode if there were no benefits (usually faster compile times of library
consumers) and there is no reason not to take advantage of it in the
\c{build2} package.
There are, however, valid reasons why a compiled mode cannot be used, the most
common of which are:
\ul|
\li|The compiled mode is not well maintained/tested by upstream and therefore
offers inferior user experience.|
\li|The compiled mode does not work on some platforms, usually Windows due to
the lack of symbol export support (but see \l{b#cc-auto-symexport Automatic
DLL Symbol Exporting}).|
\li|Uses of the compiled version of the library requires changes to the
library consumers, for example, inclusion of different headers.|
|
If a compiled mode cannot always be used, then it may be tempting to support
both modes by making the mode user-configurable. Unless there are strong
reasons to, you should resist this temptation and, if the compiled mode is not
universally usable, then use the header-only mode everywhere.
The main rationale here is that variability adds complexity which makes the
result more prone to bugs, more difficult to use, and harder to review and
maintain. If you really want to have the compiled mode, then the right way to
achieve it is to work with upstream to fix any issues that prevent its use
in \c{build2}.
There are, however, valid reasons why supporting both modes may be needed, the
most common of which are:
\ul|
\li|The library is widely used in both modes but switching from one mode to
the other requires changes to the library consumers (for example, inclusion of
different headers). In this case only supporting one mode would mean not
supporting a large number of library consumers.|
\li|The library consists of a large number of independent components while its
common for applications to only use a small subset of them. And compiling all
of them in the compiled mode takes a substantial amount of time. Note that
this can alternatively be addressed by making the presence of optional
components user-configurable.|
|
\h#dont-bundle|Don't bundle dependencies|
Sometimes third-party projects bundle their dependencies with their source
code (also called vendoring). For example, a C++ library may bundle a testing
framework. This is especially common with \l{https://cppget.org/catch2
\c{catch2}} where one often encounters a comical situation with only a few
kilobytes of library source code and over 600KB of \c{catch2.hpp}.
The extra size, while wasteful, is not the main issue, however. The bigger
problem is that if a bug is fixed in the bundled dependency, then to propagate
the fix we will need to release a new version (or revision) of each package
that bundles it. Needless to say this is not scalable.
While this doesn't apply to testing frameworks, an even bigger issue with
bundling of dependencies in general is that two libraries that bundle the same
dependency (potentially of different versions) may not be able to coexist in
the same build with the symptoms ranging from compile errors to subtle runtime
issues that are hard to diagnose.
As a result, it is strongly recommended that you unbundle any dependencies
that upstream may have bundled. In case of testing frameworks, see
\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md
How do I handle tests that have extra dependencies?} for the recommended way
to deal with such cases.
\N|One special case where a bundled dependency may be warranted is a small
utility that is completely inline/private to the implementation and where
making it an external dependency may lead to a less performant result (due to
the inability to inline without resorting to LTO). The \c{xxhash}
implementation in \c{libzstd} is a representative example of this situation.|
\h#dont-main-target-root-buildfile|Don't build your main targets in the root \c{buildfile}|
It may be tempting to have your main targets (libraries, executables) in the
root \c{buildfile}, especially if it allows you to symlink entire directories
from \c{upstream/} (which is not possible if you have to have a \c{buildfile}
inside). However, this is not recommended except for the simplest of projects.
Firstly, this quickly gets messy since you have to combine managing
\c{README}, \c{LICENSE}, etc., and subdirectories with your main target
builds. More importantly, this also means that when your main target is
imported (and thus the \c{buildfile} that defines this target must be loaded),
your entire project will be loaded, including any \c{tests/} and \c{examples/}
subprojects, and that is wasteful.
If you want to continue symlinking entire directories from \c{upstream/} but
without moving everything to the root \c{buildfile}, the recommended approach
is to simply add another subdirectory level. Let's look at a few concrete
example to illustrate the technique (see \l{#core-package-struct Decide on the
package source code layout} for background on the terminology used).
Here is the directory structure of a package which uses a combined layout (no
header/source split) and where the library target is in the root
\c{buildfile}:
\
libigl-core/
├── igl/ -> ../upstream/igl/
├── tests/
└── buildfile # Defines lib{igl-core}.
\
And here is the alternative structure where we have added the extra
\c{libigl-core} subdirectory with its own \c{buildfile}:
\
libigl-core/
├── libigl-core/
│ ├── igl/ -> ../../upstream/igl/
│ └── buildfile # Defines lib{igl-core}.
├── tests/
└── buildfile
\
Below is the \c{bdep-new} invocation that can be used to automatically
create this alternative structure (see \l{#core-package-craft-cmd Craft
\c{bdep\ new} command line to create package} for background and
\l{bdep-new(1)} for details):
\
$ bdep new \
--type lib,prefix=libigl-core,subdir=igl,buildfile-in-prefix \
libigl-core
\
Let's also look at an example of the split layout, which may require a
slightly different \c{bdep-new} sub-options to achieve the same result. Here
is the layout which matched upstream exactly:
\
$ bdep new --type lib,split,subdir=foo,no-subdir-source libfoo
$ tree libfoo
libfoo/
├── include/
│ └── foo/
│ ├── buildfile
│ └── ...
└── src/
├── buildfile
└── ...
\
However, with this layout we will not be able to symlink the entire
\c{include/foo/} and \c{src/} subdirectories because there are \c{buildfiles}
inside (and which may tempt you to just move everything to the root
\c{buildfile}). To fix this we can move the \c{buildfiles} out of source
subdirectory \c{foo/} and into prefixes (\c{include/} and \c{src/}) using the
\c{buildfile-in-prefix} sub-option. And since \c{src/} doesn't have a source
subdirectory, we have to invent one:
\
$ bdep new --type lib,split,subdir=foo,buildfile-in-prefix libfoo
$ tree libfoo
libfoo/
├── include/
│ ├── foo/ -> ../../upstream/include/foo/
│ └── buildfile
└── src/
├── foo/ -> ../../upstream/src/
└── buildfile
\
\h#dont-big-changes-revision|Don't make extensive changes in a revision|
Unlike a new version, a revision replaces the previous revision of the same
version and as a result must be strictly backwards-compatible in all aspects
with what it replaces. If you make extensive changes in a revision, it becomes
difficult to guarantee that this requirement is satisfied. As a result, you
should refrain from making major changes, like substantially altering the
build, in a revision, instead delaying such changes until the next version.
\h1#howto|Packaging HOWTO|
This chapter provides advice on how to handle less common packaging
tasks and requirements.
\h#howto-patch-upstream-source|How do I patch upstream source code?|
If you need to change something in the upstream source code, there are several
options: You can make a copy of the upstream source file and make the
modifications there. While straightforward, this approach has one major
drawback: you will have to keep re-applying the changes for every subsequent
version unless and until upstream incorporates your changes. The other two
options try to work around this drawback.
The first alternative option is to modify the upstream source code
automatically during the build, typically using an ad hoc recipe. This
approach works best when the changes are regular and can be applied
mechanically with something like the \l{testscript#builtins-sed \c{sed}
builtin}.
The second alternative option is to use the C/C++ preprocessor to make the
necessary changes to the upstream source code during compilation. Unlike the
first alternative, this approach doesn't have a prescribed way to apply it in
every situation and often requires some imagination. Note that it also has the
tendency to quickly get out of hand, at which point it's wise to keep it
simple and use the first option (manual modification).
The following sections examine each approach in detail.
\h2#howto-patch-upstream-source-manual|Modifying upstream source code manually|
As an illustration of this approach, let's say we need to patch
\c{src/foo.cpp} in our \c{libfoo} example from the previous sections (see the
\l{#core-fill-source Fill with upstream source code} step for a refresher).
The recommended sequence of steps is as follows:
\ol|
\li|Rename the upstream symlink to \c{.orig}:
\
$ cd libfoo/src/
$ mv foo.cpp foo.cpp.orig
\
|
\li|Make a deep copy of \c{.orig}:
\
$ cp -H foo.cpp.orig foo.cpp
\
|
\li|Make any necessary modifications in the deep copy:
\
$ edit foo.cpp
\
|
\li|Create a patch for the modifications:
\
$ diff -u foo.cpp.orig foo.cpp >foo.cpp.patch
\
||
The presence of the \c{.orig} and \c{.patch} files makes it clear that the
upstream code was modified. They are also useful when re-applying the changes
to the new version of the upstream source code. The recommended sequence of
steps for this task is as follows:
\ol|
\li|After the \c{upstream} submodule update (see the
\l{#core-version-management-new-version-submodule New version: update
\c{upstream} submodule} step), the \c{.orig} symlink points to the new version
of the upstream source file.|
\li|Overwrite old modified version with a deep copy of new \c{.orig}:
\
$ cp -H foo.cpp.orig foo.cpp
\
|
\li|Apply old modifications to the new deep copy:
\
$ patch <foo.cpp.patch
\
If some hunks of the patch could not be applied, manually resolve any
conflicts.|
\li|If in the previous step the patch did not apply cleanly, regenerate it:
\
$ diff -u foo.cpp.orig foo.cpp >foo.cpp.patch
\
||
\h2#howto-patch-upstream-source-build|Modifying upstream source code during build|
As an illustration of this approach, let's say upstream is using the
\c{${VAR\}} style variable substitutions in their \c{config.h.cmake} instead
of the more traditional \c{@VAR@} style:
\
/* config.h.cmake */
#define FOO_VERSION \"${PROJECT_VERSION}\"
\
The \c{${VAR\}} style is not supported by the \c{build2}
\l{https://github.com/build2/libbuild2-autoconf \c{autoconf}} module which
means we cannot use the upstream \c{config.h.cmake} as is. While we could
patch this file manually to use \c{@VAR@} instead, this is a pretty mechanical
change that can be easily made with a simple ad hoc recipe during the build,
freeing us from manually applying the same changes in subsequent versions. For
example:
\
using autoconf
h{config}: in{config.h.in}
{
autoconf.flavor = cmake
PROJECT_VERSION = $version
}
in{config.h.in}: file{config.h.cmake}
{{
sed -e 's/\$\{(.+)\}/@\1@/g' $path($<) >$path($>)
}}
\
\h2#howto-patch-upstream-source-preproc|Modifying upstream source code with C/C++ preprocessor|
A good illustration of this approach is adding the \c{build2} metadata to an
executable (see
\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md
How do I convey additional information (metadata) with executables and C/C++
libraries?} for background). Let's say we have a symlink to upstream's
\c{main.c} that implements the executable's \c{main()} function and we need to
add a snipped of code at the beginning of this function that handles the
\c{--build2-metadata} option. While manually modifying \c{main.c} is not a
wrong approach, we can try to be clever and do it automatically with the
preprocessor.
Specifically, we can create another file next to the \c{main.c} symlink,
calling it, for example, \c{main-build2.c}, with the following contents:
\
/* Handle --build2-metadata in main() (see also buildfile). */
#define main xmain
#include \"main.c\"
#undef main
#include <stdio.h>
#include <string.h>
int main (int argc, const char** argv)
{
if (argc == 2 && strncmp (argv[1], \"--build2-metadata=\", 18) == 0)
{
printf (\"# build2 buildfile foo\n\");
printf (\"export.metadata = 1 foo\n\");
printf (\"foo.name = [string] foo\n\");
...
return 0;
}
return xmain (argc, argv);
}
\
The idea here is to rename the original \c{main()} with the help of the C
preprocessor and provide our own \c{main()} which, after handling
\c{--build2-metadata} calls the original. One notable deal-breaker for this
approach would be a C++ implementation of \c{main()} that doesn't have the
explicit \c{return}. There is also a better chance in C++ for the \c{main}
macro to replace something unintended.
To complete this we also need to modify our \c{buildfile} to exclude
\c{main.c} from compilation (since it is compiled as part of \c{main-build2.c}
via the preprocessor inclusion). For example:
\
exe{foo}: {h c}{** -main}
exe{foo}: c{main}: include = adhoc # Included in main-build2.c.
\
\h#howto-bad-inclusion-practice|How do I deal with bad header inclusion practice?|
This section explains how to deal with libraries that include their public,
generically-named headers without the library name as a subdirectory
prefix. Such libraries cannot coexist, neither in the same build nor when
installed.
Specifically, as an illustration of the problem, consider the \c{libfoo}
library with a public header named \c{util.h} that is included as \c{<util.h>}
(instead of, say, \c{<libfoo/util.h>} or \c{<foo/util.h>}). If this library's
headers are installed directly into, say, \c{/usr/include}, then if two such
libraries happened to be installed at the same time, then one will overwrite
the other's header. There are also problems in the non-installed case: if two
such libraries are used by the same project, then which \c{<util.h>} header
gets included depends on which library's header search path ends up being
specified first on the command line (with the \c{-I} option).
These issues are severe enough that libraries with such inclusion issues
cannot be published to \l{https://cppget.org cppget.org} without them being
addressed in the \c{build2} package. Thankfully, most library authors these
days use the library name as an inclusion prefix (or sometimes they have
headers that are decorated with the library name). However, libraries that do
not follow these guidelines do exist and this section describes how to change
their inclusion scheme if you are attempting to package one of them.
\N|One notable consequence of changing the inclusion scheme is that it will no
longer be possible to use a system-installed version of the package (because
it presumably still uses the unqualified inclusion scheme). Note, however,
that distributions like Debian and Fedora have the same co-existence issue as
we do and are generally strict about potential header clashes. In
particular, it is not uncommon to find Debian packages installing library
headers into subdirectories of \c{/usr/include} to avoid such clashes. And if
you find this to be the case for the library you are packaging, then it may
make sense to use the same prefix as used by the main distributions for
compatibility.
It is also possible that distributions disregard these considerations for some
libraries. This usually happens for older, well-known libraries that happened
to be installed this way in the early days and changing things now will be too
disruptive. In a sense, it is understood that such libraries effectively
\"own\" the unqualified header names that they happen to be using. If you
think you are packaging such a library,
\l{https://build2.org/community.xhtml#help get in touch} to discuss this
further since it may make sense to also disregard this rule in
\l{https://cppget.org cppget.org}.|
As a concrete example of the approach, let's continue with \c{libfoo} that has
\c{util.h} and which upstream expects the users to include as \c{<util.h>}.
The is what the upstream source code layout may look like:
\
libfoo/
├── include/
│ └── util.h
└── src/
└── ...
\
Our plan is to change the inclusion scheme in the \c{build2} package from
\c{<util.h>} to \c{<libfoo/util.h>}. To this effect, we use a slightly
modified layout for our package (see \l{#core-package-craft-cmd Craft \c{bdep
new} command line to create package} on how to achieve it):
\
libfoo/
├── include/
│ └── libfoo/
│ └── util.h -> ../../../upstream/include/util.h
└── src/
└── ... -> ../../upstream/src/...
\
The installation-related section in our \l{#core-adjust-build-src-header
header \c{buildfile}} will look like this:
\
# Install into the libfoo/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/libfoo/
install.subdirs = true
}
\
In the \l{#core-adjust-build-src-source source \c{buildfile}} we will most
likely need to add the \c{include/libfoo} header search path since the
upstream source files continue to include public headers without the library
prefix (there should be no harm in that and it's not worth modifying them):
\
# Build options.
#
out_pfx_inc = [dir_path] $out_root/include/
src_pfx_inc = [dir_path] $src_root/include/
out_pfx_src = [dir_path] $out_root/src/
src_pfx_src = [dir_path] $src_root/src/
# Unqualified (without <libfoo/...>) header search paths.
#
out_pfx_inc_unq = [dir_path] $out_root/include/libfoo
src_pfx_inc_unq = [dir_path] $src_root/include/libfoo
cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \\
\"-I$out_pfx_inc\" \"-I$src_pfx_inc\" \\
\"-I$out_pfx_inc_unq\" \"-I$src_pfx_inc_unq\"
\
It is also possible that public headers include each other as \c{<util.h>}
rather than the more common \c{\"util.h\"}. If that's the case, then we need
to fix that and there are two ways to do it. The first approach is to patch
the public headers to include each other with the library prefix (that is,
\c{<libfoo/util.h>}, etc). See \l{#howto-patch-upstream-source How do I patch
upstream source code?} for details.
The second approach is to support including public headers both ways, that is,
as \c{<libfoo/util.h>} and as \c{<util.h>}. This will not only solve the above
problem (public headers including each other), but also support any existing
code that uses this library and most likely includes its headers the old way,
without the prefix.
There is, however, a major drawback to doing that: while the installation of
the library can now co-exist with other libraries (because we install its
public headers into, say, \c{/usr/include/libfoo}), it may still not be usable
in combination with other libraries from the same build (because we still add
the unqualified header search path).
If you still want to provide this dual inclusion support, the way to achieve
it is by exporting the unqualified header search path and also adding it to
the \c{pkg-config} files (see \l{#howto-extra-header-install-subdir How do I
handle extra header installation subdirectory?} for background on the
latter). For example:
\
# Export options.
#
lib{foo}:
{
cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" \\
\"-I$out_pfx_inc_unq\" \"-I$src_pfx_inc_unq\"
cxx.export.libs = $intf_libs
}
# Make sure headers installed into, say, /usr/include/libfoo/
# can also be included without the directory prefix for backwards
# compatibility.
#
lib{foo}: cxx.pkgconfig.include = include/ include/libfoo/
\
\h#howto-extra-header-install-subdir|How do I handle extra header installation subdirectory?|
This section explains how to handle an additional header installation
subdirectory. As an illustration of the problem, consider the \c{libfoo}
example from the previous sections (see the \l{#core-fill-source Fill with
upstream source code} step for a refresher). In that example the library
headers are included as \c{<foo/util.hpp>} and installed as, say,
\c{/usr/include/foo/util.hpp}. In this scheme the installed header inclusion
works without requiring any extra steps from our side because the compiler
searches for header in \c{/usr/include} by default.
However, some libraries choose to install their headers into a subdirectory
of, say, \c{/usr/include} but without having this subdirectory as part of the
inclusion path (\c{foo/} in \c{<foo/util.hpp>}). The two typical reasons for
this are support for installing multiple versions of the same library
side-by-side (for example, \c{/usr/include/foo-v1/foo/util.hpp}) as well as
not using the library name as the inclusion subdirectory prefix and then
having to hide the headers in a subdirectory due to potential clashes with
other headers (if installed directly into, say, \c{/usr/include}; see
\l{#howto-bad-inclusion-practice How do I deal with bad header inclusion
practice?} for background).
In such cases the installed header inclusion does not work out of the box and
we have to arrange for an additional header search path to be added via
\c{pkg-config}. Let's use the versioned library case to illustrate this
technique. The relevant part from the \l{#core-adjust-build-src-header header
\c{buildfile}} will look like this:
\
# Install into the foo-vN/foo/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/\"foo-v$version.major\"/foo/
install.subdirs = true
}
\
The part that we need to add, this time to the
\l{#core-adjust-build-src-source source \c{buildfile}}, looks like this:
\
# Make sure headers installed into, say, /usr/include/foo-vN/foo/
# can be included as <foo/*.hpp> by overriding the header search
# path in the generated pkg-config files.
#
lib{foo}: cxx.pkgconfig.include = include/\"foo-v$version.major\"/
\
\N|The variable will be \c{c.pkgconfig.include} for a C library.|
\h#howto-no-extension-header|How do I handle headers without an extension?|
If all the headers in a project have no extension, then you can simply
specify the empty \c{extension} value for the \c{hxx{\}} target type
in \c{build/root.build}:
\
hxx{*}: extension =
cxx{*}: extension = cpp
\
Note, however, that using wildcard patterns for such headers in your
\c{buildfile} is a bad idea since such a wildcard will most likely pick up
other files that also have no extension (such as \c{buildfile}, executables on
UNIX-like systems, etc). Instead, it's best to spell the names of such headers
explicitly. For example, instead of:
\
lib{hello}: {hxx cxx}{*}
\
Write:
\
lib{hello}: cxx{*} hxx{hello}
\
If only some headers in a project have no extension, then it's best to specify
the non-empty extension for the \c{extension} variable in \c{build/root.build}
(so that you can still use wildcards for headers with extensions) and spell
out the headers with no extension explicitly. Continuing with the above
example, if we have both the \c{hello.hpp} and \c{hello} headers, then we can
handle them like this:
\
hxx{*}: extension = hpp
cxx{*}: extension = cpp
\
\
lib{hello}: {hxx cxx}{*} hxx{hello.}
\
Notice the trailing dot in \c{hxx{hello.\}} \- this is the explicit \"no
extension\" specification. See \l{b#targets Targets and Target Types}
for details.
\h#howto-debug-macro|How do I expose extra debug macros of a library?|
Sometimes libraries provide extra debugging facilities that are usually
enabled or disabled with a macro. For example, \c{libfoo} may provide the
\c{LIBFOO_DEBUG} macro that enables additional sanity checks, tracing, etc.
Normally, such facilities are disabled by default.
While it may seem like a good idea to detect a debug build and enable this
automatically, it is not: such facilities usually impose substantial overheads
and the presence of debug information does not mean that performance is not
important (people routinely make optimized builds with debug information).
As a result, the recommended approach is to expose this as a configuration
variable that the consumers of the library can use (see \l{b#proj-config
Project Configuration} for background). Continue with the \c{libfoo} example,
we can add \c{config.libfoo.debug} to its \c{build/root.build}:
\
# build/root.build
config [bool] config.libfoo.debug ?= false
\
And then define the \c{LIBFOO_DEBUG} macro based on that in the \c{buildfile}:
\
# src/buildfile
if $config.libfoo.debug
cxx.poptions += -DLIBFOO_DEBUG
\
If the macro is also used in the library's interface (for example, in inline
or template functions), then we will also need to export it (see
\l{#core-adjust-build-src-source-opt Adjust source \c{buildfile}: build and
export options} for details):
\
# src/buildfile
if $config.libfoo.debug
{
cxx.poptions += -DLIBFOO_DEBUG
lib{foo}: cxx.export.poptions += -DLIBFOO_DEBUG
}
\
\N|If the debug facility in question should be enabled by default even in the
optimized builds (in which case the macro usually has the \c{NO_DEBUG}
semantics), the other option is to hook it up to the standard \c{NDEBUG}
macro, for example, in the library's configuration header file.|
Note that such \c{.debug} configuration variables should primarily be meant
for the user to selectively enable extra debugging support in certain
libraries of their build. However, if your project depends on a number of
libraries with such extra debugging support and it generally makes sense to
also enable this support in dependencies if it is enabled in your project,
then you may want to propagate your \c{.debug} configuration value to the
dependencies (see the \l{bpkg#manifest-package-depends \c{depends} package
\c{manifest} value} for details on dependency configuration). You, however,
should still allow the user to override this decision on the per-dependency
basis.
Continuing with the above example, let's say we have \c{libbar} with
\c{config.libbar.debug} that depends on \c{libfoo} and wishes by default to
enable debugging in \c{libfoo} if it is enabled in \c{libbar}. This is how we
can correctly arrange for this in \c{libbar}'s \c{manifest}:
\
depends:
\\
libfoo ^1.2.3
{
# We prefer to enable debug in libfoo if enabled in libbar
# but accept if it's disabled (for example, by the user).
#
prefer
{
if $config.libbar.debug
config.libfoo.debug = true
}
accept (true)
}
\\
\
\h1#faq|Packaging FAQ|
\h#faq-alpha-stable|Why is my package in \c{alpha} rather than \c{stable}?|
If your package uses a semver version (or semver-like, that is, has three
version components) and the first component is zero (for example, \c{0.1.0}),
then, according to the semver specification, this is an alpha version and
\l{bdep-publish(1)} automatically published such a version to the \c{alpha}
section of the repository.
Sometimes, however, in a third-party package, while the version may look like
semver, upstream may not assign the zero first component any special
meaning. In such cases you can override the \c{bdep-publish} behavior with the
\c{--section} option, for example:
\
$ bdep publish --section=stable
\
Note that you should only do this if you are satisfied that by having the zero
first component upstream does not imply alpha quality. Getting an explicit
statement to this effect from upstream is recommended.
\h#faq-publish-stage|Where to publish if package requires staged toolchain?|
If your package requires the \l{https://build2.org/community.xhtml#stage staged
toolchain}, for example, because it needs a feature or bugfix that is not yet
available in the released toolchain, then you won't be able to publish it to
\c{cppget.org}. Specifically, if your package has the accurate \c{build2}
version constraint and you attempt to publish it, you will get an error like
this:
\
error: package archive is not valid
info: unable to satisfy constraint (build2 >= 0.17.0-) for package foo
info: available build2 version is 0.16.0
\
There are three alternative ways to proceed in this situation:
\ol|
\li|Wait until the next release and then publish the package to
\c{cppget.org}.|
\li|If the requirement for the staged toolchain is \"minor\", that is, it
doesn't affect the common functionality of the package or only affects a small
subset of platforms/compilers, then you can lower the toolchain version
requirement and publish the package to \c{cppget.org}. For example, if you
require the staged toolchain because of a bugfix that only affects one
platform, it doesn't make sense to delay publishing the package since it is
perfectly usable on all the other platforms in the meantime.|
\li|Publish it to \l{https://queue.stage.build2.org queue.stage.build2.org},
the staging package repository. This repository contain new packages that
require the staged toolchain to work and which will be automatically
moved to \c{cppget.org} once the staged version is released. The other
advantage of publishing to this repository (besides not having to remember
to manually publish the package once the staged version is released) is
that your package becomes available from an archive repository, which is
substantially faster than a \c{git} repository.
To publish to this repository, use the following \c{bdep-publish} command
line:
\
$ bdep publish --repository=https://stage.build2.org ...
\
||
"
|