xbintsc 0.5.45 → 0.5.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -12,16 +12,21 @@
12
12
  * Steps:
13
13
  * 1. fetch the pinned SDL3 source into `vendor/SDL` (gitignored),
14
14
  * 2. build `libSDL3.a` with CMake,
15
- * 2b. fetch + build static FreeType and HarfBuzz for text,
16
- * 2c. fetch + build static glslang (GLSL ES -> SPIR-V) and SPIRV-Cross
15
+ * 2a. fetch + build static HarfBuzz and FreeType for the optional `shaping`
16
+ * feature; the default text path uses the bundled stb_truetype,
17
+ * 2b. fetch + build static glslang (GLSL ES -> SPIR-V) and SPIRV-Cross
17
18
  * (SPIR-V -> Metal Shading Language),
18
19
  * 3. compile the engine translation units,
19
20
  * 4. merge everything into `gui.a` (libtool on macOS, `ar -M` elsewhere;
20
21
  * `gui.lib` on Windows, built from MSVC-compatible COFF objects).
21
22
  *
22
- * Media decoding is *not* part of this archive: it lives in the separate
23
- * `media` extension (`npm run media`, `runtime/ext_media/`), so a program that
24
- * only renders HTML/CSS never links FFmpeg.
23
+ * Media decoding is *not* part of this archive: it is an optional link feature
24
+ * of the `gui` extension (`npm run media`, `runtime/ext_media/`), so a program
25
+ * that only renders HTML/CSS never links FFmpeg.
26
+ *
27
+ * Every library and every engine object is compiled with function/data sections;
28
+ * SDL is configured without the subsystems the engine never calls. The linker's
29
+ * dead-section pass can then drop whatever a particular program does not reach.
25
30
  *
26
31
  * Environment:
27
32
  * xbintsc_CXX C++ compiler override (default: clang++ next to the resolved clang)
@@ -46,14 +51,21 @@ import { fileURLToPath } from "node:url";
46
51
  import { platformSlug } from "../src/driver/paths.js";
47
52
  import { isMsvcTarget, probeClang, realRunner } from "../src/driver/toolchain.js";
48
53
  import { resolveToolchain } from "../src/driver/toolchain-provider.js";
54
+ import {
55
+ cmakeToolchainArgs,
56
+ HB_LEAN_DEFINES,
57
+ LEAN_SDL_OPTIONS,
58
+ SECTION_CMAKE_FLAGS,
59
+ SECTION_FLAGS,
60
+ } from "./gui-build-options.js";
49
61
 
50
62
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
51
63
  const runtimeDir = join(root, "runtime");
52
64
  const engineDir = join(runtimeDir, "ext_gui");
53
65
  const vendorDir = join(root, "vendor");
54
66
  const sdlSrc = join(vendorDir, "SDL");
55
- /* SDL's build tree is a *sibling* of its source, like FreeType's and HarfBuzz's,
56
- * never inside it: the CI cache keyed on a vendored library ships the whole
67
+ /* SDL's build tree is a *sibling* of its source, like glslang's, never inside
68
+ * it: the CI cache keyed on a vendored library ships the whole
57
69
  * `vendor/SDL` tree, and a CMake build tree is not relocatable across machines
58
70
  * or platforms. `CMakeCache.txt` records the host's compiler and probes, and
59
71
  * SDL's video options are `option()`s — the first configure to run wins, so a
@@ -61,22 +73,25 @@ const sdlSrc = join(vendorDir, "SDL");
61
73
  * default is OFF) survives into a Linux configure and SDL then aborts with
62
74
  * "SDL could not find X11 or Wayland development libraries". Keeping the build
63
75
  * tree out of the cached source path makes each machine configure its own. */
64
- const sdlBuild = join(vendorDir, "sdl-build");
76
+ const sdlBuild = join(vendorDir, "sdl-build-lean");
77
+ /* HarfBuzz + FreeType are only linked when the optional `shaping` feature is
78
+ * active; the archive is still built once so the default text path needs no
79
+ * external dependency. */
65
80
  const freetypeSrc = join(vendorDir, "freetype");
66
- const freetypeBuild = join(vendorDir, "freetype-build");
81
+ const freetypeBuild = join(vendorDir, "freetype-build-lean");
67
82
  const harfbuzzSrc = join(vendorDir, "harfbuzz");
