Skip to content

Rust bindings target in generate.mk - #7104

Merged
Fedr merged 23 commits into
masterfrom
rust-bindings-target
Oct 14, 2026
Merged

Fedr merged 23 commits into
masterfrom
rust-bindings-target

Conversation

@Fedr

@Fedr Fedr commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Adds TARGET=rust to scripts/mrbind/generate.mk, the MeshLib side of MeshInspector/mrbind#54 (requested in #5910), plus examples, tests, CI, and the crate as a release asset.

generate.mk

make -f scripts/mrbind/generate.mk TARGET=rust

generates source/MeshLibRust/src (gitignored) from source/MeshLibC2/temp/interop_desc.json and runs cargo build on the crate. source/MeshLibRust/Cargo.toml and build.rs are committed (the generator doesn't emit them, per the mrbind review). Like TARGET=csharp, it needs TARGET=c to have run first. The CUDA sub-library is mapped to MeshLibC2Cuda with --imported-split-lib-name, same as for C#. --force-namespace MR puts the std:: container instantiations under mr:: too. Consumers of the crate get the C libraries' directory from MESHLIB_LIB_DIR, which build.rs reads and the build rule sets to the MeshLib build directory when it exists.

On the full MeshLib API the generator wraps 37764 functions (715 of them with raw C types, none skipped) and the crate compiles with no warnings. Classes get upcast_to_<base>() / downcasts from the C cast functions, std::function wrappers get from_fn(closure), and [[clang::lifetimebound]] becomes explicit lifetimes.

Examples

examples/rust-examples has MeshBoolean, MeshDecimate and MeshLoadSave, ported from the C examples. The boolean one, for comparison with MeshBoolean.dox.c:

let sphere1 = mr::make_uv_sphere(Some(1.0), Some(64), Some(64));
let mut sphere2 = sphere1.clone();
sphere2.transform(&mr::AffineXf3f::translation(&Vector3f {x: 0.7, y: 0.0, z: 0.0}), None);
let mut mapper = BooleanResultMapper::new();
let result = mr::boolean_6(&sphere1, &sphere2, BooleanOperation::Intersection, None, Some(&mut mapper), None);
if !result.valid() { return Err(format!("Failed to perform boolean: {}", result.error_string())); }
let faces_of_sphere1 = mapper.map_mr_face_bit_set(sphere1.topology().get_valid_faces(), MapObject::A);

Tests

source/MRTestRust ports all of MRTestC2 (28 files, 92 tests) to cargo test, one module per C test file, with the same inputs and expected numbers, plus one test with a progress callback made from a Rust closure (including cancelling through it). All pass on every CI job below, and locally against the MeshLib 3.1.4.297 NuGet libraries (one assertion, compute_per_face_normals_1, needs a newer library than that package).

CI

One Release job per OS and architecture runs .github/actions/build-rust-bindings: install Rust if missing (.github/actions/setup-rust, a minimal rustup toolchain in the runner's temp directory), generate and build the crate, build and run the examples and the tests against the freshly built C bindings. That's the GCC jobs on Ubuntu (22/24/26, x64 and arm64), the CMake jobs on Windows (x64 and arm64), and the vcpkg jobs on macOS (x64 and arm64).

The arm64 ubuntu22 job that already uploads the C# and Python binding sources also uploads the crate, and upload-distributions attaches it to the draft release as meshlib_<version>_rust_bindings.zip (Cargo.toml, build.rs, src; the C libraries come from the platform packages).
The generated src is byte-identical on all 10 jobs (verified by a temporary comparison job, https://github.com/MeshInspector/MeshLib/actions/runs/38567043688/job/115887156957), so one archive serves all platforms.

Docs

doxygen/general_pages/RustSetupGuide.md (linked from Getting Started and the navigation), and Rust tabs in the boolean, decimate and load/save example pages.

mrbind gitlink

thirdparty/mrbind is bumped to the head of the rust-generator branch so that mrbind_gen_rust gets built by the existing mrbind build steps on every platform. It must be re-bumped to the merge commit once mrbind#54 merges.

🤖 Generated with Claude Code

Fedr and others added 13 commits October 12, 2026 12:48
`make -f scripts/mrbind/generate.mk TARGET=rust` generates the `meshlib`
crate into source/MeshLibRust from the interop JSON of the C bindings
and runs `cargo build` on it, the same way TARGET=csharp does for C#.
Bumps mrbind to the branch of MeshInspector/mrbind#54, which adds the
generator; re-bump once that merges.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
examples/rust-examples: MeshBoolean, MeshDecimate and MeshLoadSave,
ported from the C examples. The Ubuntu job that uploads the bindings
sources now also generates the crate and builds and runs the examples
against the freshly built C bindings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The trim step deleted it before caching. The action file is now part of
the cache key, so the caches saved without it are not reused.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Ubuntu job stores PATH in GITHUB_ENV, and the GITHUB_PATH prepend
rebuilt PATH without the virtualenv, so the later "Create Python Stubs"
step could not find pip.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One Release job per OS and architecture: GCC jobs on Ubuntu, the CMake
jobs on Windows (x64 and arm64), the vcpkg jobs on macOS. A composite
action installs a minimal rustup toolchain into the runner temp
directory when `cargo` is missing. The crate is always built in the dev
profile, consumers pick their own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
In the MSYS2 shell that generate_win.bat runs make in, `link.exe` on PATH
is the coreutils one, and rustc tried to link with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
source/MRTestRust: one module per C test file, 92 tests, run with
`cargo test` on the same CI jobs that build the Rust examples.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e build output

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er mr::

Follows the mrbind review: the generator no longer emits the crate manifest, so source/MeshLibRust/Cargo.toml and build.rs are hand-written and committed, and only src/ is generated (and ignored). --force-namespace MR puts the std:: container instantiations under mr::, which the tests now use.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mrbind now generates from_fn()/assign_fn() on the std::function wrappers, casts to and from the base classes, and explicit lifetimes from the annotations.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@Grantim Grantim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should at least put result package at draft releases

Comment thread .github/workflows/build-test-macos.yml Outdated
…draft release

Review: the three Rust steps of the Ubuntu, Windows and macOS workflows become one shared action. The arm64 ubuntu22 job that already uploads the C# and Python binding sources also uploads the crate (Cargo.toml, build.rs, src), and upload-distributions attaches it to the draft release as meshlib_<version>_rust_bindings.zip.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@Grantim Grantim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we upload from other platforms too?

…hem and publishes one

Review: the crate is sources only and must come out the same on every platform, so instead of trusting one job, all Rust jobs upload RustBindings-<os>-<arch>, upload-distributions diffs them (a warning annotation on a mismatch) and attaches the first one to the draft release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Fedr

Fedr commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor Author

The crate is sources only, like the C# assembly: the same code on every platform (it references the C library by name, MeshLibC2, which the linker resolves to the .dll/.so/.dylib of the target), while the binaries come from the platform packages. So we publish one crate, and the new compare-rust-bindings job verifies on every run that all platforms generate the same one (8122de7, f45e593).

Fedr and others added 2 commits October 14, 2026 15:02
…generate the same crate; bump mrbind

The PR run showed the crates identical on all platforms except for CRLF on Windows, which mrbind fd142eb9 fixes by writing in binary mode. The comparison now runs on every run as its own job (an error on a mismatch, upload-distributions needs it), instead of a warning in the release-only publishing job.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The first compare-rust-bindings run found the generated sources identical on all 10 platforms; only the committed Cargo.toml and build.rs differed, by the CRLF of the Windows checkout. The jobs now upload src alone, and the release archive takes the manifest from the checkout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread .github/workflows/build-test-distribute.yml Outdated
@Grantim

Grantim commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

should we mention Rust in public docs?

Fedr and others added 2 commits October 14, 2026 16:40
…g sources

Review: no other bindings have a cross-platform comparison job, and the per-platform uploads plus compare-rust-bindings were there only to verify once that all platforms generate the same crate. They did (the generated src is byte-identical on all 10 jobs since mrbind writes LF on Windows), so the check goes, and the arm64 ubuntu22 job that uploads the C# and Python sources uploads the Rust src for the draft release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ave examples

Review: the public docs now mention Rust. The guide covers getting the crate from the release archive, MESHLIB_LIB_DIR for linking, the runtime library path, and the API conventions (Owned<T>, Option for defaults, C-style overload names, closures for callbacks, explicit casts).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Fedr

Fedr commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Done in 5e35e44: a Rust setup guide (doxygen/general_pages/RustSetupGuide.md, linked from Getting Started and the navigation), and Rust tabs in the boolean, decimate and load/save example pages.

Comment thread source/MeshLibRust/Cargo.toml Outdated
Review: v3.1.4.528 becomes the semver 3.1.4+528 when upload-distributions packs the crate; the committed manifest keeps 0.0.0, like a local build has no version.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fedr and others added 2 commits October 14, 2026 23:00
…); mrbind on the vibecoded branch head

mrbind#54 was squash-merged into the vibecoded branch as dcb43322 (the interop field is callable_info there, the CLI is unchanged).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Fedr
Fedr merged commit 0517526 into master Oct 14, 2026
59 checks passed
@Fedr
Fedr deleted the rust-bindings-target branch October 14, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants