Skip to content

Java bindings target in generate.mk - #7115

Merged
Fedr merged 17 commits into
masterfrom
java-bindings
Oct 15, 2026
Merged

Fedr merged 17 commits into
masterfrom
java-bindings

Conversation

@Fedr

@Fedr Fedr commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds TARGET=java to scripts/mrbind/generate.mk, the MeshLib side of MeshInspector/mrbind#55, plus examples, tests and CI. Same structure as #7104 for Rust.

generate.mk

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

generates source/MeshLibJava/src (gitignored) from source/MeshLibC2/temp/interop_desc.json, compiles it with -Xlint:all -Werror into source/MeshLibJava/classes, and packs source/MeshLibJava/meshlib.jar. Only a README is committed in source/MeshLibJava. Like TARGET=csharp and TARGET=rust, 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#; --package meshlib --force-namespace MR makes the API meshlib.mr.Mesh, meshlib.mr.Functions.makeCube(...), with the std:: container instantiations under meshlib.mr too. The JDK tools come from JAVA_HOME when it's set.

The bindings use the foreign function API of Java 22 (java.lang.foreign), so there's no JNI glue and the jar has no native code of its own: at runtime the MeshLib shared libraries must be in java.library.path. On the full MeshLib API the generator wraps 37863 functions (875 of them with raw C types, none skipped), 3157 classes, compiling in ~20 s with no warnings. Classes get upcastTo<Base>() / downcasts from the C cast functions, the std_function_... wrappers get of(lambda), and the [[clang::lifetimebound]] annotations decide what a returned reference keeps alive.

Examples

examples/java-examples has MeshBoolean, MeshDecimate and MeshLoadSave, ported from the C examples (plain javac, no build system; the classes are package-private because of the .dox.java file names). The boolean one, for comparison with MeshBoolean.dox.c:

Mesh sphere1 = Functions.makeUVSphere(1.0f, 64, 64);
Mesh sphere2 = new Mesh(sphere1);
sphere2.transform(AffineXf3f.translation(new Vector3f(0.7f, 0.0f, 0.0f)), null);
BooleanResultMapper mapper = new BooleanResultMapper();
BooleanResult result = Functions.boolean_(sphere1, sphere2, BooleanOperation.Intersection, null, mapper, null);
if (!result.valid()) { System.err.println("Failed to perform boolean: " + result.errorString()); System.exit(1); }
FaceBitSet facesOfSphere1 = mapper.map(sphere1.topology().getValidFaces(), BooleanResultMapper_MapObject.A);

Tests

source/MRTestJava ports all of MRTestC2 (28 files, 92 tests) one class per C test file, with the same inputs and expected numbers, plus TestCallbacks: a progress callback made from a Java lambda, cancelling through it, a lambda exception rethrown by the caller, and ObjectMesh ↔ Object casts. 96 tests, run by a 60-line reflective Main instead of a test framework, so the bindings stay dependency-free. All pass on every CI job below; locally against the MeshLib 3.1.4.297 NuGet libraries a few need newer functions than that package has.

CI

One Release job per OS and architecture generates and builds the bindings, runs MeshBoolean and the tests against the freshly built C bindings: 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 steps live in a build-java-bindings composite action with the same inputs as build-rust-bindings, called once per workflow right after the Rust one. .github/actions/setup-java downloads a Temurin JDK 25 (Microsoft's build on Windows arm64, Temurin has none past 21 there) into the runner's temp directory when the runner has no JDK 22+, the GitHub-hosted images ship up to 21. build-mrbind keeps and strips mrbind_gen_java too.

Windows needs the mangled DLLs

On Windows the tests run against a copy of the C library made with scripts/nuget_patch/patch_library_deps.py, i.e. with delvewheel-mangled dependency names like the NuGet package, not the raw build tree. java.exe has already loaded the JDK's own zip.dll when the bindings load MeshLibC2.dll, and MRMesh.dll imports libzip under that same name; the loader binds an import to an already-loaded module of the same base name, so it fails with "The specified procedure could not be found" no matter the search path (freetype.dll and msvcp140.dll from the JDK are further collisions of the same kind). So a Java distribution for Windows must ship the NuGet-style DLL set. The generated runtime also loads the library with LoadLibraryExW(LOAD_WITH_ALTERED_SEARCH_PATH) and reports the Windows error code when that fails.

Docs and release archive

doxygen/general_pages/JavaSetupGuide.md (prerequisites, where to get the jar and the libraries per platform, the java.library.path / --enable-native-access command line, a usage snippet and the naming rules), Java tabs in the three example pages, and Java in the language lists of About/GettingStarted and in the setup-guide menu, same as the Rust guide.

The arm64 ubuntu22 Release job uploads meshlib.jar and the generated src as the JavaBindings artifact, and upload-distributions packs them into meshlib_<version>_java_bindings.zip next to the Rust archive: one archive for all platforms, since the jar has no native code and the C libraries come from the platform packages.

mrbind gitlink

thirdparty/mrbind is bumped to the head of vibecoded with MeshInspector/mrbind#55 merged (a5bb95b4; master already pointed at that branch after the Rust generator), so that mrbind_gen_java gets built by the existing mrbind build steps on every platform.

🤖 Generated with Claude Code

Fedr and others added 8 commits October 13, 2026 13:25
`TARGET=java` generates `source/MeshLibJava/src` with mrbind_gen_java, compiles
it and packs `meshlib.jar`. Examples and the 92 tests of MRTestRust ported to
Java, built with plain javac. CI runs them in the same Release jobs as Rust,
with a Temurin JDK 25 downloaded into the runner temp directory when needed.

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

System.loadLibrary finds MeshLibC2.dll in java.library.path, but Windows resolves its dependencies (MRMesh.dll and the rest) through PATH.

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

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
java.exe loads msvcp140.dll from its own directory before anything else, and the
JDK's copy (14.40) is older than what MeshLib's DLLs need, which made loading
MeshLibC2.dll fail with "The specified procedure could not be found". Without
it the installed Visual C++ redistributable is used. Also bump mrbind for the
error reporting of that case.

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

java.exe has already loaded the JDK's own zip.dll when the bindings load
MeshLibC2.dll, and MRMesh.dll imports libzip under that same name, so the
import binds to the wrong module and fails with "The specified procedure could
not be found". The same holds for freetype.dll and msvcp140.dll. No search path
can fix a module that's already loaded, which is what the NuGet package and the
wheels solve by mangling the dependency names with delvewheel; the Windows CI
step now makes such a copy with the existing patch_library_deps.py. The JDK
runtime deletion and the temporary diagnostics are gone.

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

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fedr and others added 6 commits October 13, 2026 20:38
The mrbind side (mrbind#55, rebased onto mrbind#54) now constructs
std::function wrappers from Java lambdas and has the upcasts and downcasts
between a class and its bases. Tests: a progress callback, a cancelling one, a
lambda exception rethrown by the caller, and ObjectMesh <-> Object casts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts:
#	.github/actions/build-mrbind/action.yml
#	.github/workflows/build-test-macos.yml
#	.github/workflows/build-test-ubuntu.yml
#	.github/workflows/build-test-windows.yml
#	.gitignore
#	scripts/mrbind/generate.mk
#	thirdparty/mrbind
…he other languages (#7130)

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

JavaSetupGuide.md plus Java tabs in the three example pages, Java in the
language lists and in the setup-guide menu. The arm64 ubuntu22 Release job
uploads meshlib.jar and the generated sources, and upload-distributions packs
them into meshlib_<version>_java_bindings.zip next to the Rust archive.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread .github/workflows/build-test-macos.yml Outdated
Same shape as build-rust-bindings: lib-dir and windows-vsdevcmd inputs, the
three workflows call it with one step each. Requested by Grantim in the review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fedr and others added 2 commits October 15, 2026 14:59
@Fedr
Fedr merged commit 68c74ef into master Oct 15, 2026
60 checks passed
@Fedr
Fedr deleted the java-bindings branch October 15, 2026 13:44
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