68
- const harfbuzzBuild = join(vendorDir, "harfbuzz-build");
83
+ const harfbuzzBuild = join(vendorDir, "harfbuzz-build-lean");
69
84
  /* glslang translates the GLSL ES that WebGL programs supply at run time; it is
70
85
  * built statically and merged into the archive, so a compiled binary needs no
71
86
  * Vulkan SDK. The SPIR-V headers are its only external dependency. */
72
87
  const glslangSrc = join(vendorDir, "glslang");
73
- const glslangBuild = join(vendorDir, "glslang-build");
88
+ const glslangBuild = join(vendorDir, "glslang-build-lean");
74
89
  const spirvHeadersSrc = join(glslangSrc, "External", "spirv-headers");
75
90
  /* SPIRV-Cross turns the same SPIR-V into the Metal Shading Language that
76
91
  * SDL_GPU's Metal backend consumes at run time (macOS). It needs no external
77
92
  * headers: its own parser reads the module that glslang produced. */
78
93
  const spirvCrossSrc = join(vendorDir, "spirv-cross");
79
- const spirvCrossBuild = join(vendorDir, "spirv-cross-build");
94
+ const spirvCrossBuild = join(vendorDir, "spirv-cross-build-lean");
80
95
  const objDir = join(root, "build", "gui-obj");
81
96
  const outDir = join(runtimeDir, "lib", platformSlug());
82
97
 
@@ -84,41 +99,6 @@ const isWindows = process.platform === "win32";
84
99
  const objectSuffix = isWindows ? ".obj" : ".o";
85
100
  const output = join(outDir, isWindows ? "gui.lib" : "gui.a");
86
101
 
87
- /* CMake generator and ABI settings used for every vendored library. Ninja is
88
- * selected on Windows because the Visual Studio generator imports the
89
- * machine-wide MSBuild/vcpkg integration, whose `LIBPATH` shadows the Windows
90
- * SDK and makes the compiler probe fail with
91
- * `LNK1104: cannot open file 'ucrtd.lib'`.
92
- *
93
- * `CMAKE_MSVC_RUNTIME_LIBRARY` is pinned to the *static* CRT (`MultiThreaded`),
94
- * which is clang's Windows default (`-defaultlib:libcmt`) and what xbintsc uses
95
- * for its own runtime objects. Building the vendored libraries against the DLL
96
- * CRT instead fails the final link with
97
- * `LNK2038: mismatch detected for 'RuntimeLibrary'`. The variable only takes
98
- * effect when policy CMP0091 is NEW, but FreeType and HarfBuzz declare
99
- * `cmake_minimum_required` below 3.15, which defaults the policy to OLD and
100
- * silently ignores it — hence `CMAKE_POLICY_DEFAULT_CMP0091`.
101
- *
102
- * The tool paths are absolute and come from the same clang the driver resolves
103
- * (on Windows, the LLVM bundled with Visual Studio when no newer one is on
104
- * `PATH`), so every object merged into the archive shares one ABI and one
105
- * optimization behaviour. Never leave these as bare `clang`/`clang++`: an older
106
- * compiler earlier on `PATH` may be picked up at build time. */
107
- function cmakeToolchainArgs(): string[] {
108
- const compilers = [
109
- `-DCMAKE_C_COMPILER=${clang}`,
110
- `-DCMAKE_CXX_COMPILER=${clangxx}`,
111
- ];
112
- if (!isWindows) return compilers;
113
- return [
114
- "-G", "Ninja",
115
- "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded",
116
- "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW",
117
- ...compilers,
118
- `-DCMAKE_AR=${llvmAr}`,
119
- ];
120
- }
121
-
122
102
  const SDL_TAG = process.env.SDL3_TAG || "release-3.2.10";
123
103
  const FREETYPE_VERSION = process.env.FREETYPE_VERSION || "2.13.3";
124
104
  const HARFBUZZ_VERSION = process.env.HARFBUZZ_VERSION || "10.1.0";
@@ -274,7 +254,7 @@ function configureCmake(
274
254
  rmSync(build, { recursive: true, force: true });
275
255
  }
276
256
  const cmake = findCmake();
277
- run(cmake, ["-S", source, "-B", build, ...cmakeToolchainArgs(), ...extraArgs]);
257
+ run(cmake, ["-S", source, "-B", build, ...cmakeToolchainArgs({ clang, clangxx, llvmAr, isWindows }), ...extraArgs]);
278
258
  return cmake;
279
259
  }
280
260
 
@@ -318,7 +298,7 @@ function firstExisting(candidates: readonly string[]): string | undefined {
318
298
  }
