YAML schema
Every key the build YAML accepts — mainBuild, extraBuilds, additionalCppCode, additionalCppFiles, additionalBindCode.
The build YAML controls which OCCT classes get bound, which C++ wrapper code is injected, and which emscripten linker flags drive the final wasm.
Top-level shape
mainBuild:
name: <string>
bindings:
- symbol: <ClassName>
emccFlags:
- <flag>
additionalBindCode: |
EMSCRIPTEN_BINDINGS(custom) { ... }
extraBuilds:
- name: <string>
bindings: [...]
emccFlags: [...]
additionalBindCode: |
...
additionalCppCode: |
typedef opencascade::handle<Geom_Curve> Handle_Geom_Curve;
class MyWrapper { ... };
additionalCppFiles:
- path/to/extra.cpp
generateTypescriptDefinitions: trueThe canonical Cerberus definition lives in src/customBuildSchema.py.
mainBuild
The primary wasm artifact produced by the YAML.
mainBuild.name
Output filename without extension. name: my-occt produces my-occt.wasm,
my-occt.js, my-occt.d.ts, and my-occt.build-manifest.json.
mainBuild.bindings
Allowlist of OCCT classes to expose to JS via embind. Only classes listed here (and their transitive base classes) are accessible at runtime.
bindings:
- symbol: BRepPrimAPI_MakeBox
- symbol: TopoDS_Shape
- symbol: gp_PntThe symbol name must match exactly the C++ class name in the generated
binding .cpp files under build/bindings/. Base classes are auto-included
if missing from the list.
mainBuild.emccFlags
Emscripten linker flags. See Custom emcc flags for the recommended baseline and per-flag rationale.
mainBuild.additionalBindCode
Per-build raw EMSCRIPTEN_BINDINGS(...) registrations compiled with
<emscripten/bind.h> already included. See Extend with C++.
Symbol resolution classes
Every YAML-requested symbol becomes a linked binding through exactly one of four mechanisms. The post-link build-manifest.json (schema build-manifest-v2) buckets each requested symbol into one of these categories under symbols:
- Direct compilation.
bindings: - symbol: gp_Pntcauses the generator to emitbuild/bindings/gp_Pnt.cpp, whichcompileBindings.pycompiles intobuild/compiled-bindings/gp_Pnt.cpp.o. Detected byocjs_bindgen.link.manifest_registry.collect_compiled_symbols. Reported assatisfied_by_compiled(count surfaces assymbols.compiled). - NCollection typedef alias.
bindings: - symbol: TColgp_Array1OfPntresolves via the canonical mangled spellingNCollection_Array1_gp_Pnt; the linker substitutes the typedef at link time. Mapping lives inbuild/ncollection-manifest.json. Detected bymanifest_registry.load_ncollection_alias_index. Reported undersymbols.alias_resolvedas{alias, canonical}entries. - Embind builtin. OCJS's
BUILTIN_ADDITIONAL_BIND_CODEblock (OCJS,TopoDS,TColStd_IndexedDataMapOfStringString) registers Embind class wrappers with no.cpp.oof their own. Detected bymanifest_registry.builtin_binding_symbolsreadingbuild/additional-bind-symbols.json. - Consumer
additionalBindCode. YAML's ownmainBuild.additionalBindCodeblock undergoes the same Embind pathway. The link stage compilesBUILTIN_ADDITIONAL_BIND_CODE + consumer additionalBindCodeinto ONE translation unit so the AST producer emits a singleadditional-bind-symbols.jsonmanifest covering both sources. Reported undersymbols.builtin(no separate bucket).
Anything that survives all four lookups lands in symbols.missing and triggers validation_passed=false. The link step also raises immediately via yaml_build.verifyBindings (no env-var gate) so a YAML asking for a symbol the toolchain cannot provide fails the link, not just the post-link audit.
Auto-discovered NCollection canonicals (entries the YAML never named directly, but that became reachable from the YAML's scope) are tracked separately in <variant>.provenance.json::nCollectionManifest.{linked, total, dropped} (schema wasm-build-provenance-v1.1). They never appear in symbols.requested because they're produced by the discovery pass, not requested by the operator.
Producer-side manifest contract
Every mechanism above has exactly one producer — a pipeline stage with the semantic knowledge to compute it — that writes a JSON manifest in build/ or the dist sidecar. Every downstream consumer (link-time verifyBindings, post-link validate-build.py, generate-docs.mjs, docker-e2e-validate.sh) reads the manifest through the corresponding manifest_registry loader. No consumer re-parses C++, runs regex against source, or re-derives set-difference math.
| Manifest | Producer | Consumer loader |
|---|---|---|
build/ncollection-manifest.json | ocjs_bindgen.discover | manifest_registry.load_ncollection_alias_index |
build/additional-bind-symbols.json | runBuild::getAdditionalBindCodeO() (libclang AST via ocjs_bindgen.ast.parse_additional_bind_code + ocjs_bindgen.ast.walker.extract_class_registrations) | manifest_registry.builtin_binding_symbols |
build/compiled-bindings/*.cpp.o | compileBindings.py | manifest_registry.collect_compiled_symbols |
build/compiled-bindings/binding-report.json | compileBindings.py | validate-build.py::validate_binding_report |
<variant>.provenance.json::nCollectionManifest | yaml_build.main via provenance.add_linking(ncollection_linked=, ncollection_total=, ncollection_dropped=) | generate-docs.mjs, scripts/docker-e2e-validate.sh |
build/any-type-report.json | generate.py | validate-build.py::merge_any_reasons |
When a manifest is missing, consumers fail loudly with a pointer at pnpm nx run ocjs:build. Stale artifacts are stale by definition; rendering them with degraded math produces docs whose numbers contradict the build that produced them.
extraBuilds
Same schema as mainBuild. Each entry produces a sibling wasm artifact.
Useful for shipping multiple variants (e.g. baseline + relaxed-SIMD) from one
YAML pass.
additionalCppCode
Inline C++ injected into a generated .cpp file compiled with the same flags
as the bindings TU. Common uses:
- Handle typedefs (
typedef opencascade::handle<T> Handle_T;). - Free-function helpers reachable from
additionalBindCode. - Small
value_object<T>POD definitions.
additionalCppFiles
List of .cpp files concatenated onto additionalCppCode before custom-binding
generation runs.
additionalCppCode: |
typedef opencascade::handle<Geom_BSplineSurface> Handle_Geom_BSplineSurface;
additionalCppFiles:
- wrappers/fair-curve.cpp
- wrappers/shape-cast.cpp- Paths resolve relative to the YAML file's directory; absolute paths also accepted.
- File contents are appended in declaration order.
additionalCppCodewins on conflict because it precedes file contents in the TU.- A missing file fails-loud at validate time.
generateTypescriptDefinitions
Default true. Set to false to skip .d.ts generation (rare — useful only
for ultra-fast iteration builds).
Validation
./build-wasm.sh validate build-configs/my-config.ymlvalidate checks the YAML against the schema and exits non-zero on any
malformed entry, unknown key, duplicated symbol, or missing
additionalCppFiles path. It does not run the C++ pipeline — pair it with
./build-wasm.sh link to actually produce the wasm.
Related
- Trim symbols — end-to-end workflow.
- Extend with C++ — when to reach for which mechanism.
- Custom emcc flags — flag-by-flag rationale.