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
|
########################################################################
# 2025 April 5
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# * May you do good and not evil.
# * May you find forgiveness for yourself and forgive others.
# * May you share freely, never taking more than you give.
#
########################################################################
# ----- @module teaish.tcl -----
# @section TEA-ish ((TCL Extension Architecture)-ish)
#
# Functions in this file with a prefix of teaish__ are
# private/internal APIs. Those with a prefix of teaish- are
# public APIs.
#
# Teaish has a hard dependency on proj.tcl, and any public API members
# of that module are considered legal for use by teaish extensions.
#
# Project home page: https://fossil.wanderinghorse.net/r/teaish
use proj
#
# API-internal settings and shared state.
array set teaish__Config [proj-strip-hash-comments {
#
# Teaish's version number, not to be confused with
# teaish__PkgInfo(-version).
#
version 0.1-beta
# set to 1 to enable some internal debugging output
debug-enabled 0
#
# 0 = don't yet have extension's pkgindex
# 0x01 = found TEAISH_EXT_DIR/pkgIndex.tcl.in
# 0x02 = found srcdir/pkgIndex.tcl.in
# 0x10 = found TEAISH_EXT_DIR/pkgIndex.tcl (static file)
# 0x20 = static-pkgIndex.tcl pragma: behave as if 0x10
# 0x100 = disabled by -tm.tcl.in
# 0x200 = disabled by -tm.tcl
#
# Reminder: it's significant that the bottom 4 bits be
# cases where teaish manages ./pkgIndex.tcl.
#
pkgindex-policy 0
#
# The pkginit counterpart of pkgindex-policy:
#
# 0 = no pkginit
# 0x01 = found default X.in: generate X from X.in
# 0x10 = found static pkginit file X
# 0x02 = user-provided X.in generates ./X.
# 0x20 = user-provided static pkginit file X
#
# The 0x0f bits indicate that teaish is responsible for cleaning up
# the (generated) pkginit file.
#
pkginit-policy 0
#
# 0 = no tm.tcl
# 0x01 = tm.tcl.in
# 0x10 = static tm.tcl
tm-policy 0
#
# If 1+ then teaish__verbose will emit messages.
#
verbose 0
#
# Mapping of pkginfo -flags to their TEAISH_xxx define (if any).
# This must not be modified after initialization.
#
pkginfo-f2d {
-name TEAISH_NAME
-name.dist TEAISH_DIST_NAME
-name.pkg TEAISH_PKGNAME
-version TEAISH_VERSION
-libDir TEAISH_LIBDIR_NAME
-loadPrefix TEAISH_LOAD_PREFIX
-vsatisfies TEAISH_VSATISFIES
-pkgInit.tcl TEAISH_PKGINIT_TCL
-pkgInit.tcl.in TEAISH_PKGINIT_TCL_IN
-url TEAISH_URL
-tm.tcl TEAISH_TM_TCL
-tm.tcl.in TEAISH_TM_TCL_IN
-options {}
-pragmas {}
}
#
# Queues for use with teaish-checks-queue and teaish-checks-run.
#
queued-checks-pre {}
queued-checks-post {}
# Whether or not "make dist" parts are enabled. They get enabled
# when building from an extension's dir, disabled when building
# elsewhere.
dist-enabled 1
# Whether or not "make install" parts are enabled. By default
# they are, but we have a single use case where they're
# both unnecessary and unhelpful, so...
install-enabled 1
# By default we enable compilation of a native extension but if the
# extension has no native code or the user wants to take that over
# via teaish.make.in or provide a script-only extension, we will
# elide the default compilation rules if this is 0.
dll-enabled 1
# Files to include in the "make dist" bundle.
dist-files {}
# List of source files for the extension.
extension-src {}
# Path to the teaish.tcl file.
teaish.tcl {}
# Dir where teaish.tcl is found.
extension-dir {}
# Whether the generates TEASH_VSATISFIES_CODE should error out on a
# satisfies error. If 0, it uses return instead of error.
vsatisfies-error 1
# Whether or not to allow a "full dist" - a "make dist" build which
# includes both the extension and teaish. By default this is only on
# if the extension dir is teaish's dir.
dist-full-enabled 0
}]
set teaish__Config(core-dir) $::autosetup(libdir)/teaish
#
# Array of info managed by teaish-pkginfo-get and friends. Has the
# same set of keys as $teaish__Config(pkginfo-f2d).
#
array set teaish__PkgInfo {}
#
# Runs {*}$args if $lvl is <= the current verbosity level, else it has
# no side effects.
#
proc teaish__verbose {lvl args} {
if {$lvl <= $::teaish__Config(verbose)} {
{*}$args
}
}
#
# @teaish-argv-has flags...
#
# Returns true if any arg in $::argv matches any of the given globs,
# else returns false.
#
proc teaish-argv-has {args} {
foreach glob $args {
foreach arg $::argv {
if {[string match $glob $arg]} {
return 1
}
}
}
return 0
}
if {[teaish-argv-has --teaish-verbose --t-v]} {
# Check this early so that we can use verbose-only messages in the
# pre-options-parsing steps.
set ::teaish__Config(verbose) 1
#teaish__verbose 1 msg-result "--teaish-verbose activated"
}
msg-quiet use system ; # Outputs "Host System" and "Build System" lines
if {"--help" ni $::argv} {
teaish__verbose 1 msg-result "TEA(ish) Version = $::teaish__Config(version)"
teaish__verbose 1 msg-result "Source dir = $::autosetup(srcdir)"
teaish__verbose 1 msg-result "Build dir = $::autosetup(builddir)"
}
#
# @teaish-configure-core
#
# Main entry point for the TEA-ish configure process. auto.def's primary
# (ideally only) job should be to call this.
#
proc teaish-configure-core {} {
proj-tweak-default-env-dirs
set ::teaish__Config(install-mode) [teaish-argv-has --teaish-install*]
set ::teaish__Config(create-ext-mode) \
[teaish-argv-has --teaish-create-extension=* --t-c-e=*]
set gotExt 0; # True if an extension config is found
if {!$::teaish__Config(create-ext-mode)
&& !$::teaish__Config(install-mode)} {
# Don't look for an extension if we're in --t-c-e or --t-i mode
set gotExt [teaish__find_extension]
}
#
# Set up the core --flags. This needs to come before teaish.tcl is
# sourced so that that file can use teaish-pkginfo-set to append
# options.
#
options-add [proj-strip-hash-comments {
with-tcl:DIR
=> {Directory containing tclConfig.sh or a directory one level up from
that, from which we can derive a directory containing tclConfig.sh.
Defaults to the $TCL_HOME environment variable.}
with-tclsh:PATH
=> {Full pathname of tclsh to use. It is used for trying to find
tclConfig.sh. Warning: if its containing dir has multiple tclsh
versions, it may select the wrong tclConfig.sh!
Defaults to the $TCLSH environment variable.}
# TEA has --with-tclinclude but it appears to only be useful for
# building an extension against an uninstalled copy of TCL's own
# source tree. The policy here is that either we get that info
# from tclConfig.sh or we give up.
#
# with-tclinclude:DIR
# => {Specify the directory which contains the tcl.h. This should not
# normally be required, as that information comes from tclConfig.sh.}
# We _generally_ want to reduce the possibility of flag collisions with
# extensions, and thus use a teaish-... prefix on most flags. However,
# --teaish-extension-dir is frequently needed, so...
#
# As of this spontaneous moment, we'll settle on using --t-A-X to
# abbreviate --teaish-A...-X... flags when doing so is
# unambiguous...
ted: t-e-d:
teaish-extension-dir:DIR
=> {Looks for an extension in the given directory instead of the current
dir.}
t-c-e:
teaish-create-extension:TARGET_DIRECTORY
=> {Writes stub files for creating an extension. Will refuse to overwrite
existing files without --teaish-force.}
t-f
teaish-force
=> {Has a context-dependent meaning (autosetup defines --force for its
own use).}
t-d-d
teaish-dump-defines
=> {Dump all configure-defined vars to config.defines.txt}
t-v:=0
teaish-verbose:=0
=> {Enable more (often extraneous) messages from the teaish core.}
t-d
teaish-debug=0 => {Enable teaish-specific debug output}
t-i
teaish-install:=auto
=> {Installs a copy of teaish, including autosetup, to the target dir.
When used with --teaish-create-extension=DIR, a value of "auto"
(no no value) will inherit that directory.}
#TODO: --teaish-install-extension:=dir as short for
# --t-c-e=dir --t-i
t-e-p:
teaish-extension-pkginfo:pkginfo
=> {For use with --teaish-create-extension. If used, it must be a
list of arguments for use with teaish-pkginfo-set, e.g.
--teaish-extension-pkginfo="-name Foo -version 2.3"}
t-v-c
teaish-vsatisfies-check=1
=> {Disable the configure-time "vsatisfies" check on the target tclsh.}
}]; # main options.
if {$gotExt} {
# We found an extension. Source it...
set ttcl $::teaish__Config(teaish.tcl)
proj-assert {"" ne [teaish-pkginfo-get -name]}
proj-assert {[file exists $ttcl]} \
"Expecting to have found teaish.(tcl|config) by now"
if {[string match *.tcl $ttcl]} {
uplevel 1 {source $::teaish__Config(teaish.tcl)}
} else {
teaish-pkginfo-set {*}[proj-file-content -trim $ttcl]
}
unset ttcl
# Set up some default values if the extension did not set them.
# This must happen _after_ it's sourced but before
# teaish-configure is called.
array set f2d $::teaish__Config(pkginfo-f2d)
foreach {pflag key type val} {
- TEAISH_CFLAGS -v ""
- TEAISH_LDFLAGS -v ""
- TEAISH_MAKEFILE -v ""
- TEAISH_MAKEFILE_CODE -v ""
- TEAISH_MAKEFILE_IN -v ""
- TEAISH_PKGINDEX_TCL -v ""
- TEAISH_PKGINDEX_TCL_IN -v ""
- TEAISH_PKGINIT_TCL -v ""
- TEAISH_PKGINIT_TCL_IN -v ""
- TEAISH_PKGINIT_TCL_TAIL -v ""
- TEAISH_TEST_TCL -v ""
- TEAISH_TEST_TCL_IN -v ""
-version - -v 0.0.0
-name.pkg - -e {set ::teaish__PkgInfo(-name)}
-name.dist - -e {set ::teaish__PkgInfo(-name)}
-libDir - -e {
join [list \
$::teaish__PkgInfo(-name.pkg) \
$::teaish__PkgInfo(-version)] ""
}
-loadPrefix - -e {
string totitle $::teaish__PkgInfo(-name.pkg)
}
-vsatisfies - -v {{Tcl 8.5-}}
-pkgInit.tcl - -v ""
-pkgInit.tcl.in - -v ""
-url - -v ""
-tm.tcl - -v ""
-tm.tcl.in - -v ""
} {
set isPIFlag [expr {"-" ne $pflag}]
if {$isPIFlag} {
if {[info exists ::teaish__PkgInfo($pflag)]} {
# Was already set - skip it.
continue;
}
proj-assert {{-} eq $key}
set key $f2d($pflag)
}
proj-assert {"" ne $key}
set got [get-define $key "<nope>"]
if {"<nope>" ne $got} {
# Was already set - skip it.
continue
}
switch -exact -- $type {
-v {}
-e { set val [eval $val] }
default { proj-error "Invalid type flag: $type" }
}
#puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag got=$got"
define $key $val
if {$isPIFlag} {
set ::teaish__PkgInfo($pflag) $val
}
}
unset isPIFlag pflag key type val
array unset f2d
}; # sourcing extension's teaish.tcl
if {[llength [info proc teaish-options]] > 0} {
# Add options defined by teaish-options, which is assumed to be
# imported via [teaish-get -teaish-tcl].
set o [teaish-options]
if {"" ne $o} {
options-add $o
}
}
#set opts [proj-options-combine]
#lappend opts teaish-debug => {x}; #testing dupe entry handling
if {[catch {options {}} msg xopts]} {
# Workaround for <https://github.com/msteveb/autosetup/issues/73>
# where [options] behaves oddly on _some_ TCL builds when it's
# called from deeper than the global scope.
dict incr xopts -level
return {*}$xopts $msg
}
proj-xfer-options-aliases {
t-c-e => teaish-create-extension
t-d => teaish-debug
t-d-d => teaish-dump-defines
ted => teaish-extension-dir
t-e-d => teaish-extension-dir
t-e-p => teaish-extension-pkginfo
t-f => teaish-force
t-i => teaish-install
t-v => teaish-verbose
t-v-c => teaish-vsatisfies-check
}
scan [opt-val teaish-verbose 0] %d ::teaish__Config(verbose)
set ::teaish__Config(debug-enabled) [opt-bool teaish-debug]
set exitEarly 0
if {[proj-opt-was-provided teaish-create-extension]} {
teaish__create_extension [opt-val teaish-create-extension]
incr exitEarly
}
if {$::teaish__Config(install-mode)} {
teaish__install
incr exitEarly
}
if {$exitEarly} {
file delete -force config.log
return
}
proj-assert {1==$gotExt} "Else we cannot have gotten this far"
teaish__configure_phase1
}
#
# Internal config-time debugging output routine. It is not legal to
# call this from the global scope.
#
proc teaish-debug {msg} {
if {$::teaish__Config(debug-enabled)} {
puts stderr [proj-bold "** DEBUG: \[[proj-scope 1]\]: $msg"]
}
}
#
# Runs "phase 1" of the configuration, immediately after processing
# --flags. This is what will import the client-defined teaish.tcl.
#
proc teaish__configure_phase1 {} {
msg-result \
[join [list "Configuring build of Tcl extension" \
[proj-bold [teaish-pkginfo-get -name] \
[teaish-pkginfo-get -version]] "..."]]
uplevel 1 {
use cc cc-db cc-shared cc-lib; # pkg-config
}
teaish__check_tcl
apply {{} {
#
# If --prefix or --exec-prefix are _not_ provided, use their
# TCL_... counterpart from tclConfig.sh. Caveat: by the time we can
# reach this point, autosetup's system.tcl will have already done
# some non-trivial amount of work with these to create various
# derived values from them, so we temporarily end up with a mishmash
# of autotools-compatibility var values. That will be straightened
# out in the final stage of the configure script via
# [proj-remap-autoconf-dir-vars].
#
foreach {flag uflag tclVar} {
prefix prefix TCL_PREFIX
exec-prefix exec_prefix TCL_EXEC_PREFIX
} {
if {![proj-opt-was-provided $flag]} {
if {"exec-prefix" eq $flag} {
# If --exec-prefix was not used, ensure that --exec-prefix
# derives from the --prefix we may have just redefined.
set v {${prefix}}
} else {
set v [get-define $tclVar "???"]
teaish__verbose 1 msg-result "Using \$$tclVar for --$flag=$v"
}
proj-assert {"???" ne $v} "Expecting teaish__check_tcl to have defined $tclVar"
#puts "*** $flag $uflag $tclVar = $v"
proj-opt-set $flag $v
define $uflag $v
# ^^^ As of here, all autotools-compatibility vars which derive
# from --$flag, e.g. --libdir, still derive from the default
# --$flag value which was active when system.tcl was
# included. So long as those flags are not explicitly passed to
# the configure script, those will be straightened out via
# [proj-remap-autoconf-dir-vars].
}
}
}}; # --[exec-]prefix defaults
teaish__check_common_bins
#
# Set up library file names
#
proj-file-extensions
teaish__define_pkginfo_derived *
teaish-checks-run -pre
if {[llength [info proc teaish-configure]] > 0} {
# teaish-configure is assumed to be imported via
# teaish.tcl
teaish-configure
}
teaish-checks-run -post
apply {{} {
# Set up "vsatisfies" code for pkgIndex.tcl.in,
# _teaish.tester.tcl.in, and for a configure-time check. We would
# like to put this before [teaish-checks-run -pre] but it's
# marginally conceivable that a client may need to dynamically
# calculate the vsatisfies and set it via [teaish-configure].
set vs [get-define TEAISH_VSATISFIES ""]
if {"" eq $vs} return
set code {}
set n 0
# Treat $vs as a list-of-lists {{Tcl 8.5-} {Foo 1.0- -3.0} ...}
# and generate Tcl which will run package vsatisfies tests with
# that info.
foreach pv $vs {
set n [llength $pv]
if {$n < 2} {
proj-error "-vsatisfies: {$pv} appears malformed. Whole list is: $vs"
}
set pkg [lindex $pv 0]
set vcheck {}
for {set i 1} {$i < $n} {incr i} {
lappend vcheck [lindex $pv $i]
}
if {[opt-bool teaish-vsatisfies-check]} {
set tclsh [get-define TCLSH_CMD]
set vsat "package vsatisfies \[ package provide $pkg \] $vcheck"
set vputs "puts \[ $vsat \]"
#puts "*** vputs = $vputs"
scan [exec echo $vputs | $tclsh] %d vvcheck
if {![info exists vvcheck] || 0 == $vvcheck} {
proj-fatal -up $tclsh "check failed:" $vsat
}
}
if {$::teaish__Config(vsatisfies-error)} {
set vunsat \
[list error [list Package \
$::teaish__PkgInfo(-name) $::teaish__PkgInfo(-version) \
requires $pv]]
} else {
set vunsat return
}
lappend code \
[string trim [subst -nocommands \
{if { ![package vsatisfies [package provide $pkg] $vcheck] } {\n $vunsat\n}}]]
}; # foreach pv
define TEAISH_VSATISFIES_CODE [join $code "\n"]
}}; # vsatisfies
if {[proj-looks-like-windows] || [proj-looks-like-mac]} {
# Without this, linking of an extension will not work on Cygwin or
# Msys2.
msg-result "Using USE_TCL_STUBS for this environment"
teaish-cflags-add -DUSE_TCL_STUBS=1
}
#define AS_LIBDIR $::autosetup(libdir)
define TEAISH_TESTUTIL_TCL $::teaish__Config(core-dir)/tester.tcl
apply {{} {
#
# Ensure we have a pkgIndex.tcl and don't have a stale generated one
# when rebuilding for different --with-tcl=... values.
#
if {!$::teaish__Config(pkgindex-policy)} {
proj-error "Cannot determine which pkgIndex.tcl to use"
}
if {0x300 & $::teaish__Config(pkgindex-policy)} {
teaish__verbose 1 msg-result "pkgIndex disabled by -tm.tcl(.in)"
} else {
set tpi [proj-coalesce \
[get-define TEAISH_PKGINDEX_TCL_IN] \
[get-define TEAISH_PKGINDEX_TCL]]
proj-assert {$tpi ne ""} \
"TEAISH_PKGINDEX_TCL should have been set up by now"
teaish__verbose 1 msg-result "Using pkgIndex from $tpi"
if {0x0f & $::teaish__Config(pkgindex-policy)} {
# Don't leave stale pkgIndex.tcl laying around yet don't delete
# or overwrite a user-managed static pkgIndex.tcl.
file delete -force -- [get-define TEAISH_PKGINDEX_TCL]
proj-dot-ins-append [get-define TEAISH_PKGINDEX_TCL_IN]
} else {
teaish-dist-add [file tail $tpi]
}
}
}}; # $::teaish__Config(pkgindex-policy)
#
# Ensure we clean up TEAISH_PKGINIT_TCL if needed and @-process
# TEAISH_PKGINIT_TCL_IN if needed.
#
if {0x0f & $::teaish__Config(pkginit-policy)} {
file delete -force -- [get-define TEAISH_PKGINIT_TCL]
proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN]
}
if {0x0f & $::teaish__Config(tm-policy)} {
file delete -force -- [get-define TEAISH_TM_TCL]
proj-dot-ins-append [get-define TEAISH_TM_TCL_IN]
}
apply {{} {
# Queue up any remaining dot-in files
set dotIns [list]
foreach d {
TEAISH_TESTER_TCL_IN
TEAISH_TEST_TCL_IN
TEAISH_MAKEFILE_IN
} {
lappend dotIns [get-define $d ""]
}
lappend dotIns $::autosetup(srcdir)/Makefile.in; # must be after TEAISH_MAKEFILE_IN
foreach f $dotIns {
if {"" ne $f} {
proj-dot-ins-append $f
}
}
}}
define TEAISH_DIST_FULL \
[expr {
$::teaish__Config(dist-enabled)
&& $::teaish__Config(dist-full-enabled)
}]
define TEAISH_AUTOSETUP_DIR $::teaish__Config(core-dir)
define TEAISH_ENABLE_DIST $::teaish__Config(dist-enabled)
define TEAISH_ENABLE_INSTALL $::teaish__Config(install-enabled)
define TEAISH_ENABLE_DLL $::teaish__Config(dll-enabled)
define TEAISH_TCL $::teaish__Config(teaish.tcl)
define TEAISH_DIST_FILES [join $::teaish__Config(dist-files)]
define TEAISH_EXT_DIR [join $::teaish__Config(extension-dir)]
define TEAISH_EXT_SRC [join $::teaish__Config(extension-src)]
proj-setup-autoreconfig TEAISH_AUTORECONFIG
foreach f {
TEAISH_CFLAGS
TEAISH_LDFLAGS
} {
# Ensure that any of these lists are flattened
define $f [join [get-define $f]]
}
proj-remap-autoconf-dir-vars
set tdefs [teaish__defines_to_list]
define TEAISH__DEFINES_MAP $tdefs; # injected into _teaish.tester.tcl
#
# NO [define]s after this point!
#
proj-dot-ins-process -validate
proj-if-opt-truthy teaish-dump-defines {
proj-file-write config.defines.txt $tdefs
}
}; # teaish__configure_phase1
#
# Run checks for required binaries.
#
proc teaish__check_common_bins {} {
if {"" eq [proj-bin-define install]} {
proj-warn "Cannot find install binary, so 'make install' will not work."
define BIN_INSTALL false
}
if {"" eq [proj-bin-define zip]} {
proj-warn "Cannot find zip, so 'make dist.zip' will not work."
}
if {"" eq [proj-bin-define tar]} {
proj-warn "Cannot find tar, so 'make dist.tgz' will not work."
}
}
#
# TCL...
#
# teaish__check_tcl performs most of the --with-tcl and --with-tclsh
# handling. Some related bits and pieces are performed before and
# after that function is called.
#
# Important [define]'d vars:
#
# - TCLSH_CMD is the path to the canonical tclsh or "".
#
# - TCL_CONFIG_SH is the path to tclConfig.sh or "".
#
# - TCLLIBDIR is the dir to which the extension library gets
# - installed.
#
proc teaish__check_tcl {} {
define TCLSH_CMD false ; # Significant is that it exits with non-0
define TCLLIBDIR "" ; # Installation dir for TCL extension lib
define TCL_CONFIG_SH ""; # full path to tclConfig.sh
# Clear out all vars which would harvest from tclConfig.sh so that
# the late-config validation of @VARS@ works even if --disable-tcl
# is used.
proj-tclConfig-sh-to-autosetup ""
# TODO: better document the steps this is taking.
set srcdir $::autosetup(srcdir)
msg-result "Checking for a suitable tcl... "
set use_tcl 1
set withSh [opt-val with-tclsh [proj-get-env TCLSH]]
set tclHome [opt-val with-tcl [proj-get-env TCL_HOME]]
if {[string match */lib $tclHome]} {
# TEA compatibility kludge: its --with-tcl wants the lib
# dir containing tclConfig.sh.
#proj-warn "Replacing --with-tcl=$tclHome for TEA compatibility"
regsub {/lib^} $tclHome "" tclHome
msg-result "NOTE: stripped /lib suffix from --with-tcl=$tclHome (a TEA-ism)"
}
if {0} {
# This misinteracts with the $TCL_PREFIX default: it will use the
# autosetup-defined --prefix default
if {"prefix" eq $tclHome} {
set tclHome [get-define prefix]
}
}
teaish-debug "use_tcl ${use_tcl}"
teaish-debug "withSh=${withSh}"
teaish-debug "tclHome=$tclHome"
if {"" eq $withSh && "" eq $tclHome} {
# If neither --with-tclsh nor --with-tcl are provided, try to find
# a workable tclsh.
set withSh [proj-first-bin-of tclsh9.1 tclsh9.0 tclsh8.6 tclsh]
teaish-debug "withSh=${withSh}"
}
set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases
if {"" ne $withSh} {
# --with-tclsh was provided or found above. Validate it and use it
# to trump any value passed via --with-tcl=DIR.
if {![file-isexec $withSh]} {
proj-error "TCL shell $withSh is not executable"
} else {
define TCLSH_CMD $withSh
#msg-result "Using tclsh: $withSh"
}
if {$doConfigLookup &&
[catch {exec $withSh $::autosetup(libdir)/find_tclconfig.tcl} result] == 0} {
set tclHome $result
}
if {"" ne $tclHome && [file isdirectory $tclHome]} {
teaish__verbose 1 msg-result "$withSh recommends the tclConfig.sh from $tclHome"
} else {
proj-warn "$withSh is unable to recommend a tclConfig.sh"
set use_tcl 0
}
}
set cfg ""
set tclSubdirs {tcl9.1 tcl9.0 tcl8.6 tcl8.5 lib}
while {$use_tcl} {
if {"" ne $tclHome} {
# Ensure that we can find tclConfig.sh under ${tclHome}/...
if {$doConfigLookup} {
if {[file readable "${tclHome}/tclConfig.sh"]} {
set cfg "${tclHome}/tclConfig.sh"
} else {
foreach i $tclSubdirs {
if {[file readable "${tclHome}/$i/tclConfig.sh"]} {
set cfg "${tclHome}/$i/tclConfig.sh"
break
}
}
}
}
if {"" eq $cfg} {
proj-error "No tclConfig.sh found under ${tclHome}"
}
} else {
# If we have not yet found a tclConfig.sh file, look in $libdir
# which is set automatically by autosetup or via the --prefix
# command-line option. See
# https://sqlite.org/forum/forumpost/e04e693439a22457
set libdir [get-define libdir]
if {[file readable "${libdir}/tclConfig.sh"]} {
set cfg "${libdir}/tclConfig.sh"
} else {
foreach i $tclSubdirs {
if {[file readable "${libdir}/$i/tclConfig.sh"]} {
set cfg "${libdir}/$i/tclConfig.sh"
break
}
}
}
if {![file readable $cfg]} {
break
}
}
teaish__verbose 1 msg-result "Using tclConfig.sh = $cfg"
break
}; # while {$use_tcl}
define TCL_CONFIG_SH $cfg
# Export a subset of tclConfig.sh to the current TCL-space. If $cfg
# is an empty string, this emits empty-string entries for the
# various options we're interested in.
proj-tclConfig-sh-to-autosetup $cfg
if {"" eq $withSh && $cfg ne ""} {
# We have tclConfig.sh but no tclsh. Attempt to locate a tclsh
# based on info from tclConfig.sh.
set tclExecPrefix [get-define TCL_EXEC_PREFIX]
proj-assert {"" ne $tclExecPrefix}
set tryThese [list \
$tclExecPrefix/bin/tclsh[get-define TCL_VERSION] \
$tclExecPrefix/bin/tclsh ]
foreach trySh $tryThese {
if {[file-isexec $trySh]} {
set withSh $trySh
break
}
}
if {![file-isexec $withSh]} {
proj-warn "Cannot find a usable tclsh (tried: $tryThese)"
}
}
define TCLSH_CMD $withSh
if {$use_tcl} {
# Set up the TCLLIBDIR
set tcllibdir [get-env TCLLIBDIR ""]
set extDirName [teaish-pkginfo-get -libDir]
if {"" eq $tcllibdir} {
# Attempt to extract TCLLIBDIR from TCL's $auto_path
if {"" ne $withSh &&
[catch {exec echo "puts stdout \$auto_path" | "$withSh"} result] == 0} {
foreach i $result {
if {![string match //zip* $i] && [file isdirectory $i]} {
# isdirectory actually passes on //zipfs:/..., but those are
# useless for our purposes
set tcllibdir $i/$extDirName
break
}
}
} else {
proj-error "Cannot determine TCLLIBDIR."
}
}
define TCLLIBDIR $tcllibdir
}; # find TCLLIBDIR
set gotSh [file-isexec $withSh]
set tmdir ""; # first tcl::tm::list entry
if {$gotSh} {
catch {
set tmli [exec echo {puts [tcl::tm::list]} | $withSh]
# Reminder: this list contains many names of dirs which do not
# exist but are legitimate. If we rely only on an is-dir check,
# we can end up not finding any of the many candidates.
set firstDir ""
foreach d $tmli {
if {"" eq $firstDir && ![string match //*:* $d]} {
# First non-VFS entry, e.g. not //zipfs:
set firstDir $d
}
if {[file isdirectory $d]} {
set tmdir $d
break
}
}
if {"" eq $tmdir} {
set tmdir $firstDir
}
}; # find tcl::tm path
}
define TEAISH_TCL_TM_DIR $tmdir
# Finally, let's wrap up...
if {$gotSh} {
teaish__verbose 1 msg-result "Using tclsh = $withSh"
if {$cfg ne ""} {
define HAVE_TCL 1
} else {
proj-warn "Found tclsh but no tclConfig.sh."
}
if {"" eq $tmdir} {
proj-warn "Did not find tcl::tm directory."
}
}
show-notices
# If TCL is not found: if it was explicitly requested then fail
# fatally, else just emit a warning. If we can find the APIs needed
# to generate a working JimTCL then that will suffice for build-time
# TCL purposes (see: proc sqlite-determine-codegen-tcl).
if {!$gotSh} {
proj-error "Did not find tclsh"
} elseif {"" eq $cfg} {
proj-indented-notice -error {
Cannot find a usable tclConfig.sh file. Use --with-tcl=DIR to
specify a directory near which tclConfig.sh can be found, or
--with-tclsh=/path/to/tclsh to allow the tclsh binary to locate
its tclConfig.sh, with the caveat that a symlink to tclsh, or
wrapper script around it, e.g. ~/bin/tclsh ->
$HOME/tcl/9.0/bin/tclsh9.1, may not work because tclsh emits
different library paths for the former than the latter.
}
}
msg-result "Using Tcl [get-define TCL_VERSION] from [get-define TCL_PREFIX]."
teaish__tcl_platform_quirks
}; # teaish__check_tcl
#
# Perform last-minute platform-specific tweaks to account for quirks.
#
proc teaish__tcl_platform_quirks {} {
define TEAISH_POSTINST_PREREQUIRE ""
switch -glob -- [get-define host] {
*-haiku {
# Haiku's default TCLLIBDIR is "all wrong": it points to a
# read-only virtual filesystem mount-point. We bend it back to
# fit under $TCL_PACKAGE_PATH here.
foreach {k d} {
vj TCL_MAJOR_VERSION
vn TCL_MINOR_VERSION
pp TCL_PACKAGE_PATH
ld TCLLIBDIR
} {
set $k [get-define $d]
}
if {[string match /packages/* $ld]} {
set old $ld
set tail [file tail $ld]
if {8 == $vj} {
set ld "${pp}/tcl${vj}.${vn}/${tail}"
} else {
proj-assert {9 == $vj}
set ld "${pp}/${tail}"
}
define TCLLIBDIR $ld
# [load foo.so], without a directory part, does not work via
# automated tests on Haiku (but works when run
# manually). Similarly, the post-install [package require ...]
# test fails, presumably for a similar reason. We work around
# the former in _teaish.tester.tcl.in. We work around the
# latter by amending the post-install check's ::auto_path (in
# Makefile.in). This code MUST NOT contain any single-quotes.
define TEAISH_POSTINST_PREREQUIRE \
[join [list set ::auto_path \
\[ linsert \$::auto_path 0 $ld \] \; \
]]
proj-indented-notice [subst -nocommands -nobackslashes {
Haiku users take note: patching target installation dir to match
Tcl's home because Haiku's is not writable.
Original : $old
Substitute: $ld
}]
}
}
}
}; # teaish__tcl_platform_quirks
#
# Searches $::argv and/or the build dir and/or the source dir for
# teaish.tcl and friends. Fails if it cannot find teaish.tcl or if
# there are other irreconcilable problems. If it returns 0 then it did
# not find an extension but the --help flag was seen, in which case
# that's not an error.
#
# This does not _load_ the extension, it primarily locates the files
# which make up an extension and fills out no small amount of teaish
# state related to that.
#
proc teaish__find_extension {} {
proj-assert {!$::teaish__Config(install-mode)}
teaish__verbose 1 msg-result "Looking for teaish extension..."
# Helper for the foreach loop below.
set checkTeaishTcl {{mustHave fid dir} {
set f [file join $dir $fid]
if {[file readable $f]} {
file-normalize $f
} elseif {$mustHave} {
proj-error "Missing required $dir/$fid"
}
}}
#
# We have to handle some flags manually because the extension must
# be loaded before [options] is run (so that the extension can
# inject its own options).
#
set dirBld $::autosetup(builddir); # dir we're configuring under
set dirSrc $::autosetup(srcdir); # where teaish's configure script lives
set extT ""; # teaish.tcl
set largv {}; # rewritten $::argv
set gotHelpArg 0; # got the --help
foreach arg $::argv {
#puts "*** arg=$arg"
switch -glob -- $arg {
--ted=* -
--t-e-d=* -
--teaish-extension-dir=* {
# Ensure that $extD refers to a directory and contains a
# teaish.tcl.
regexp -- {--[^=]+=(.+)} $arg - extD
set extD [file-normalize $extD]
if {![file isdirectory $extD]} {
proj-error "--teaish-extension-dir value is not a directory: $extD"
}
set extT [apply $checkTeaishTcl 0 teaish.config $extD]
if {"" eq $extT} {
set extT [apply $checkTeaishTcl 1 teaish.tcl $extD]
}
set ::teaish__Config(extension-dir) $extD
}
--help {
incr gotHelpArg
lappend largv $arg
}
default {
lappend largv $arg
}
}
}
set ::argv $largv
set dirExt $::teaish__Config(extension-dir); # dir with the extension
#
# teaish.tcl is a TCL script which implements various
# interfaces described by this framework.
#
# We use the first one we find in the builddir or srcdir.
#
if {"" eq $extT} {
set flist [list]
proj-assert {$dirExt eq ""}
lappend flist $dirBld/teaish.tcl $dirBld/teaish.config $dirSrc/teaish.tcl
if {![proj-first-file-found extT $flist]} {
if {$gotHelpArg} {
# Tell teaish-configure-core that the lack of extension is not
# an error when --help or --teaish-install is used.
return 0;
}
proj-indented-notice -error "
Did not find any of: $flist
If you are attempting an out-of-tree build, use
--teaish-extension-dir=/path/to/extension"
}
}
if {![file readable $extT]} {
proj-error "extension tcl file is not readable: $extT"
}
set ::teaish__Config(teaish.tcl) $extT
set dirExt [file dirname $extT]
set ::teaish__Config(extension-dir) $dirExt
set ::teaish__Config(blddir-is-extdir) [expr {$dirBld eq $dirExt}]
set ::teaish__Config(dist-enabled) $::teaish__Config(blddir-is-extdir); # may change later
set ::teaish__Config(dist-full-enabled) \
[expr {[file-normalize $::autosetup(srcdir)]
eq [file-normalize $::teaish__Config(extension-dir)]}]
set addDist {{file} {
teaish-dist-add [file tail $file]
}}
apply $addDist $extT
teaish__verbose 1 msg-result "Extension dir = [teaish-get -dir]"
teaish__verbose 1 msg-result "Extension config = $extT"
teaish-pkginfo-set -name [file tail [file dirname $extT]]
#
# teaish.make[.in] provides some of the info for the main makefile,
# like which source(s) to build and their build flags.
#
# We use the first one of teaish.make.in or teaish.make we find in
# $dirExt.
#
if {[proj-first-file-found extM \
[list \
$dirExt/teaish.make.in \
$dirExt/teaish.make \
]]} {
if {[string match *.in $extM]} {
define TEAISH_MAKEFILE_IN $extM
define TEAISH_MAKEFILE [file rootname [file tail $extM]]
} else {
define TEAISH_MAKEFILE_IN ""
define TEAISH_MAKEFILE $extM
}
apply $addDist $extM
teaish__verbose 1 msg-result "Extension makefile = $extM"
} else {
define TEAISH_MAKEFILE_IN ""
define TEAISH_MAKEFILE ""
}
# Look for teaish.pkginit.tcl[.in]
set piPolicy 0
if {[proj-first-file-found extI \
[list \
$dirExt/teaish.pkginit.tcl.in \
$dirExt/teaish.pkginit.tcl \
]]} {
if {[string match *.in $extI]} {
# Generate teaish.pkginit.tcl from $extI.
define TEAISH_PKGINIT_TCL_IN $extI
define TEAISH_PKGINIT_TCL [file rootname [file tail $extI]]
set piPolicy 0x01
} else {
# Assume static $extI.
define TEAISH_PKGINIT_TCL_IN ""
define TEAISH_PKGINIT_TCL $extI
set piPolicy 0x10
}
apply $addDist $extI
teaish__verbose 1 msg-result "Extension post-load init = $extI"
define TEAISH_PKGINIT_TCL_TAIL \
[file tail [get-define TEAISH_PKGINIT_TCL]]; # for use in pkgIndex.tcl.in
}
set ::teaish__Config(pkginit-policy) $piPolicy
# Look for pkgIndex.tcl[.in]...
set piPolicy 0
if {[proj-first-file-found extPI $dirExt/pkgIndex.tcl.in]} {
# Generate ./pkgIndex.tcl from $extPI.
define TEAISH_PKGINDEX_TCL_IN $extPI
define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]]
apply $addDist $extPI
set piPolicy 0x01
} elseif {$dirExt ne $dirSrc
&& [proj-first-file-found extPI $dirSrc/pkgIndex.tcl.in]} {
# Generate ./pkgIndex.tcl from $extPI.
define TEAISH_PKGINDEX_TCL_IN $extPI
define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]]
set piPolicy 0x02
} elseif {[proj-first-file-found extPI $dirExt/pkgIndex.tcl]} {
# Assume $extPI's a static file and use it.
define TEAISH_PKGINDEX_TCL_IN ""
define TEAISH_PKGINDEX_TCL $extPI
apply $addDist $extPI
set piPolicy 0x10
}
# Reminder: we have to delay removal of stale TEAISH_PKGINDEX_TCL
# and the proj-dot-ins-append of TEAISH_PKGINDEX_TCL_IN until much
# later in the process.
set ::teaish__Config(pkgindex-policy) $piPolicy
# Look for teaish.test.tcl[.in]
proj-assert {"" ne $dirExt}
set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl]
if {[proj-first-file-found ttt $flist]} {
if {[string match *.in $ttt]} {
# Generate teaish.test.tcl from $ttt
set xt [file rootname [file tail $ttt]]
file delete -force -- $xt; # ensure no stale copy is used
define TEAISH_TEST_TCL $xt
define TEAISH_TEST_TCL_IN $ttt
} else {
define TEAISH_TEST_TCL $ttt
define TEAISH_TEST_TCL_IN ""
}
apply $addDist $ttt
} else {
define TEAISH_TEST_TCL ""
define TEAISH_TEST_TCL_IN ""
}
# Look for _teaish.tester.tcl[.in]
set flist [list $dirExt/_teaish.tester.tcl.in $dirSrc/_teaish.tester.tcl.in]
if {[proj-first-file-found ttt $flist]} {
# Generate teaish.test.tcl from $ttt
set xt [file rootname [file tail $ttt]]
file delete -force -- $xt; # ensure no stale copy is used
define TEAISH_TESTER_TCL $xt
define TEAISH_TESTER_TCL_IN $ttt
if {[lindex $flist 0] eq $ttt} {
apply $addDist $ttt
}
unset ttt xt
} else {
if {[file exists [set ttt [file join $dirSrc _teaish.tester.tcl.in]]]} {
set xt [file rootname [file tail $ttt]]
define TEAISH_TESTER_TCL $xt
define TEAISH_TESTER_TCL_IN $ttt
} else {
define TEAISH_TESTER_TCL ""
define TEAISH_TESTER_TCL_IN ""
}
}
unset flist
# TEAISH_OUT_OF_EXT_TREE = 1 if we're building from a dir other
# than the extension's home dir.
define TEAISH_OUT_OF_EXT_TREE \
[expr {[file-normalize $::autosetup(builddir)] ne \
[file-normalize $::teaish__Config(extension-dir)]}]
return 1
}; # teaish__find_extension
#
# @teaish-cflags-add ?-p|prepend? ?-define? cflags...
#
# Equivalent to [proj-define-amend TEAISH_CFLAGS {*}$args].
#
proc teaish-cflags-add {args} {
proj-define-amend TEAISH_CFLAGS {*}$args
}
#
# @teaish-define-to-cflag ?flags? defineName...|{defineName...}
#
# Uses [proj-define-to-cflag] to expand a list of [define] keys, each
# one a separate argument, to CFLAGS-style -D... form then appends
# that to the current TEAISH_CFLAGS.
#
# It accepts these flags from proj-define-to-cflag: -quote,
# -zero-undef. It does _not_ support its -list flag.
#
# It accepts its non-flag argument(s) in 2 forms: (1) each arg is a
# single [define] key or (2) its one arg is a list of such keys.
#
# TODO: document teaish's well-defined (as it were) defines for this
# purpose. At a bare minimum:
#
# - TEAISH_NAME
# - TEAISH_PKGNAME
# - TEAISH_VERSION
# - TEAISH_LIBDIR_NAME
# - TEAISH_LOAD_PREFIX
# - TEAISH_URL
#
proc teaish-define-to-cflag {args} {
set flags {}
while {[string match -* [lindex $args 0]]} {
set arg [lindex $args 0]
switch -exact -- $arg {
-quote -
-zero-undef {
lappend flags $arg
set args [lassign $args -]
}
default break
}
}
if {1 == [llength $args]} {
set args [list {*}[lindex $args 0]]
}
#puts "***** flags=$flags args=$args"
teaish-cflags-add [proj-define-to-cflag {*}$flags {*}$args]
}
#
# @teaish-cflags-for-tea ?...CFLAGS?
#
# Adds several -DPACKAGE_... CFLAGS using the extension's metadata,
# all as quoted strings. Those symbolic names are commonly used in
# TEA-based builds, and this function is intended to simplify porting
# of such builds. The -D... flags added are:
#
# -DPACKAGE_VERSION=...
# -DPACKAGE_NAME=...
# -DPACKAGE_URL=...
# -DPACKAGE_STRING=...
#
# Any arguments are passed-on as-is to teaish-cflags-add.
#
proc teaish-cflags-for-tea {args} {
set name $::teaish__PkgInfo(-name)
set version $::teaish__PkgInfo(-version)
set pstr [join [list $name $version]]
teaish-cflags-add \
{*}$args \
'-DPACKAGE_VERSION="$version"' \
'-DPACKAGE_NAME="$name"' \
'-DPACKAGE_STRING="$pstr"' \
'-DPACKAGE_URL="[teaish-get -url]"'
}
#
# @teaish-ldflags-add ?-p|-prepend? ?-define? ldflags...
#
# Equivalent to [proj-define-amend TEAISH_LDFLAGS {*}$args].
#
# Typically, -lXYZ flags need to be in "reverse" order, with each -lY
# resolving symbols for -lX's to its left. This order is largely
# historical, and not relevant on all environments, but it is
# technically correct and still relevant on some environments.
#
# See: teaish-ldflags-prepend
#
proc teaish-ldflags-add {args} {
proj-define-amend TEAISH_LDFLAGS {*}$args
}
#
# @teaish-ldflags-prepend args...
#
# Functionally equivalent to [teaish-ldflags-add -p {*}$args]
#
proc teaish-ldflags-prepend {args} {
teaish-ldflags-add -p {*}$args
}
#
# @teaish-src-add ?-dist? ?-dir? src-files...
#
# Appends all non-empty $args to the project's list of C/C++ source or
# (in some cases) object files.
#
# If passed -dist then it also passes each filename, as-is, to
# [teaish-dist-add].
#
# If passed -dir then each src-file has [teaish-get -dir] prepended to
# it before they're added to the list. As often as not, that will be
# the desired behavior so that out-of-tree builds can find the
# sources, but there are cases where it's not desired (e.g. when using
# a source file from outside of the extension's dir, or when adding
# object files (which are typically in the build tree)).
#
proc teaish-src-add {args} {
set i 0
proj-parse-simple-flags args flags {
-dist 0 {expr 1}
-dir 0 {expr 1}
}
if {$flags(-dist)} {
teaish-dist-add {*}$args
}
if {$flags(-dir)} {
set xargs {}
foreach arg $args {
if {"" ne $arg} {
lappend xargs [file join $::teaish__Config(extension-dir) $arg]
}
}
set args $xargs
}
lappend ::teaish__Config(extension-src) {*}$args
}
#
# @teaish-dist-add files-or-dirs...
#
# Adds the given files to the list of files to include with the "make
# dist" rules.
#
# This is a no-op when the current build is not in the extension's
# directory, as dist support is disabled in out-of-tree builds.
#
# It is not legal to call this until [teaish-get -dir] has been
# reliably set (via teaish__find_extension).
#
proc teaish-dist-add {args} {
if {$::teaish__Config(blddir-is-extdir)} {
# ^^^ reminder: we ignore $::teaish__Config(dist-enabled) here
# because the client might want to implement their own dist
# rules.
#proj-warn "**** args=$args"
lappend ::teaish__Config(dist-files) {*}$args
}
}
# teaish-install-add files...
# Equivalent to [proj-define-apend TEAISH_INSTALL_FILES ...].
#proc teaish-install-add {args} {
# proj-define-amend TEAISH_INSTALL_FILES {*}$args
#}
#
# @teash-make-add args...
#
# Appends makefile code to the TEAISH_MAKEFILE_CODE define. Each
# arg may be any of:
#
# -tab: emit a literal tab
# -nl: emit a literal newline
# -nltab: short for -nl -tab
# -bnl: emit a backslash-escaped end-of-line
# -bnltab: short for -eol -tab
#
# Anything else is appended verbatim. This function adds no additional
# spacing between each argument nor between subsequent invocations.
# Generally speaking, a series of calls to this function need to
# be sure to end the series with a newline.
proc teaish-make-add {args} {
set out [get-define TEAISH_MAKEFILE_CODE ""]
foreach a $args {
switch -exact -- $a {
-bnl { set a " \\\n" }
-bnltab { set a " \\\n\t" }
-tab { set a "\t" }
-nl { set a "\n" }
-nltab { set a "\n\t" }
}
append out $a
}
define TEAISH_MAKEFILE_CODE $out
}
# Internal helper to generate a clean/distclean rule name
proc teaish__cleanup_rule {{tgt clean}} {
set x [incr ::teaish__Config(teaish__cleanup_rule-counter-${tgt})]
return ${tgt}-_${x}_
}
# @teaish-make-obj objfile srcfile ?...args?
#
# Uses teaish-make-add to inject makefile rules for $objfile from
# $srcfile, which is assumed to be C code which uses libtcl. Unless
# -recipe is used (see below) it invokes the compiler using the
# makefile-defined $(CC.tcl) which, in the default Makefile.in
# template, includes any flags needed for building against the
# configured Tcl.
#
# This always terminates the resulting code with a newline.
#
# Any arguments after the 2nd may be flags described below or, if no
# -recipe is provided, flags for the compiler call.
#
# -recipe {...}
# Uses the trimmed value of {...} as the recipe, prefixing it with
# a single hard-tab character.
#
# -deps {...}
# List of extra files to list as dependencies of $o. Good luck
# escaping non-trivial cases properly.
#
# -clean
# Generate cleanup rules as well.
proc teaish-make-obj {o src args} {
set consume 0
set clean 0
set flag ""
array set flags {}
set xargs {}
foreach arg $args {
if {$consume} {
set consume 0
set flags($flag) $arg
continue
}
switch -exact -- $arg {
-clean {incr clean}
-recipe -
-deps {
set flag $arg
incr consume
}
default {
lappend xargs $arg
}
}
}
teaish-make-add \
"# [proj-scope 1] -> [proj-scope] $o $src" -nl \
"$o: $src $::teaish__Config(teaish.tcl)"
if {[info exists flags(-deps)]} {
teaish-make-add " " [join $flags(-deps)]
}
teaish-make-add -nltab
if {[info exists flags(-recipe)]} {
teaish-make-add [string trim $flags(-recipe)] -nl
} else {
teaish-make-add [join [list \$(CC.tcl) -c $src {*}$xargs]] -nl
}
if {$clean} {
set rule [teaish__cleanup_rule]
teaish-make-add \
"clean: $rule\n$rule:\n\trm -f \"$o\"\n"
}
}
#
# @teaish-make-clean ?-r? ?-dist? ...files|{...files}
#
# Adds makefile rules for cleaning up the given files via the "make
# clean" or (if -dist is used) "make distclean" makefile rules. The -r
# flag uses "rm -fr" instead of "rm -f", so be careful with that.
#
# The file names are taken literally as arguments to "rm", so they may
# be shell wildcards to be resolved at cleanup-time. To clean up whole
# directories, pass the -r flag. Each name gets quoted in
# double-quotes, so spaces in names should not be a problem (but
# double-quotes in names will be).
#
proc teaish-make-clean {args} {
if {1 == [llength $args]} {
set args [list {*}[lindex $args 0]]
}
set tgt clean
set rmflags "-f"
proj-parse-simple-flags args flags {
-dist 0 {
set tgt distclean
}
-r 0 {
set rmflags "-fr"
}
}
set rule [teaish__cleanup_rule $tgt]
teaish-make-add "# [proj-scope 1] -> [proj-scope]: [join $args]\n"
teaish-make-add "${rule}:\n\trm ${rmflags}"
foreach a $args {
teaish-make-add " \"$a\""
}
teaish-make-add "\n${tgt}: ${rule}\n"
}
#
# @teaish-make-config-header filename
#
# Invokes autosetup's [make-config-header] and passes it $filename and
# a relatively generic list of options for controlling which defined
# symbols get exported. Clients which need more control over the
# exports can copy/paste/customize this.
#
# The exported file is then passed to [proj-touch] because, in
# practice, that's sometimes necessary to avoid build dependency
# issues.
#
proc teaish-make-config-header {filename} {
make-config-header $filename \
-none {HAVE_CFLAG_* LDFLAGS_* SH_* TEAISH__* TEAISH_*_CODE} \
-auto {SIZEOF_* HAVE_* TEAISH_* TCL_*} \
-none *
proj-touch $filename; # help avoid frequent unnecessary auto-reconfig
}
#
# @teaish-feature-cache-set $key value
#
# Sets a feature-check cache entry with the given key.
# See proj-cache-set for the key's semantics. $key should
# normally be 0.
#
proc teaish-feature-cache-set {key val} {
proj-cache-set -key $key -level 1 $val
}
#
# @teaish-feature-cache-check key tgtVarName
#
# Checks for a feature-check cache entry with the given key.
# See proj-cache-set for the key's semantics.
#
# $key should also almost always be 0 but, due to a tclsh
# incompatibility in 1 OS, it cannot have a default value unless it's
# the second argument (but it should be the first one).
#
# If the feature-check cache has a matching entry then this function
# assigns its value to tgtVar and returns 1, else it assigns tgtVar to
# "" and returns 0.
#
# See proj-cache-check for $key's semantics.
#
proc teaish-feature-cache-check {key tgtVar} {
upvar $tgtVar tgt
proj-cache-check -key $key -level 1 tgt
}
#
# @teaish-check-cached@ ?flags? msg script...
#
# A proxy for feature-test impls which handles caching of a feature
# flag check on per-function basis, using the calling scope's name as
# the cache key.
#
# It emits [msg-checking $msg]. If $msg is empty then it defaults to
# the name of the caller's scope. The -nomsg flag suppresses the
# message for non-cache-hit checks. At the end, it will [msg-result
# "ok"] [msg-result "no"] unless -nostatus is used, in which case the
# caller is responsible for emitting at least a newline when it's
# done. The -msg-0 and -msg-1 flags can be used to change the ok/no
# text.
#
# This function checks for a cache hit before running $script and
# caching the result. If no hit is found then $script is run in the
# calling scope and its result value is stored in the cache. This
# routine will intercept a 'return' from $script.
#
# $script may be a command and its arguments, as opposed to a single
# script block.
#
# Flags:
#
# -nostatus = do not emit "ok" or "no" at the end. This presumes
# that either $script will emit at least one newline before
# returning or the caller will account for it. Because of how this
# function is typically used, -nostatus is not honored when the
# response includes a cached result.
#
# -quiet = disable output from Autosetup's msg-checking and
# msg-result for the duration of the $script check. Note that when
# -quiet is in effect, Autosetup's user-notice can be used to queue
# up output to appear after the check is done. Also note that
# -quiet has no effect on _this_ function, only the $script part.
#
# -nomsg = do not emit $msg for initial check. Like -nostatus, this
# flag is not honored when the response includes a cached result
# because it would otherwise produce no output (which is confusing
# in this context). This is useful when a check runs several other
# verbose checks and they emit all the necessary info.
#
# -msg-0 and -msg-1 MSG = strings to show when the check has failed
# resp. passed. Defaults are "no" and "ok". The 0 and 1 refer to the
# result value from teaish-feature-cache-check.
#
# -key cachekey = set the cache context key. Only needs to be
# explicit when using this function multiple times from a single
# scope. See proj-cache-check and friends for details on the key
# name. Its default is the name of the scope which calls this
# function.
#
proc teaish-check-cached {args} {
proj-parse-simple-flags args flags {
-nostatus 0 {expr 1}
-quiet 0 {expr 1}
-key => 1
-nomsg 0 {expr 1}
-msg-0 => no
-msg-1 => ok
}
set args [lassign $args msg]
set script [join $args]
if {"" eq $msg} {
set msg [proj-scope 1]
}
if {[teaish-feature-cache-check $flags(-key) check]} {
#if {0 == $flags(-nomsg)} {
msg-checking "${msg} ... (cached) "
#}
#if {!$flags(-nostatus)} {
msg-result $flags(-msg-[expr {0 != ${check}}])
#}
return $check
} else {
if {0 == $flags(-nomsg)} {
msg-checking "${msg} ... "
}
if {$flags(-quiet)} {
incr ::autosetup(msg-quiet)
}
set code [catch {uplevel 1 $script} rc xopt]
if {$flags(-quiet)} {
incr ::autosetup(msg-quiet) -1
}
#puts "***** cached-check got code=$code rc=$rc"
if {$code in {0 2}} {
teaish-feature-cache-set 1 $rc
if {!$flags(-nostatus)} {
msg-result $flags(-msg-[expr {0 != ${rc}}])
} else {
#show-notices; # causes a phantom newline because we're in a
#msg-checking scope, so...
if {[info exists ::autosetup(notices)]} {
show-notices
}
}
} else {
#puts "**** code=$code rc=$rc xopt=$xopt"
teaish-feature-cache-set 1 0
}
#puts "**** code=$code rc=$rc"
return {*}$xopt $rc
}
}
#
# Internal helper for teaish__defs_format_: returns a JSON-ish quoted
# form of the given string-type values.
#
# If $asList is true then the return value is in {$value} form. If
# $asList is false it only performs the most basic of escaping and
# the input must not contain any control characters.
#
proc teaish__quote_str {asList value} {
if {$asList} {
return "{${value}}"
}
return \"[string map [list \\ \\\\ \" \\\"] $value]\"
}
#
# Internal helper for teaish__defines_to_list. Expects to be passed
# a name and the variadic $args which are passed to
# teaish__defines_to_list.. If it finds a pattern match for the
# given $name in the various $args, it returns the type flag for that
# $name, e.g. "-str" or "-bare", else returns an empty string.
#
proc teaish__defs_type {name spec} {
foreach {type patterns} $spec {
foreach pattern $patterns {
if {[string match $pattern $name]} {
return $type
}
}
}
return ""
}
#
# An internal impl detail. Requires a data type specifier, as used by
# Autosetup's [make-config-header], and a value. Returns the formatted
# value or the value $::teaish__Config(defs-skip) if the caller should
# skip emitting that value.
#
# In addition to -str, -auto, etc., as defined by make-config-header,
# it supports:
#
# -list {...} will cause non-integer values to be quoted in {...}
# instead of quotes.
#
# -autolist {...} works like -auto {...} except that it falls back to
# -list {...} type instead of -str {...} style for non-integers.
#
# -jsarray {...} emits the output in something which, for
# conservative inputs, will be a valid JSON array. It can only
# handle relatively simple values with no control characters in
# them.
#
set teaish__Config(defs-skip) "-teaish__defs_format sentinel"
proc teaish__defs_format {type value} {
switch -exact -- $type {
-bare {
# Just output the value unchanged
}
-none {
set value $::teaish__Config(defs-skip)
}
-str {
set value [teaish__quote_str 0 $value]
}
-auto {
# Automatically determine the type
if {![string is integer -strict $value]} {
set value [teaish__quote_str 0 $value]
}
}
-autolist {
if {![string is integer -strict $value]} {
set value [teaish__quote_str 1 $value]
}
}
-list {
set value [teaish__quote_str 1 $value]
}
-jsarray {
set ar {}
foreach v $value {
if {![string is integer -strict $v]} {
set v [teaish__quote_str 0 $v]
}
if {$::teaish__Config(defs-skip) ne $v} {
lappend ar $v
}
}
set value [concat \[ [join $ar {, }] \]]
}
"" {
# (Much later:) Why do we do this?
set value $::teaish__Config(defs-skip)
}
default {
proj-error \
"Unknown [proj-scope] -type ($type) called from" \
[proj-scope 1]
}
}
return $value
}
#
# Returns Tcl code in the form of code which evaluates to a list of
# configure-time DEFINEs in the form {key val key2 val...}. It may
# misbehave for values which are not numeric or simple strings. Some
# defines are specifically filtered out of the result, either because
# their irrelevant to teaish or because they may be arbitrarily large
# (e.g. makefile content).
#
# The $args are explained in the docs for internal-use-only
# [teaish__defs_format]. The default mode is -autolist.
#
proc teaish__defines_to_list {args} {
set lines {}
lappend lines "\{"
set skipper $::teaish__Config(defs-skip)
set args [list \
-none {
TEAISH__*
TEAISH_*_CODE
AM_* AS_*
} \
{*}$args \
-autolist *]
foreach d [lsort [dict keys [all-defines]]] {
set type [teaish__defs_type $d $args]
set value [teaish__defs_format $type [get-define $d]]
if {$skipper ne $value} {
lappend lines "$d $value"
}
}
lappend lines "\}"
tailcall join $lines "\n"
}
#
# teaish__pragma ...flags
#
# Offers a way to tweak how teaish's core behaves in some cases, in
# particular those which require changing how the core looks for an
# extension and its files.
#
# Accepts the following flags. Those marked with [L] are safe to use
# during initial loading of tclish.tcl (recall that most teaish APIs
# cannot be used until [teaish-configure] is called).
#
# static-pkgIndex.tcl [L]: Tells teaish that ./pkgIndex.tcl is not
# a generated file, so it will not try to overwrite or delete
# it. Errors out if it does not find pkgIndex.tcl in the
# extension's dir.
#
# no-dist [L]: tells teaish to elide the 'make dist' recipe
# from the generated Makefile.
#
# no-dll [L]: tells teaish to elide the DLL-building recipe
# from the generated Makefile.
#
# no-vsatisfies-error [L]: tells teaish that failure to match the
# -vsatisfies value should simply "return" instead of "error".
#
# no-tester [L]: disables automatic generation of teaish.test.tcl
# even if a copy of _teaish.tester.tcl.in is found.
#
# no-full-dist [L]: changes the "make dist" rules to never include
# a copy of teaish itself. By default it will include itself only
# if the extension lives in the same directory as teaish.
#
# full-dist [L]: changes the "make dist" rules to always include
# a copy of teaish itself.
#
# Emits a warning message for unknown arguments.
#
proc teaish__pragma {args} {
foreach arg $args {
switch -exact -- $arg {
static-pkgIndex.tcl {
if {$::teaish__Config(tm-policy)} {
proj-fatal -up "Cannot use pragma $arg together with -tm.tcl or -tm.tcl.in."
}
set tpi [file join $::teaish__Config(extension-dir) pkgIndex.tcl]
if {[file exists $tpi]} {
define TEAISH_PKGINDEX_TCL_IN ""
define TEAISH_PKGINDEX_TCL $tpi
set ::teaish__Config(pkgindex-policy) 0x20
} else {
proj-error "pragma $arg: found no package-local pkgIndex.tcl\[.in]"
}
}
no-dist {
set ::teaish__Config(dist-enabled) 0
}
no-install {
set ::teaish__Config(install-enabled) 0
}
full-dist {
set ::teaish__Config(dist-full-enabled) 1
}
no-full-dist {
set ::teaish__Config(dist-full-enabled) 0
}
no-dll {
set ::teaish__Config(dll-enabled) 0
}
no-vsatisfies-error {
set ::teaish__Config(vsatisfies-error) 0
}
no-tester {
define TEAISH_TESTER_TCL_IN ""
define TEAISH_TESTER_TCL ""
}
default {
proj-error "Unknown flag: $arg"
}
}
}
}
#
# @teaish-pkginfo-set ...flags
#
# The way to set up the initial package state. Used like:
#
# teaish-pkginfo-set -name foo -version 0.1.2
#
# Or:
#
# teaish-pkginfo-set ?-vars|-subst? {-name foo -version 0.1.2}
#
# The latter may be easier to write for a multi-line invocation.
#
# For the second call form, passing the -vars flag tells it to perform
# a [subst] of (only) variables in the {...} part from the calling
# scope. The -subst flag will cause it to [subst] the {...} with
# command substitution as well (but no backslash substitution). When
# using -subst for string concatenation, e.g. with -libDir
# foo[get-version-number], be sure to wrap the value in braces:
# -libDir {foo[get-version-number]}.
#
# Each pkginfo flag corresponds to one piece of extension package
# info. Teaish provides usable default values for all of these flags,
# but at least the -name and -version should be set by clients.
# e.g. the default -name is the directory name the extension lives in,
# which may change (e.g. when building it from a "make dist" bundle).
#
# The flags:
#
# -name theName: The extension's name. It defaults to the name of the
# directory containing the extension. (In TEA this would be the
# PACKAGE_NAME, not to be confused with...)
#
# -name.pkg pkg-provide-name: The extension's name for purposes of
# Tcl_PkgProvide(), [package require], and friends. It defaults to
# the `-name`, and is normally the same, but some projects (like
# SQLite) have a different name here than they do in their
# historical TEA PACKAGE_NAME.
#
# -version version: The extension's package version. Defaults to
# 0.0.0.
#
# -libDir dirName: The base name of the directory into which this
# extension should be installed. It defaults to a concatenation of
# `-name.pkg` and `-version`.
#
# -loadPrefix prefix: For use as the second argument passed to
# Tcl's `load` command in the package-loading process. It defaults
# to title-cased `-name.pkg` because Tcl's `load` plugin system
# expects it in that form.
#
# -options {...}: If provided, it must be a list compatible with
# Autosetup's `options-add` function. These can also be set up via
# `teaish-options`.
#
# -vsatisfies {{...} ...}: Expects a list-of-lists of conditions
# for Tcl's `package vsatisfies` command: each list entry is a
# sub-list of `{PkgName Condition...}`. Teaish inserts those
# checks via its default pkgIndex.tcl.in and _teaish.tester.tcl.in
# templates to verify that the system's package dependencies meet
# these requirements. The default value is `{{Tcl 8.5-}}` (recall
# that it's a list-of-lists), as 8.5 is the minimum Tcl version
# teaish will run on, but some extensions may require newer
# versions or dependencies on other packages. As a special case,
# if `-vsatisfies` is given a single token, e.g. `8.6-`, then it
# is transformed into `{Tcl $thatToken}`, i.e. it checks the Tcl
# version which the package is being run with. If given multiple
# lists, each `package provides` check is run in the given
# order. Failure to meet a `vsatisfies` condition triggers an
# error.
#
# -url {...}: an optional URL for the extension.
#
# -pragmas {...} A list of infrequently-needed lower-level
# directives which can influence teaish, including:
#
# static-pkgIndex.tcl: tells teaish that the client manages their
# own pkgIndex.tcl, so that teaish won't try to overwrite it
# using a template.
#
# no-dist: tells teaish to elide the "make dist" recipe from the
# makefile so that the client can implement it.
#
# no-dll: tells teaish to elide the makefile rules which build
# the DLL, as well as any templated test script and pkgIndex.tcl
# references to them. The intent here is to (A) support
# client-defined build rules for the DLL and (B) eventually
# support script-only extensions.
#
# Unsupported flags or pragmas will trigger an error.
#
# Potential pothole: setting certain state, e.g. -version, after the
# initial call requires recalculating of some [define]s. Any such
# changes should be made as early as possible in teaish-configure so
# that any later use of those [define]s gets recorded properly (not
# with the old value). This is particularly relevant when it is not
# possible to determine the -version or -name until teaish-configure
# has been called, and it's updated dynamically from
# teaish-configure. Notably:
#
# - If -version or -name are updated, -libDir will almost certainly
# need to be explicitly set along with them.
#
# - If -name is updated, -loadPrefix probably needs to be as well.
#
proc teaish-pkginfo-set {args} {
set doVars 0
set doCommands 0
set xargs $args
set recalc {}
foreach arg $args {
switch -exact -- $arg {
-vars {
incr doVars
set xargs [lassign $xargs -]
}
-subst {
incr doVars
incr doCommands
set xargs [lassign $xargs -]
}
default {
break
}
}
}
set args $xargs
unset xargs
if {1 == [llength $args] && [llength [lindex $args 0]] > 1} {
# Transform a single {...} arg into the canonical call form
set a [list {*}[lindex $args 0]]
if {$doVars || $doCommands} {
set sflags -nobackslashes
if {!$doCommands} {
lappend sflags -nocommands
}
set a [uplevel 1 [list subst {*}$sflags $a]]
}
set args $a
}
set sentinel "<nope>"
set flagDefs [list]
foreach {f d} $::teaish__Config(pkginfo-f2d) {
lappend flagDefs $f => $sentinel
}
proj-parse-simple-flags args flags $flagDefs
if {[llength $args]} {
proj-error -up "Too many (or unknown) arguments to [proj-scope]: $args"
}
foreach {f d} $::teaish__Config(pkginfo-f2d) {
if {$sentinel eq [set v $flags($f)]} continue
switch -exact -- $f {
-options {
proj-assert {"" eq $d}
options-add $v
}
-pragmas {
teaish__pragma {*}$v
}
-vsatisfies {
if {1 == [llength $v] && 1 == [llength [lindex $v 0]]} {
# Transform X to {Tcl $X}
set v [list [join [list Tcl $v]]]
}
define $d $v
}
-pkgInit.tcl -
-pkgInit.tcl.in {
if {0x22 & $::teaish__Config(pkginit-policy)} {
proj-fatal "Cannot use -pkgInit.tcl(.in) more than once."
}
set x [file join $::teaish__Config(extension-dir) $v]
set tTail [file tail $v]
if {"-pkgInit.tcl.in" eq $f} {
# Generate pkginit file X from X.in
set pI 0x02
set tIn $x
set tOut [file rootname $tTail]
set other -pkgInit.tcl
} else {
# Static pkginit file X
set pI 0x20
set tIn ""
set tOut $x
set other -pkgInit.tcl.in
}
set ::teaish__Config(pkginit-policy) $pI
set ::teaish__PkgInfo($other) {}
define TEAISH_PKGINIT_TCL_IN $tIn
define TEAISH_PKGINIT_TCL $tOut
define TEAISH_PKGINIT_TCL_TAIL $tTail
teaish-dist-add $v
set v $x
}
-tm.tcl -
-tm.tcl.in {
if {0x30 & $::teaish__Config(pkgindex-policy)} {
proj-fatal "Cannot use $f together with a pkgIndex.tcl."
} elseif {$::teaish__Config(tm-policy)} {
proj-fatal "Cannot use -tm.tcl(.in) more than once."
}
set x [file join $::teaish__Config(extension-dir) $v]
if {"-tm.tcl.in" eq $f} {
# Generate tm file X from X.in
set pT 0x02
set pI 0x100
set tIn $x
set tOut [file rootname [file tail $v]]
set other -tm.tcl
} else {
# Static tm file X
set pT 0x20
set pI 0x200
set tIn ""
set tOut $x
set other -tm.tcl.in
}
set ::teaish__Config(pkgindex-policy) $pI
set ::teaish__Config(tm-policy) $pT
set ::teaish__PkgInfo($other) {}
define TEAISH_TM_TCL_IN $tIn
define TEAISH_TM_TCL $tOut
define TEAISH_PKGINDEX_TCL ""
define TEAISH_PKGINDEX_TCL_IN ""
define TEAISH_PKGINDEX_TCL_TAIL ""
teaish-dist-add $v
teaish__pragma no-dll
set v $x
}
default {
proj-assert {"" ne $d}
define $d $v
}
}
set ::teaish__PkgInfo($f) $v
if {$f in {-name -version -libDir -loadPrefix}} {
lappend recalc $f
}
}
if {"" ne $recalc} {
teaish__define_pkginfo_derived $recalc
}
}
#
# @teaish-pkginfo-get ?arg?
#
# If passed no arguments, it returns the extension config info in the
# same form accepted by teaish-pkginfo-set.
#
# If passed one -flagname arg then it returns the value of that config
# option.
#
# Else it treats arg as the name of caller-scoped variable to
# which this function assigns an array containing the configuration
# state of this extension, in the same structure accepted by
# teaish-pkginfo-set. In this case it returns an empty string.
#
proc teaish-pkginfo-get {args} {
set cases {}
set argc [llength $args]
set rv {}
switch -exact $argc {
0 {
# Return a list of (-flag value) pairs
lappend cases default {{
if {[info exists ::teaish__PkgInfo($flag)]} {
lappend rv $flag $::teaish__PkgInfo($flag)
} else {
lappend rv $flag [get-define $defName]
}
}}
}
1 {
set arg $args
if {[string match -* $arg]} {
# Return the corresponding -flag's value
lappend cases $arg {{
if {[info exists ::teaish__PkgInfo($flag)]} {
return $::teaish__PkgInfo($flag)
} else {
return [get-define $defName]
}
}}
} else {
# Populate target with an array of (-flag value).
upvar $arg tgt
array set tgt {}
lappend cases default {{
if {[info exists ::teaish__PkgInfo($flag)]} {
set tgt($flag) $::teaish__PkgInfo($flag)
} else {
set tgt($flag) [get-define $defName]
}
}}
}
}
default {
proj-error "invalid arg count from [proj-scope 1]"
}
}
foreach {flag defName} $::teaish__Config(pkginfo-f2d) {
switch -exact -- $flag [join $cases]
}
if {0 == $argc} { return $rv }
}
# (Re)set some defines based on pkginfo state. $flags is the list of
# pkginfo -flags which triggered this, or "*" for the initial call.
proc teaish__define_pkginfo_derived {flags} {
set all [expr {{*} in $flags}]
if {$all || "-version" in $flags || "-name" in $flags} {
set name $::teaish__PkgInfo(-name) ; # _not_ -name.pkg
if {[info exists ::teaish__PkgInfo(-version)]} {
set pkgver $::teaish__PkgInfo(-version)
set libname "lib"
if {[string match *-cygwin [get-define host]]} {
set libname cyg
}
define TEAISH_DLL8_BASENAME $libname$name$pkgver
define TEAISH_DLL9_BASENAME ${libname}tcl9$name$pkgver
set ext [get-define TARGET_DLLEXT]
define TEAISH_DLL8 [get-define TEAISH_DLL8_BASENAME]$ext
define TEAISH_DLL9 [get-define TEAISH_DLL9_BASENAME]$ext
}
}
if {$all || "-libDir" in $flags} {
if {[info exists ::teaish__PkgInfo(-libDir)]} {
define TCLLIBDIR \
[file dirname [get-define TCLLIBDIR]]/$::teaish__PkgInfo(-libDir)
}
}
}
#
# @teaish-checks-queue -pre|-post args...
#
# Queues one or more arbitrary "feature test" functions to be run when
# teaish-checks-run is called. $flag must be one of -pre or -post to
# specify whether the tests should be run before or after
# teaish-configure is run. Each additional arg is the name of a
# feature-test proc.
#
proc teaish-checks-queue {flag args} {
if {$flag ni {-pre -post}} {
proj-error "illegal flag: $flag"
}
lappend ::teaish__Config(queued-checks${flag}) {*}$args
}
#
# @teaish-checks-run -pre|-post
#
# Runs all feature checks queued using teaish-checks-queue
# then cleares the queue.
#
proc teaish-checks-run {flag} {
if {$flag ni {-pre -post}} {
proj-error "illegal flag: $flag"
}
#puts "*** running $flag: $::teaish__Config(queued-checks${flag})"
set foo 0
foreach f $::teaish__Config(queued-checks${flag}) {
if {![teaish-feature-cache-check $f foo]} {
set v [$f]
teaish-feature-cache-set $f $v
}
}
set ::teaish__Config(queued-checks${flag}) {}
}
#
# A general-purpose getter for various teaish state. Requires one
# flag, which determines its result value. Flags marked with [L] below
# are safe for using at load-time, before teaish-pkginfo-set is called
#
# -dir [L]: returns the extension's directory, which may differ from
# the teaish core dir or the build dir.
#
# -teaish-home [L]: the "home" dir of teaish itself, which may
# differ from the extension dir or build dir.
#
# -build-dir [L]: the build directory (typically the current working
# -dir).
#
# Any of the teaish-pkginfo-get/get flags: returns the same as
# teaish-pkginfo-get. Not safe for use until teaish-pkginfo-set has
# been called.
#
# Triggers an error if passed an unknown flag.
#
proc teaish-get {flag} {
#-teaish.tcl {return $::teaish__Config(teaish.tcl)}
switch -exact -- $flag {
-dir {
return $::teaish__Config(extension-dir)
}
-teaish-home {
return $::autosetup(srcdir)
}
-build-dir {
return $::autosetup(builddir)
}
default {
if {[info exists ::teaish__PkgInfo($flag)]} {
return $::teaish__PkgInfo($flag)
}
}
}
proj-error "Unhandled flag: $flag"
}
#
# Handles --teaish-create-extension=TARGET-DIR
#
proc teaish__create_extension {dir} {
set force [opt-bool teaish-force]
if {"" eq $dir} {
proj-error "--teaish-create-extension=X requires a directory name."
}
file mkdir $dir/generic
set cwd [pwd]
#set dir [file-normalize [file join $cwd $dir]]
teaish__verbose 1 msg-result "Created dir $dir"
cd $dir
if {!$force} {
# Ensure that we don't blindly overwrite anything
foreach f {
generic/teaish.c
teaish.tcl
teaish.make.in
teaish.test.tcl
} {
if {[file exists $f]} {
error "Cowardly refusing to overwrite $dir/$f. Use --teaish-force to overwrite."
}
}
}
set name [file tail $dir]
set pkgName $name
set version 0.0.1
set loadPrefix [string totitle $pkgName]
set content {teaish-pkginfo-set }
#puts "0 content=$content"
if {[opt-str teaish-extension-pkginfo epi]} {
set epi [string trim $epi]
if {[string match "*\n*" $epi]} {
set epi "{$epi}"
} elseif {![string match "{*}" $epi]} {
append content "\{" $epi "\}"
} else {
append content $epi
}
#puts "2 content=$content\nepi=$epi"
} else {
append content [subst -nocommands -nobackslashes {{
-name ${name}
-name.pkg ${pkgName}
-name.dist ${pkgName}
-version ${version}
-loadPrefix $loadPrefix
-libDir ${name}${version}
-vsatisfies {{Tcl 8.5-}}
-url {}
-options {}
-pragmas {full-dist}
}}]
#puts "3 content=$content"
}
#puts "1 content=$content"
append content "\n" {
#proc teaish-options {} {
# Return a list and/or use \[options-add\] to add new
# configure flags. This is called before teaish's
# bootstrapping is finished, so only teaish-*
# APIs which are explicitly noted as being safe
# early on may be used here. Any autosetup-related
# APIs may be used here.
#
# Return an empty string if there are no options to
# add or if they are added using \[options-add\].
#
# If there are no options to add, this proc need
# not be defined.
#}
# Called by teaish once bootstrapping is complete.
# This function is responsible for the client-specific
# parts of the configuration process.
proc teaish-configure {} {
teaish-src-add -dir -dist generic/teaish.c
teaish-define-to-cflag -quote TEAISH_PKGNAME TEAISH_VERSION
# TODO: your code goes here..
}
}; # $content
proj-file-write teaish.tcl $content
teaish__verbose 1 msg-result "Created teaish.tcl"
set content "# Teaish test script.
# When this tcl script is invoked via 'make test' it will have loaded
# the package, run any teaish.pkginit.tcl code, and loaded
# autosetup/teaish/tester.tcl.
"
proj-file-write teaish.test.tcl $content
teaish__verbose 1 msg-result "Created teaish.test.tcl"
set content [subst -nocommands -nobackslashes {
#include <tcl.h>
static int
${loadPrefix}_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]){
Tcl_SetObjResult(interp, Tcl_NewStringObj("this is the ${name} extension", -1));
return TCL_OK;
}
extern int DLLEXPORT ${loadPrefix}_Init(Tcl_Interp *interp){
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
if (Tcl_PkgProvide(interp, TEAISH_PKGNAME, TEAISH_VERSION) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, TEAISH_PKGNAME, ${loadPrefix}_Cmd, NULL, NULL);
return TCL_OK;
}
}]
proj-file-write generic/teaish.c $content
teaish__verbose 1 msg-result "Created generic/teaish.c"
set content "# teaish makefile for the ${name} extension
# tx.src = \$(tx.dir)/generic/teaish.c
# tx.LDFLAGS =
# tx.CFLAGS =
"
proj-file-write teaish.make.in $content
teaish__verbose 1 msg-result "Created teaish.make.in"
msg-result "Created new extension \[$dir\]."
cd $cwd
set ::teaish__Config(install-ext-dir) $dir
}
#
# Internal helper for teaish__install
#
proc teaish__install_file {f destDir force} {
set dest $destDir/[file tail $f]
if {[file isdirectory $f]} {
file mkdir $dest
} elseif {!$force && [file exists $dest]} {
array set st1 [file stat $f]
array set st2 [file stat $dest]
if {($st1(mtime) == $st2(mtime))
&& ($st1(size) == $st2(size))} {
if {[file tail $f] in {
pkgIndex.tcl.in
_teaish.tester.tcl.in
}} {
# Assume they're the same. In the scope of the "make dist"
# rules, this happens legitimately when an extension with a
# copy of teaish installed in the same dir assumes that the
# pkgIndex.tcl.in and _teaish.tester.tcl.in belong to the
# extension, whereas teaish believes they belong to teaish.
# So we end up with dupes of those.
return
}
}
proj-error -up "Cowardly refusing to overwrite \[$dest\]." \
"Use --teaish-force to enable overwriting."
} else {
# file copy -force $f $destDir; # loses +x bit
#
# JimTcl doesn't have [file attribute], so we can't use that here
# (in the context of an autosetup configure script).
exec cp -p $f $dest
}
}
#
# Installs a copy of teaish, with autosetup, to $dDest, which defaults
# to the --teaish-install=X or --teash-create-extension=X dir. Won't
# overwrite files unless --teaish-force is used.
#
proc teaish__install {{dDest ""}} {
if {$dDest in {auto ""}} {
set dDest [opt-val teaish-install]
if {$dDest in {auto ""}} {
if {[info exists ::teaish__Config(install-ext-dir)]} {
set dDest $::teaish__Config(install-ext-dir)
}
}
}
set force [opt-bool teaish-force]
if {$dDest in {auto ""}} {
proj-error "Cannot determine installation directory."
} elseif {!$force && [file exists $dDest/auto.def]} {
proj-error \
"Target dir looks like it already contains teaish and/or autosetup: $dDest" \
"\nUse --teaish-force to overwrite it."
}
set dSrc $::autosetup(srcdir)
set dAS $::autosetup(libdir)
set dAST $::teaish__Config(core-dir)
set dASTF $dAST/feature
teaish__verbose 1 msg-result "Installing teaish to \[$dDest\]..."
if {$::teaish__Config(verbose)>1} {
msg-result "dSrc = $dSrc"
msg-result "dAS = $dAS"
msg-result "dAST = $dAST"
msg-result "dASTF = $dASTF"
msg-result "dDest = $dDest"
}
# Dest subdirs...
set ddAS $dDest/autosetup
set ddAST $ddAS/teaish
set ddASTF $ddAST/feature
foreach {srcDir destDir} [list \
$dAS $ddAS \
$dAST $ddAST \
$dASTF $ddASTF \
] {
teaish__verbose 1 msg-result "Copying files to $destDir..."
file mkdir $destDir
foreach f [glob -directory $srcDir *] {
if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} {
# Editor-generated backups and emacs lock files
continue
}
teaish__verbose 2 msg-result "\t$f"
teaish__install_file $f $destDir $force
}
}
teaish__verbose 1 msg-result "Copying files to $dDest..."
foreach f {
auto.def configure Makefile.in pkgIndex.tcl.in
_teaish.tester.tcl.in
} {
teaish__verbose 2 msg-result "\t$f"
teaish__install_file $dSrc/$f $dDest $force
}
set ::teaish__Config(install-self-dir) $dDest
msg-result "Teaish $::teaish__Config(version) installed in \[$dDest\]."
}
|