319
299
 
320
300
  /** Locate a library CMake produced, accepting the POSIX and MSVC names and
321
- * falling back to a version-suffixed MSVC name (e.g. `freetype-2.13.3.lib`). */
301
+ * falling back to a version-suffixed MSVC name. */
322
302
  function builtLibrary(buildDir: string, names: readonly string[], pattern?: RegExp): string | undefined {
323
303
  const candidates: string[] = [];
324
304
  for (const name of names) {
@@ -398,6 +378,8 @@ if (!existingSdlArchive) {
398
378
  "-DSDL_STATIC=ON",
399
379
  "-DSDL_TESTS=OFF",
400
380
  "-DSDL_EXAMPLES=OFF",
381
+ ...LEAN_SDL_OPTIONS,
382
+ ...SECTION_CMAKE_FLAGS,
401
383
  ]);
402
384
  console.log(`xbintsc: building static SDL3 with ${cmake}`);
403
385
  run(cmake, ["--build", sdlBuild, "--config", "Release", "--target", "SDL3-static", "--parallel"]);
@@ -405,27 +387,7 @@ if (!existingSdlArchive) {
405
387
  console.log(`xbintsc: reusing existing ${existingSdlArchive}`);
406
388
  }
407
389
 
408
- /* -- 2b. FreeType + HarfBuzz (text) --------------------------------------- */
409
-
410
- /** Download a release tarball into `vendor/` and extract it under `name`.
411
- * Release tarballs are used (not git clones) because they ship generated
412
- * sources, so no ragel/autotools step is needed. `marker` is a file that only
413
- * exists once the tarball has been extracted (e.g. `CMakeLists.txt`). */
414
- function ensureTarballSource(name: string, url: string, extractedName: string, marker = "CMakeLists.txt"): string {
415
- const destination = join(vendorDir, name);
416
- if (existsSync(join(destination, marker))) return destination;
417
- const tarball = join(vendorDir, `${name}.tar.xz`);
418
- console.log(`xbintsc: fetching ${name} from ${url}`);
419
- mkdirSync(vendorDir, { recursive: true });
420
- run("curl", ["-sL", "--fail", "-o", tarball, url]);
421
- run("tar", ["-xf", tarball, "-C", vendorDir]);
422
- const extracted = join(vendorDir, extractedName);
423
- if (!existsSync(extracted)) fail(`expected ${extractedName} after extracting ${name}`);
424
- rmSync(destination, { recursive: true, force: true });
425
- renameSync(extracted, destination);
426
- rmSync(tarball, { force: true });
427
- return destination;
428
- }
390
+ /* -- 2a. HarfBuzz + FreeType (optional `shaping` feature) ---------------- */
429
391
 
430
392
  ensureTarballSource("freetype", FREETYPE_URL, `freetype-${FREETYPE_VERSION}`);
431
393
  ensureTarballSource("harfbuzz", HARFBUZZ_URL, `harfbuzz-${HARFBUZZ_VERSION}`);
@@ -440,6 +402,7 @@ if (!existsSync(freetypeArchive() ?? "")) {
440
402
  "-DFT_DISABLE_PNG=TRUE",
441
403
  "-DFT_DISABLE_HARFBUZZ=TRUE",
442
404
  "-DFT_DISABLE_BROTLI=TRUE",
405
+ ...SECTION_CMAKE_FLAGS,
443
406
  ]);
444
407
  console.log("xbintsc: building static FreeType");
445
408
  run(cmake, ["--build", freetypeBuild, "--config", "Release", "--parallel"]);
@@ -458,6 +421,8 @@ if (!existsSync(harfbuzzArchive() ?? "")) {
458
421
  "-DHB_HAVE_GLIB=OFF",
459
422
  "-DHB_HAVE_ICU=OFF",
460
423
  "-DHB_HAVE_GRAPHITE2=OFF",
424
+ `-DCMAKE_C_FLAGS=${SECTION_FLAGS.join(" ")}`,
425
+ `-DCMAKE_CXX_FLAGS=${[...HB_LEAN_DEFINES, ...SECTION_FLAGS].join(" ")}`,
461
426
  ]);
462
427
  console.log("xbintsc: building static HarfBuzz");
463
428
  run(cmake, ["--build", harfbuzzBuild, "--config", "Release", "--parallel"]);
@@ -465,7 +430,27 @@ if (!existsSync(harfbuzzArchive() ?? "")) {
465
430
  console.log(`xbintsc: reusing existing ${harfbuzzArchive()}`);
466
431
  }
