8000 Packaging: native platform detection, glibc floor, and Java ergonomics · Issue #4 · project-minigraf/minigraf-java · GitHub
[go: up one dir, main page]

Skip to content

Packaging: native platform detection, glibc floor, and Java ergonomics #4

Closed

Description

@adityamukho

Found while wiring io.github.project-minigraf:minigraf-jvm:2.0.2 into decision_telemetry. The round trip works on Linux x86_64, but the packaging has some rough edges. Ordered by impact.

1. NativeLoader picks the wrong binary instead of failing clearly

java/src/main/kotlin/io/github/project_minigraf/minigraf/NativeLoader.kt:

  • Any Linux arch that is not aarch64/arm64 gets linux/x86_64 (riscv64, ppc64le, s390x, x86).
  • Every Windows JVM gets windows/x86_64, including an arm64 JVM on Windows on ARM, which cannot load an x64 DLL.
  • There is no musl check, so Alpine gets a glibc .so.

Each case ends in an opaque UnsatisfiedLinkError from JNA rather than the "Unsupported platform" message. The else branch also points to the minigraf repo, not minigraf-java.

Suggested fix: match OS and arch explicitly (amd64/x86_64 and aarch64/arm64), detect musl on Linux (for example, check for /lib/ld-musl-*), and throw the unsupported-platform error for anything else. Point the error at this repo.

2. Linux natives need glibc 2.34

objdump -T on natives/linux/x86_64/libminigraf_ffi.so shows GLIBC_2.34 as the highest required version, because it is built on ubuntu-latest. That rules out RHEL/Rocky 8 (2.28), Debian 11 (2.31) and Ubuntu 20.04 (2.31), which are still common on servers.

Suggested fix: build the Linux targets against an older glibc, for example with cargo zigbuild --target x86_64-unknown-linux-gnu.2.17 or inside a manylinux container. Add x86_64-unknown-linux-musl and aarch64-unknown-linux-musl for Alpine, and aarch64-pc-windows-msvc for Windows on ARM.

3. The findLibraryName patch can silently no-op

generateKotlinBindings in java/build.gradle.kts uses a plain String.replace to inject NativeLoader.load(). If a UniFFI upgrade changes that line, the replace matches nothing, the build still passes, and the published jar cannot find its native library.

Suggested fix: fail the task when the pattern is not found:

val marker = "private fun findLibraryName(componentName: String): String {"
var patchedAny = false
// ... for each file:
if (marker in text) { patchedAny = true; file.writeText(text.replace(marker, "$marker\n    io.github.project_minigraf.minigraf.NativeLoader.load()")) }
// after the loop:
check(patchedAny) { "findLibraryName() not found in generated bindings; NativeLoader patch not applied" }

4. Public API lives in uniffi.minigraf_ffi

There is no uniffi.toml, so the generated classes use UniFFI's default package. Only NativeLoader is under io.github.project_minigraf.minigraf. Consumers have to import uniffi.minigraf_ffi.MiniGrafDb, which does not match the Maven group.

Suggested fix: add a uniffi.toml with

[bindings.kotlin]
package_name = "io.github.project_minigraf.minigraf"

This is a breaking change for imports, so it belongs in a minor or major release.

5. Factory methods are awkward from Java

open and openInMemory exist only on the Kotlin Companion, so Java callers write MiniGrafDb.Companion.open(path). UniFFI cannot add @JvmStatic itself. Options: a small hand-written Java-friendly facade (for example, Minigraf.open(path)), or at least a Java example in the README showing the Companion call.

6. Smaller items

  • No Automatic-Module-Name. The manifest only has Manifest-Version, so JPMS users get a file-name-derived module name. Add it in tasks.jar { manifest { attributes("Automatic-Module-Name" to "io.github.project_minigraf.minigraf") } }.
  • kotlin-stdlib is a compile-scope dependency. Java users get it on their classpath. This is expected for UniFFI Kotlin output, but it is worth a line in the README.
  • License metadata. The POM has one <license> named MIT OR Apache-2.0 that links only to LICENSE-MIT. Two separate <license> entries (MIT, Apache-2.0) are read correctly by license scanners.
  • Windows temp files leak. deleteOnExit cannot remove a DLL that is still loaded, so each JVM leaves a minigraf_native* directory behind on Windows. One fix is a stable per-version cache directory (for example, under java.io.tmpdir/minigraf-<version>/) that is reused instead of recreated.
  • KDoc is stale. NativeLoader says "Call load before using any Minigraf class", but the build patch calls it automatically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0