[FEA] Publish self-contained Java classifier JARs with a statically linked libcuopt, as cuDF does
@chris-maes is already working on this.
Since Aug 27, 2026.
- Dominant language
- Cuda
- Stars
- 1k
- Forks
- 233
- Avg merge
- 4d 4h
- Merged PRs (30d)
- 95
Description
Is your feature request related to a problem? Please describe.
The Java bindings (#1524) have no installation path. Nothing is published — no jar, no libcuopt_jni.so — so a user must build the JNI shim from source. That needs libcuopt, its headers, a C++20 compiler, CMake, Ninja, Maven and a JDK.
Conda is effectively mandatory, and not by choice: the JNI build needs the exact rmm and raft headers libcuopt was compiled against, because rmm encodes its version in an inline namespace (rmm::_RMM_26_10). A mismatched copy links cleanly and then fails at dlopen with an undefined symbol. Those headers only ship as conda packages, so a pip wheel is not sufficient.
Verified end to end in a clean nvidia/cuda:13.0.3-base-ubuntu24.04 container: miniforge -> conda env -> build_native.sh -> mvn package -> compile and run a user program. It works, but that is the floor for "hello world".
Describe the solution you'd like
Adopt the cuDF model: publish self-contained per-classifier JARs that bundle a statically linked native library, so a user needs only a Maven dependency and a driver.
cuDF did this in rapidsai/cudf#23261 (merged 2026-07-21):
Link against a static libcudf built from source per CUDA version rather than a conda shared libcudf.
It builds with -DBUILD_SHARED_LIBS=OFF and splits the work into three composable stages — static native build, per-classifier JAR packaging, Maven-repo gather.
The result is genuinely self-contained. libcudf.so inside the published jar has these DT_NEEDED entries and nothing else:
librt, libz, libdl, libpthread, libstdc++, libm, libgcc_s, libc, ld-linux
No libcudart, no librmm, no conda. Published classifiers on Maven Central for ai.rapids:cudf:26.08.1:
cudf-26.08.1-cuda12.jar 707.9 MB
cudf-26.08.1-cuda12-arm64.jar 702.6 MB
cudf-26.08.1-cuda13.jar 321.5 MB
cudf-26.08.1-cuda13-arm64.jar 317.5 MB
For cuOpt this would also defuse #1703 as a distribution hazard. libcuopt_jni.so currently reaches through the private pdlp/cuopt_c_internal.hpp and is coupled to a specific libcuopt build. That is only dangerous because the user supplies libcuopt independently; if both halves are built and shipped together, a cuOpt point release cannot break the jar. It does not remove the need for #1703, but it removes the failure mode.
Describe alternatives you've considered
The cuVS model — a pure-Java jar. com.nvidia.cuvs:cuvs-java is 1.4 MB, ships a NativeDependencyLoader, and expects libcuvs from conda/pip/system. Simpler and far smaller, but it leaves the user to supply and match the native library, which is exactly the fragile configuration given #1703.
Reasonable as a stopgap; poor as an end state.
Additional context
Size is the open risk, and it is specific to cuOpt. libcuopt.so is 554 MB shared with 29 DT_NEEDED entries:
| dependency | size |
|---|---|
| libcublas | 517 MB |
| libnccl | 279 MB |
| libcusparse | 155 MB |
| libcudss | 67 MB |
| libgrpc / protobuf / abseil | ~25 MB |
A naive static build would likely exceed cuDF's 708 MB. Maven Central documents a 1 GB limit per uploaded bundle ("You can upload an archive up to 1GB in size"), applying to the whole archive rather than a single file.
The scoping work needed is already in flight, and this issue depends on it:
- #1622 — split
libcuoptintocuopt_base/cuopt_routing/cuopt_lp. The Java bindings expose no routing, so a Java jar should carry only the LP components. - #1203 — static-link gRPC, protobuf, abseil, tbb, libgomp.
- #1607 — reduce
libcuopt.sobinary size.
NCCL (distributed multi-GPU PDLP) and the gRPC server stack are not needed by a Java jar at all, which is ~300 MB of the total.
Publishing infrastructure is being rebuilt upstream and we should adopt it rather than write our own:
- rapidsai/build-infra#379 — cuDF Java release pipeline migration to ADI-owned GitHub Actions. Its stated goal is that "other ADI Java projects (cuVS, cuvs-lucene, etc.) can adopt the same pattern."
- #382 sign/stage in Artifactory, #383 promote RC to Maven Central, #384 promote nightly to Sonatype snapshots — all In Progress.
- #385 provision credentials — Not started.
Worth noting when planning: cuDF's java-publish job is currently skipped in every recent build.yaml run (gated on release builds) and #385 is not started, so that pipeline has not yet run for a real release. The 26.08.1 artifacts on Central came from the legacy Spark RAPIDS Jenkins process. Its assemble_maven_repo.sh gathers all classifiers into a single Maven repo tree published as one bundle, which for 26.08.1 totals 2.69 GB against the documented 1 GB ceiling — unresolved, and raised upstream.
Proposed scope
- A static
libcuoptbuild (BUILD_SHARED_LIBS=OFF), LP-only where #1622 allows, excluding routing, NCCL and the gRPC server. - Per-classifier JAR packaging:
cuda12/cuda13xx86_64/arm64. - A Maven-repo gather step producing the layout the shared publish workflow consumes.
- Measure the resulting jar. If it approaches 1 GB, that is the signal to narrow scope further rather than to proceed.
Contributes to #1535.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.