467
432
 
468
- /* -- 2c. glslang (WebGL GLSL ES -> SPIR-V) -------------------------------- */
433
+ /** Download a release tarball into `vendor/` and extract it under `name`.
434
+ * Release tarballs are used (not git clones) because they ship generated
435
+ * sources, so no ragel/autotools step is needed. `marker` is a file that only
436
+ * exists once the tarball has been extracted (e.g. `CMakeLists.txt`). */
437
+ function ensureTarballSource(name: string, url: string, extractedName: string, marker = "CMakeLists.txt"): string {
438
+ const destination = join(vendorDir, name);
439
+ if (existsSync(join(destination, marker))) return destination;
440
+ const tarball = join(vendorDir, `${name}.tar.xz`);
441
+ console.log(`xbintsc: fetching ${name} from ${url}`);
442
+ mkdirSync(vendorDir, { recursive: true });
443
+ run("curl", ["-sL", "--fail", "-o", tarball, url]);
444
+ run("tar", ["-xf", tarball, "-C", vendorDir]);
445
+ const extracted = join(vendorDir, extractedName);
446
+ if (!existsSync(extracted)) fail(`expected ${extractedName} after extracting ${name}`);
447
+ rmSync(destination, { recursive: true, force: true });
448
+ renameSync(extracted, destination);
449
+ rmSync(tarball, { force: true });
450
+ return destination;
451
+ }
452
+
453
+ /* -- 2b. glslang (WebGL GLSL ES -> SPIR-V) -------------------------------- */
469
454
 
470
455
  ensureTarballSource("glslang", GLSLANG_URL, `glslang-${GLSLANG_VERSION}`);
471
456
  ensureTarballSource(join("glslang", "External", "spirv-headers"), SPIRV_HEADERS_URL,
@@ -479,6 +464,7 @@ if (!existsSync(glslangArchive() ?? "")) {
479
464
  "-DENABLE_HLSL=OFF",
480
465
  "-DENABLE_GLSLANG_BINARIES=OFF",
481
466
  "-DGLSLANG_TESTS=OFF",
467
+ ...SECTION_CMAKE_FLAGS,
482
468
  ]);
483
469
  console.log("xbintsc: building static glslang");
484
470
  run(cmake, ["--build", glslangBuild, "--config", "Release", "--parallel"]);
