|
OCCT-Light 0.1
C ABI and C++ veneer for multi-language CAD workflows
|
This is the top-level architecture for OCCT-Light, a C-ABI wrapper around Open CASCADE Technology. It exists so that Python, C#, JS/TS, WASM, Rust, Go, Swift, and any other language with a C foreign-function interface can drive OCCT through a single, stable, language-agnostic surface — without re-translating C++ types per binding. Before the first release, ABI-breaking cleanups are allowed when they improve the final headless CAD interface.
For day-to-day rules, read the companions:
OCCTL_BUILD_* option covers, which OCCT toolkits it needs, and module-to-module dependencies.<stdint.h> and <stddef.h> only.TopoDS_* confined to internal round-trips for algorithms that don't yet have graph-native variants.OCCTL_BUILD_<MODULE>); core is always linked, everything else is optional.occtl-hpp) so C++ consumers get RAII and exceptions for free without any extra linkage.TKCAF, TKLCAF, TKCDF, TKXCAF, TKBin*, TKXml*, TKStd*, or TKTObj* for any module shipped in v1.OCCT-Light uses a stable C-ABI shape that is practical for every binding language:
struct_version, p_next);Hard rules between layers:
include/occtl/*.h) include <stdint.h>, <stddef.h>, and other occtl_*.h. Nothing else. Enforced by a CI grep.occtl-hpp is #include-only. It links nothing of its own; it just wraps the C ABI. Mismatch with the C headers is a build failure.occtl_internal is C++17. It freely uses OCCT, STL, NCollection, exceptions. Its symbols are hidden; nothing in src/ is OCCTL_API-marked. The golden rule for all modules that link OCCT: delegate every computation to OCCT types. Convert POD ABI structs to gp_* / BRep_Tool / BRepAlgoAPI_* types at the function entry point, call OCCT, convert the result back. Thin ToGp/FromGp conversion helpers are the canonical bridge — see src/geom/GeomMath.hxx. Reimplementing OCCT math in C++17 is explicitly forbidden.OCCTL::occtl / component compatibility targets.Implemented modules own src/<module>/, tests/<module>/, and public headers under include/occtl/ plus optional C++ veneer headers under include/occtl-hpp/. Planned modules stay in the registry as configure-time errors until their implementation lands.
occtl-core, occtl-geom, occtl-minimal, occtl-cad, occtl-full, and occtl-full-viz.OCCTL::core, OCCTL::topo, etc.) are compatibility/component targets over the selected physical library, not separate DSOs/archives.core is implicit and always linked. Others toggle via OCCTL_BUILD_<MODULE>=ON|OFF. Defaults: GEOM and TOPO are ON; everything else is OFF.cmake/OCCTLRegistry.cmake. The registry drives option creation, planned-module configure errors, OCCT toolkit discovery, enabled subdirectories, summary output, and installed feature metadata.occtl.h #includes only what's enabled.OCCTLFeatures.json with the selected physical library name, enabled components, binding features, enabled C/C++ headers, ABI version, and OCCT toolkit list. Bindings use it together with abi.json.CMakePresets.json:core-only — smallest; no OCCT required.geom-only — core + geom; used while iterating on geom.minimal — core + geom + topo + prim.cad — minimal + bool + mesh + heal + io_brep + io_step + io_stl + de.full — every implemented module except viz.full-with-viz — full + viz (heavy OS deps).LINK_ONLY dependency model.| Surface | Mechanism |
|---|---|
| Library version | OCCTL_VERSION_MAJOR/MINOR/PATCH macros + occtl_runtime_version(uint32_t* M, uint32_t* m, uint32_t* p). SemVer at the wrapper level, independent of OCCT version. |
| ABI version | OCCTL_ABI_VERSION integer macro and occtl_runtime_abi_version(uint32_t*). Bumped only on hard breakage. |
| Struct evolution | Every *_create_info_t carries uint32_t struct_version as its first field, and a reserved const void* p_next for future extension chains. Initializer macros and runtime *_init functions both set the version. |
| Symbol evolution | New behavior ships under _v2 symbols when the old symbol cannot be preserved bit-for-bit. Old symbols stay until the next major bump. |
| Enum evolution | Enums append values, never reorder. Each public enum carries OCCTL_<ENUM>_RESERVED_FUTURE = 0x7fffffff to force int32_t storage. |
| OCCT version coupling | Recorded in occtl_runtime_occt_version() for diagnostics; not part of the wrapper's ABI promise. |
The deprecation rule starts at the first release. During the pre-release phase, public symbols may be renamed, reshaped, or removed to keep the final API coherent; after the first release, nothing in the public surface silently changes behavior.
occtl_batch_commit).occtl_error_last() in thread A returns A's last error; never B's.occtl_progress_t* token. The token is itself thread-safe (the canceller may live on a different thread from the worker).Every public function returns occtl_status_t. OCCTL_OK == 0 so if (s) goto fail; works naturally in C callers. On failure, a thread-local occtl_error_t carries:
Exceptions never cross the C boundary. OcctL::Core::Guard catches Standard_Failure (root of all OCCT exceptions), std::exception, and ..., translates each to a status code, and populates the thread-local. See ABI_PATTERNS.md §5.
One allocator boundary at the ABI:
fn(..., NULL, &n) then allocate, refill).occtl_*_free (NULL-tolerant, idempotent).The hard rules in AGENTS.md cover what cannot appear in public headers (OCCT/STL types, exceptions, OCAF/XCAF). Two further omissions worth naming explicitly: no header-leaked OCCT macros (Standard_EXPORT, DEFINE_STANDARD_HANDLE), and no Tcl/Draw integration (that stays in OCCT's test harness).
Implemented: core, geom, topo, prim, text, bool, mesh, heal, io_brep, io_step, io_iges, io_stl, io_obj, io_gltf, io_vrml, io_ply, de, viz, and the Python / C# / Node / WASM / Rust / Go / Java binding trees. Planned: Swift and deeper native-window smoke coverage on Linux/Windows.
Each module ships with gtest coverage and at least one binding-language smoke test before being declared complete.