@@ -488,25 +474,35 @@ if (!existsSync(glslangArchive() ?? "")) {
488
474
 
489
475
  /* -- 2d. SPIRV-Cross (SPIR-V -> Metal Shading Language) ------------------- */
490
476
 
491
- ensureTarballSource("spirv-cross", SPIRV_CROSS_URL, `SPIRV-Cross-${SPIRV_CROSS_VERSION}`);
492
-
493
- if (!existsSync(spirvCrossArchive("spirv-cross-msl") ?? "")) {
494
- const cmake = configureCmake("SPIRV-Cross", spirvCrossSrc, spirvCrossBuild, [
495
- "-DCMAKE_BUILD_TYPE=Release",
496
- "-DSPIRV_CROSS_CLI=OFF",
497
- "-DSPIRV_CROSS_ENABLE_TESTS=OFF",
498
- /* Only the MSL target is used; skip the C++, HLSL, reflection and C APIs
499
- * so the archive stays small. MSL links against the GLSL base class. */
500
- "-DSPIRV_CROSS_ENABLE_HLSL=OFF",
501
- "-DSPIRV_CROSS_ENABLE_CPP=OFF",
502
- "-DSPIRV_CROSS_ENABLE_REFLECT=OFF",
503
- "-DSPIRV_CROSS_ENABLE_UTIL=OFF",
504
- "-DSPIRV_CROSS_ENABLE_C_API=OFF",
505
- ]);
506
- console.log("xbintsc: building static SPIRV-Cross");
507
- run(cmake, ["--build", spirvCrossBuild, "--config", "Release", "--parallel"]);
508
- } else {
509
- console.log(`xbintsc: reusing existing ${spirvCrossArchive("spirv-cross-msl")}`);
477
+ /* Metal is the only SDL_GPU backend that consumes MSL; Vulkan takes the SPIR-V
478
+ * module directly and D3D12 falls back to the ordinary painter. A non-Apple
479
+ * build therefore never calls `cross_compile_msl` (`webgl_msl.cpp` compiles to
480
+ * nothing there), so fetching and merging the archive would only add ~2.5 MiB
481
+ * of unreachable code to every Windows/Linux product. */
482
+ const metalBackend = process.platform === "darwin";
483
+
484
+ if (metalBackend) {
485
+ ensureTarballSource("spirv-cross", SPIRV_CROSS_URL, `SPIRV-Cross-${SPIRV_CROSS_VERSION}`);
486
+
487
+ if (!existsSync(spirvCrossArchive("spirv-cross-msl") ?? "")) {
488
+ const cmake = configureCmake("SPIRV-Cross", spirvCrossSrc, spirvCrossBuild, [
489
+ "-DCMAKE_BUILD_TYPE=Release",
490
+ "-DSPIRV_CROSS_CLI=OFF",
491
+ "-DSPIRV_CROSS_ENABLE_TESTS=OFF",
492
+ /* Only the MSL target is used; skip the C++, HLSL, reflection and C APIs
493
+ * so the archive stays small. MSL links against the GLSL base class. */
494
+ "-DSPIRV_CROSS_ENABLE_HLSL=OFF",
495
+ "-DSPIRV_CROSS_ENABLE_CPP=OFF",
496
+ "-DSPIRV_CROSS_ENABLE_REFLECT=OFF",
497
+ "-DSPIRV_CROSS_ENABLE_UTIL=OFF",
498
+ "-DSPIRV_CROSS_ENABLE_C_API=OFF",
499
+ ...SECTION_CMAKE_FLAGS,
500
+ ]);
501
+ console.log("xbintsc: building static SPIRV-Cross");
502
+ run(cmake, ["--build", spirvCrossBuild, "--config", "Release", "--parallel"]);
503
+ } else {
504
+ console.log(`xbintsc: reusing existing ${spirvCrossArchive("spirv-cross-msl")}`);
505
+ }
510
506
  }
511
507
 
512
508
  /* -- 3. engine objects ---------------------------------------------------- */
@@ -520,14 +516,14 @@ const includeFlags = [
520
516
  "-I", join(harfbuzzSrc, "src"),
521
517
  "-I", glslangSrc,
522
518
  "-I", join(spirvHeadersSrc, "include"),
523
- "-I", spirvCrossSrc,
519
+ ...(metalBackend ? ["-I", spirvCrossSrc] : []),
524
520
  ];
525
521
  const sources = readdirSync(engineDir)
526
522
  .filter((entry) => entry.endsWith(".cpp"))
527
523
  .sort();
528
524
 
529
525
  const objects: string[] = [];
530
- const cxxFlags = ["-std=c++17", "-O2", "-Wall", "-Wextra", ...includeFlags];
526
+ const cxxFlags = ["-std=c++17", "-O2", "-Wall", "-Wextra", ...SECTION_FLAGS, ...includeFlags];
531
527
  /* The engine is C++ and the MSVC C++ runtime must match the static C CRT that
532
528
  * clang links by default, so let every engine object record `libcpmt` as a
533
529
  * default library. The extension itself only contributes the DLL import library
@@ -550,16 +546,19 @@ const freetype = freetypeArchive();
550
546
  const harfbuzz = harfbuzzArchive();
551
547
  const glslang = glslangArchive();
552
548
  const glslangLimits = glslangLimitsArchive();
553
- const spirvCrossCore = spirvCrossArchive("spirv-cross-core");
554
- const spirvCrossGlsl = spirvCrossArchive("spirv-cross-glsl");
555
- const spirvCrossMsl = spirvCrossArchive("spirv-cross-msl");
549
+ const spirvCrossCore = metalBackend ? spirvCrossArchive("spirv-cross-core") : undefined;
550
+ const spirvCrossGlsl = metalBackend ? spirvCrossArchive("spirv-cross-glsl") : undefined;
551
+ const spirvCrossMsl = metalBackend ? spirvCrossArchive("spirv-cross-msl") : undefined;
556
552
  if (!sdl || !freetype || !harfbuzz || !glslang || !glslangLimits ||
557
- !spirvCrossCore || !spirvCrossGlsl || !spirvCrossMsl) {
558
- fail("one of the SDL3/FreeType/HarfBuzz/glslang/SPIRV-Cross archives was not found after building");
553
+ (metalBackend && (!spirvCrossCore || !spirvCrossGlsl || !spirvCrossMsl))) {
554
+ fail(`one of the SDL3/FreeType/HarfBuzz/glslang${metalBackend ? "/SPIRV-Cross" : ""} ` +
555
+ "archives was not found after building");
559
556
  }
560
557
  const libraries = [
561
558
  sdl, freetype, harfbuzz, glslang, glslangLimits,
562
- spirvCrossCore, spirvCrossGlsl, spirvCrossMsl,
559
+ ...(spirvCrossCore && spirvCrossGlsl && spirvCrossMsl
560
+ ? [spirvCrossCore, spirvCrossGlsl, spirvCrossMsl]
561
+ : []),
563
562
  ];
564
563
 
565
564
  mkdirSync(outDir, { recursive: true });
@@ -38,6 +38,7 @@ const IGNORED_DIRECTORIES = new Set([
38
38
  /** Vendored third-party sources that ship verbatim and are not ours to split. */
39
39
  const VENDORED_FILES = new Set([
40
40
  join("runtime", "ext_gui", "stb_image.h"),
41
+ join("runtime", "ext_gui", "stb_truetype.h"),
41
42
  join("runtime", "ext_gui", "nanosvg.h"),
42
43
  join("runtime", "ext_gui", "nanosvgrast.h"),
43
44
  ]);
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Size options shared by the GUI archive build.
3
+ *
4
+ * Kept apart from `build-gui.ts` so the orchestration stays under the
5
+ * repository's per-file line budget and the "what do we compile out?" policy is
6
+ * reviewable in one place.
7
+ */
8
+
9
+ /**
10
+ * CMake generator and ABI settings used for every vendored library. Ninja is
11
+ * selected on Windows because the Visual Studio generator imports the
12
+ * machine-wide MSBuild/vcpkg integration, whose `LIBPATH` shadows the Windows
13
+ * SDK and makes the compiler probe fail with
14
+ * `LNK1104: cannot open file 'ucrtd.lib'`.
15
+ *
16
+ * `CMAKE_MSVC_RUNTIME_LIBRARY` is pinned to the *static* CRT (`MultiThreaded`),
17
+ * which is clang's Windows default (`-defaultlib:libcmt`) and what xbintsc uses
18
+ * for its own runtime objects. Building the vendored libraries against the DLL
19
+ * CRT instead fails the final link with
20
+ * `LNK2038: mismatch detected for 'RuntimeLibrary'`. The variable only takes
21
+ * effect when policy CMP0091 is NEW, but some vendored projects declare
22
+ * `cmake_minimum_required` below 3.15, which defaults the policy to OLD and
23
+ * silently ignores it — hence `CMAKE_POLICY_DEFAULT_CMP0091`.
24
+ *
25
+ * The tool paths are absolute and come from the same clang the driver resolves
26
+ * (on Windows, the LLVM bundled with Visual Studio when no newer one is on
27
+ * `PATH`), so every object merged into the archive shares one ABI and one
28
+ * optimization behaviour. Never leave these as bare `clang`/`clang++`: an older
29
+ * compiler earlier on `PATH` may be picked up at build time.
30
+ */
31
+ export function cmakeToolchainArgs(toolchain: {
32
+ readonly clang: string;
33
+ readonly clangxx: string;
34
+ readonly llvmAr: string;
35
+ readonly isWindows: boolean;
36
+ }): string[] {
37
+ const compilers = [
38
+ `-DCMAKE_C_COMPILER=${toolchain.clang}`,
39
+ `-DCMAKE_CXX_COMPILER=${toolchain.clangxx}`,
40
+ ];
41
+ if (!toolchain.isWindows) return compilers;
42
+ return [
43
+ "-G", "Ninja",
44
+ "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded",
45
+ "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW",
46
+ ...compilers,
47
+ `-DCMAKE_AR=${toolchain.llvmAr}`,
48
+ ];
49
+ }
50
+
51
+ /** One function/data item per section, so the linker can drop what a program
52
+ * never references (`--gc-sections`, `-dead_strip`, link.exe `/OPT:REF`). */
53
+ export const SECTION_FLAGS = ["-ffunction-sections", "-fdata-sections"] as const;
54
+
55
+ /** The same flags as CMake cache entries. */
56
+ export const SECTION_CMAKE_FLAGS = [
57
+ `-DCMAKE_C_FLAGS=${SECTION_FLAGS.join(" ")}`,
58
+ `-DCMAKE_CXX_FLAGS=${SECTION_FLAGS.join(" ")}`,
59
+ ];
60
+
61
+ /**
62
+ * HarfBuzz APIs the engine never uses (draw/paint/color/name/style/metrics/…).
63
+ * `HB_NO_CFF`, `HB_NO_OPEN`, `HB_NO_VAR` and `HB_NO_HINTING` are deliberately
64
+ * *not* set: the shaping backend must load files and keep CFF/OTF and
65
+ * variable-font shaping. `HB_NO_MT` is safe because text is shaped on the main
66
+ * thread.
67
+ *
68
+ * `HB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR` is needed because HarfBuzz raises its
69
+ * "should never happen" warning set to errors inside `src/hb.hh`, *after* the
70
+ * command line, so a lean define that leaves a helper unreferenced becomes a
71
+ * hard compile error. macOS is where that bites: `HB_NO_MMAP` leaves
72
+ * `_open_resource_fork` unused (`hb-blob.cc`) and `HB_NO_OT_FONT_GLYPH_NAMES`
73
+ * leaves the CoreText glyph-name helpers unused (`hb-coretext-font.cc`). With
74
+ * this define they are warnings again; every other lean define still applies.
75
+ */
76
+ export const HB_LEAN_DEFINES = [
77
+ "-DHB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR",
78
+ "-DHB_NO_DRAW",
79
+ "-DHB_NO_PAINT",
80
+ "-DHB_NO_COLOR",
81
+ "-DHB_NO_NAME",
82
+ "-DHB_NO_STYLE",
83
+ "-DHB_NO_METRICS",
84
+ "-DHB_NO_MATH",
85
+ "-DHB_NO_META",
86
+ "-DHB_NO_BITMAP",
87
+ "-DHB_NO_VERTICAL",
88
+ "-DHB_NO_BUFFER_SERIALIZE",
89
+ "-DHB_NO_BUFFER_VERIFY",
90
+ "-DHB_NO_FACE_COLLECT_UNICODES",
91
+ "-DHB_NO_MMAP",
92
+ "-DHB_NO_OT_FONT_GLYPH_NAMES",
93
+ "-DHB_NO_LAYOUT_FEATURE_PARAMS",
94
+ "-DHB_NO_LAYOUT_COLLECT_GLYPHS",
95
+ "-DHB_NO_ERRNO",
96
+ "-DHB_NO_LANGUAGE_LONG",
97
+ "-DHB_NO_LANGUAGE_PRIVATE_SUBTAG",
98
+ "-DHB_NO_MT",
99
+ "-DHB_NO_ATEXIT",
100
+ "-DHB_NO_GETENV",
101
+ "-DHB_NO_SETLOCALE",
102
+ ];
103
+
104
+ /**
105
+ * SDL subsystems the GUI engine never calls. Disabling them removes whole
106
+ * translation units (joystick/gamepad/HIDAPI, haptics, sensors, camera, the
107
+ * SDL_Render software blitter and the OpenGL/offscreen video drivers) from the
108
+ * static archive. Audio, video, events, dialogs and SDL_GPU stay enabled.
109
+ */
110
+ export const LEAN_SDL_OPTIONS = [
111
+ "-DSDL_JOYSTICK=OFF",
112
+ "-DSDL_HAPTIC=OFF",
113
+ "-DSDL_SENSOR=OFF",
114
+ "-DSDL_CAMERA=OFF",
115
+ "-DSDL_RENDER=OFF",
116
+ "-DSDL_OPENGL=OFF",
117
+ "-DSDL_OPENGLES=OFF",
118
+ "-DSDL_OFFSCREEN=OFF",
119
+ "-DSDL_HIDAPI=OFF",
120
+ "-DSDL_DIALOG=ON",
121
+ ];
122
+
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Extension-set construction for the CLI.
3
+ *
4
+ * `buildRegistry` turns the `--ext` names into a registry; `normalizeExtensions`
5
+ * keeps the pre-link-feature spelling `--ext media` working by expanding it to
6
+ * `gui` plus a forced `media` feature. Both live here so `main.ts` stays under
7
+ * the repository's per-file line budget.
8
+ */
9
+
10
+ import { bundledExtensions } from "../extensions/catalog.js";
11
+ import { nativeExtensionFromManifest } from "../extensions/native.js";
12
+ import { createDefaultRegistry, type ExtensionRegistry } from "../extensions/registry.js";
13
+
14
+ /** Register the requested bundled extensions and every native manifest. */
15
+ export function buildRegistry(
16
+ extNames: readonly string[],
17
+ nativeManifests: readonly string[],
18
+ ): ExtensionRegistry {
19
+ const registry = createDefaultRegistry();
20
+ const bundled = bundledExtensions();
21
+ for (const name of extNames) {
22
+ const extension = bundled.find((candidate) => candidate.name === name);
23
+ if (!extension) throw new Error(`Unknown extension '${name}'`);
24
+ registry.register(extension);
25
+ }
26
+ // Each manifest describes a pre-built C++ or Rust library.
27
+ for (const manifest of nativeManifests) {
28
+ registry.register(nativeExtensionFromManifest(manifest));
29
+ }
30
+ // Hint the extensions that ship with xbintsc but were not enabled, so a
31
+ // missing `import` of a known module points at the flag that enables it
32
+ // (`pass --ext node`) instead of failing later with a confusing error.
33
+ for (const extension of bundled) {
34
+ if (!registry.has(extension.name)) registry.hintExtension(extension);
35
+ }
36
+ return registry;
37
+ }
38
+
39
+ /** The part of the CLI IO a normalization warning needs. */
40
+ export interface ExtensionWarningSink {
41
+ stderr(text: string): void;
42
+ }
43
+
44
+ /**
45
+ * `--ext media` predates link features: media is now an optional half of `gui`
46
+ * selected from the program. Keep the old spelling working for one release by
47
+ * expanding it to `gui` plus a forced `media` feature.
48
+ */
49
+ export function normalizeExtensions(
50
+ extNames: readonly string[],
51
+ io: ExtensionWarningSink,
52
+ ): { extensions: string[]; features: string[] } {
53
+ const extensions: string[] = [];
54
+ const features: string[] = [];
55
+ let deprecatedMedia = false;
56
+ for (const name of extNames) {
57
+ if (name === "media") {
58
+ deprecatedMedia = true;
59
+ if (!features.includes("media")) features.push("media");
60
+ if (!extensions.includes("gui")) extensions.push("gui");
61
+ continue;
62
+ }
63
+ if (!extensions.includes(name)) extensions.push(name);
64
+ }
65
+ if (deprecatedMedia) {
66
+ io.stderr(
67
+ "xbintsc: warning: '--ext media' is deprecated; media is auto-detected with --ext gui " +
68
+ "(use --feature media to force it)\n",
69
+ );
70
+ }
71
+ return { extensions, features };
72
+ }
package/src/cli/help.ts CHANGED
@@ -25,7 +25,9 @@ Options:
25
25
  --out <dir> Output directory (default: build/)
26
26
  --emit <kind> exe | obj | ir (default: exe)
27
27
  -O0..-O3 Optimization level (default: -O2)
28
- --ext <names> Comma separated extensions (e.g. node,gui,media)
28
+ --ext <names> Comma separated extensions (e.g. node,gui)
29
+ --feature <names> Force optional link features on (media,gpu,images,shaping,webgl,all)
30
+ --no-feature <n> Keep optional link features out even when detected
29
31
  --ext-native <m> Register a C++/Rust extension from a JSON manifest
30
32
  (comma separated for several)
31
33
  --config <path> Use a project config (default: auto-detect
package/src/cli/hints.ts CHANGED
@@ -78,9 +78,9 @@ const THROWN_FAILURES: readonly ThrownFailure[] = [
78
78
  },
79
79
  },
80
80
  {
81
- /* A dependent extension (`media` needs `gui`) or a missing prebuilt archive
82
- * (`gui.a`/`media.a`) both mean "the extension set is not ready", and the
83
- * extensions guide explains both the flags and `npm run gui`/`media`. */
81
+ /* A dependent extension or a missing prebuilt archive (`gui.a`/`media.a`)
82
+ * both mean "the extension set is not ready", and the extensions guide
83
+ * explains both the flags and `npm run gui`/`media`. */
84
84
  match: /requires the '.*' extension|native library not found/i,
85
85
  code: DiagnosticCode.IOError,
86
86
  pointer: {
@@ -167,6 +167,12 @@ export function documentationFor(diagnostic: Diagnostic): DocumentationPointer |
167
167
  path: DOC_CLI,
168
168
  };
169
169
 
170
+ case DiagnosticCode.LinkFeatureUnavailable:
171
+ return {
172
+ text: "check the link-feature name, and build its prebuilt archive (`npm run gui` / `npm run media`) if it is missing; choose features with --feature / --no-feature",
173
+ path: DOC_EXTENSIONS,
174
+ };
175
+
170
176
  case DiagnosticCode.CircularDependency:
171
177
  return {
172
178
  text: "xbintsc does not support circular imports; move the shared binding into a third module",