Repository navigation
Explore self-contained Java classifier JARs with a statically linked libcuopt #1818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 32 commits
c156526
bdb8905
e521ce7
2f626af
a0816cb
87b17bb
6df31e9
a4219ed
a623cc4
78822d7
0c0fceb
74325ec
9722612
0bfbf4d
9558f83
1ee37cb
072308e
59cc144
6f37f3d
4bc2d27
d468b48
fc6443a
88b49bd
2dad31c
f373279
458b062
c1f9a15
0002a06
ee920a8
ed2fc5e
c57b227
2101490
651e791
073d518
b7497f1
050be90
d55136d
662f2a3
6c4dfe2
8b1e84d
edcb475
9f1eddd
a2e0d39
e703efb
2ab52f9
3f03753
4659c9a
6d3ace1
bc64107
27afd1f
50b1469
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,104 @@ jobs: | |
| date: ${{ inputs.date }} | ||
| sha: ${{ inputs.sha }} | ||
| script: ci/build_cpp.sh | ||
| java-static-build-matrix: | ||
| permissions: | ||
| contents: read | ||
| uses: rapidsai/shared-workflows/.github/workflows/compute-matrix.yaml@main | ||
| with: | ||
| build_type: ${{ inputs.build_type || 'branch' }} | ||
| matrix_name: conda-cpp-build | ||
| matrix_filter: 'map(. + {CUDA_MAJOR: (.CUDA_VER | split(".") | .[0])}) | unique_by([.ARCH, .CUDA_MAJOR])' | ||
|
|
||
| java-static-build: | ||
| needs: [build-details, java-static-build-matrix] | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| id-token: write | ||
| packages: read | ||
| pull-requests: read | ||
| secrets: inherit # zizmor: ignore[secrets-inherit] | ||
| uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJSON(needs.java-static-build-matrix.outputs.matrix) }} | ||
| with: | ||
| build_type: ${{ inputs.build_type || 'branch' }} | ||
| branch: ${{ inputs.branch }} | ||
| date: ${{ inputs.date }} | ||
| sha: ${{ inputs.sha }} | ||
| node_type: "cpu16" | ||
| arch: ${{ matrix.ARCH }} | ||
| # Must be CUDA-version-pinned, not the bare "26.10-latest" tag: custom-job.yaml has no | ||
| # per-matrix env passthrough, so an untagged image would leave RAPIDS_CUDA_VERSION at | ||
| # that image's single baked-in default for every matrix entry. | ||
| # ci/build_java_static.sh reads it to pick the conda CUDA toolkit, so every "cuN" classifier | ||
| # JAR would actually be built against the same CUDA version and just mislabeled. Matches | ||
| # the pattern conda-cpp-build.yaml itself uses for this image family. | ||
| container_image: "rapidsai/ci-conda:26.10-cuda${{ matrix.CUDA_VER }}-${{ matrix.LINUX_VER }}-py${{ matrix.PY_VER }}" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should be using the wheel/Rocky 8 image here in order to avoid issues like this: NVIDIA/cudf#23753
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — switched to |
||
| script: "ci/build_java_static.sh" | ||
| artifact-name: "cuopt_java_${{ matrix.ARCH }}_cu${{ matrix.CUDA_MAJOR }}" | ||
| file_to_upload: "java/cuopt/classifier-jars/" | ||
|
|
||
| # Runs the full Java suite against each classifier JAR on a GPU, with no libcuopt installed, | ||
| # so a JAR that loads but computes wrong answers fails here rather than at a user. | ||
| java-static-test: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the general pattern is to have the (nightly) test job be defined in the test.yaml file.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — java-static-test now lives in test.yaml, dispatched from build.yaml's |
||
| needs: [java-static-build, java-static-build-matrix] | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| id-token: write | ||
| packages: read | ||
| pull-requests: read | ||
| secrets: inherit # zizmor: ignore[secrets-inherit] | ||
| uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJSON(needs.java-static-build-matrix.outputs.matrix) }} | ||
| with: | ||
| build_type: ${{ inputs.build_type || 'branch' }} | ||
| branch: ${{ inputs.branch }} | ||
| date: ${{ inputs.date }} | ||
| sha: ${{ inputs.sha }} | ||
| node_type: "gpu-l4-latest-1" | ||
| arch: ${{ matrix.ARCH }} | ||
| # A plain CUDA-devel + dnf environment rather than rapidsai/ci-conda: see the matching | ||
| # comment in pr.yaml. | ||
| container_image: "rapidsai/ci-wheel:26.10-cuda${{ matrix.CUDA_VER }}-${{ matrix.LINUX_VER }}-py${{ matrix.PY_VER }}" | ||
| script: "ci/test_java_static.sh" | ||
|
|
||
| # Combines every classifier into one Maven-repository-layout artifact, which is the form a | ||
| # publishing workflow consumes. See rapidsai/build-infra#379. | ||
| java-static-gather: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The artifact from the last PR job run: https://github.com/NVIDIA/cuopt/actions/runs/35367985039/job/105683600034 has the following directory layout:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two separate things here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding 1 above, the path is valid for
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think we might have multiple packages under cuopt namespace, so I think other option is better. Is there a particular reason for RAPIDS to adhere to that standard ? |
||
| needs: [java-static-build] | ||
| runs-on: linux-amd64-cpu4 | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout code repo | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
| with: | ||
| ref: ${{ inputs.sha }} | ||
| persist-credentials: false | ||
| - name: Download per-classifier JAR artifacts | ||
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | ||
| with: | ||
| pattern: cuopt_java_* | ||
| path: ${{ runner.temp }}/jars | ||
| merge-multiple: true | ||
| - name: Assemble Maven repository layout | ||
| run: | | ||
| ./java/cuopt/ci/assemble_maven_repo.sh \ | ||
| --jars-dir "${RUNNER_TEMP}/jars" \ | ||
| --output-dir "${RUNNER_TEMP}/maven-repo" | ||
| - name: Upload combined Maven repository artifact | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: cuopt_java_maven_repo | ||
| path: ${{ runner.temp }}/maven-repo | ||
| if-no-files-found: error | ||
|
|
||
| java-build: | ||
| needs: cpp-build | ||
| permissions: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,10 @@ jobs: | |
| - conda-cpp-build | ||
| - conda-cpp-tests | ||
| - java-build | ||
| - java-static-build-matrix | ||
| - java-static-build | ||
| - java-static-test | ||
| - java-static-gather | ||
| - conda-python-build | ||
| - conda-python-tests | ||
| - docs-build | ||
|
|
@@ -113,6 +117,7 @@ jobs: | |
| - '!SECURITY.md' | ||
| - '!ci/build_wheel*.sh' | ||
| - '!ci/build_java.sh' | ||
| - '!ci/build_java_static.sh' | ||
| - '!ci/check_style.sh' | ||
| - '!ci/docker/**' | ||
| - '!ci/release/**' | ||
|
|
@@ -178,6 +183,7 @@ jobs: | |
| - '!agents/**' | ||
| - '!ci/build_docs.sh' | ||
| - '!ci/build_java.sh' | ||
| - '!ci/build_java_static.sh' | ||
| - '!ci/build_python.sh' | ||
| - '!ci/build_wheel*.sh' | ||
| - '!ci/check_style.sh' | ||
|
|
@@ -213,6 +219,7 @@ jobs: | |
| test_java: | ||
| - 'java/**' | ||
| - 'ci/build_java.sh' | ||
| - 'ci/build_java_static.sh' | ||
| - 'ci/test_java.sh' | ||
| - 'dependencies.yaml' | ||
| - '.github/workflows/pr.yaml' | ||
|
|
@@ -262,6 +269,7 @@ jobs: | |
| - '!agents/**' | ||
| - '!ci/build_docs.sh' | ||
| - '!ci/build_java.sh' | ||
| - '!ci/build_java_static.sh' | ||
| - '!ci/build_wheel*.sh' | ||
| - '!ci/check_style.sh' | ||
| - '!ci/docker/**' | ||
|
|
@@ -337,6 +345,7 @@ jobs: | |
| - '!ci/build_docs.sh' | ||
| - '!ci/build_python.sh' | ||
| - '!ci/build_java.sh' | ||
| - '!ci/build_java_static.sh' | ||
| - '!ci/check_style.sh' | ||
| - '!ci/docker/**' | ||
| - '!ci/release/**' | ||
|
|
@@ -477,6 +486,112 @@ jobs: | |
| artifact-name: "cuopt_docs" | ||
| container_image: "rapidsai/ci-conda:26.10-latest" | ||
| script: "ci/build_docs.sh" | ||
| # Compiles libcuopt from source (one self-contained classifier JAR per CUDA major and | ||
| # architecture), so this does not need conda-cpp-build. See #1817. | ||
| java-static-build-matrix: | ||
| needs: changed-files | ||
| permissions: | ||
| contents: read | ||
| uses: rapidsai/shared-workflows/.github/workflows/compute-matrix.yaml@main | ||
| if: >- | ||
| fromJSON(needs.changed-files.outputs.changed_file_groups).test_java || | ||
| fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp | ||
| with: | ||
| build_type: pull-request | ||
| matrix_name: conda-cpp-build | ||
| matrix_filter: 'map(. + {CUDA_MAJOR: (.CUDA_VER | split(".") | .[0])}) | unique_by([.ARCH, .CUDA_MAJOR])' | ||
|
|
||
| java-static-build: | ||
| needs: [java-static-build-matrix, changed-files] | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| id-token: write | ||
| packages: read | ||
| pull-requests: read | ||
| secrets: inherit # zizmor: ignore[secrets-inherit] | ||
| uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJSON(needs.java-static-build-matrix.outputs.matrix) }} | ||
| if: >- | ||
| fromJSON(needs.changed-files.outputs.changed_file_groups).test_java || | ||
| fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp | ||
| with: | ||
| build_type: pull-request | ||
| node_type: "cpu16" | ||
| arch: ${{ matrix.ARCH }} | ||
| # Must be CUDA-version-pinned, not the bare "26.10-latest" tag: custom-job.yaml has no | ||
| # per-matrix env passthrough, so an untagged image would leave RAPIDS_CUDA_VERSION at | ||
| # that image's single baked-in default for every matrix entry. | ||
| # ci/build_java_static.sh reads it to pick the conda CUDA toolkit, so every "cuN" classifier | ||
| # JAR would actually be built against the same CUDA version and just mislabeled. Matches | ||
| # the pattern conda-cpp-build.yaml itself uses for this image family. | ||
| container_image: "rapidsai/ci-conda:26.10-cuda${{ matrix.CUDA_VER }}-${{ matrix.LINUX_VER }}-py${{ matrix.PY_VER }}" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about using the wheel/Rocky 8 image here.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — same switch to |
||
| script: "ci/build_java_static.sh" | ||
| artifact-name: "cuopt_java_${{ matrix.ARCH }}_cu${{ matrix.CUDA_MAJOR }}" | ||
| file_to_upload: "java/cuopt/classifier-jars/" | ||
|
|
||
| # Runs the full Java suite against each classifier JAR on a GPU, with no libcuopt installed, | ||
| # so a JAR that loads but computes wrong answers fails here rather than at a user. | ||
| java-static-test: | ||
| needs: [java-static-build, java-static-build-matrix] | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| id-token: write | ||
| packages: read | ||
| pull-requests: read | ||
| secrets: inherit # zizmor: ignore[secrets-inherit] | ||
| uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJSON(needs.java-static-build-matrix.outputs.matrix) }} | ||
| with: | ||
| build_type: pull-request | ||
| node_type: "gpu-l4-latest-1" | ||
| arch: ${{ matrix.ARCH }} | ||
| # A plain CUDA-devel + dnf environment rather than rapidsai/ci-conda: the packaged JAR | ||
| # only needs a JDK, Maven and the CUDA runtime (libcublas/libcusparse, already in this | ||
| # image) to test, and a fresh `conda create` every run was slow enough that concurrent | ||
| # matrix jobs' cold Maven Central resolution reliably lined up and triggered 429s. | ||
| container_image: "rapidsai/ci-wheel:26.10-cuda${{ matrix.CUDA_VER }}-${{ matrix.LINUX_VER }}-py${{ matrix.PY_VER }}" | ||
| script: "ci/test_java_static.sh" | ||
| # Surefire fork-crash diagnostics (dumpstream/hs_err files) are also printed inline by | ||
| # the script on failure, but keep the raw reports downloadable too -- the JVM sometimes | ||
| # crashes without a clean dumpstream, and per-matrix-entry artifacts survive independently | ||
| # of how much of the console log GitHub keeps. | ||
| artifact-name: "cuopt_java_static_test_${{ matrix.ARCH }}_cu${{ matrix.CUDA_MAJOR }}" | ||
| file_to_upload: "java/cuopt/target/surefire-reports/" | ||
|
|
||
| java-static-gather: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This job can be temporarily added for testing but should not be part of
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — removed |
||
| needs: [java-static-build] | ||
| runs-on: linux-amd64-cpu4 | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout code repo | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Download per-classifier JAR artifacts | ||
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | ||
| with: | ||
| pattern: cuopt_java_* | ||
| path: ${{ runner.temp }}/jars | ||
| merge-multiple: true | ||
| - name: Assemble Maven repository layout | ||
| run: | | ||
| ./java/cuopt/ci/assemble_maven_repo.sh \ | ||
| --jars-dir "${RUNNER_TEMP}/jars" \ | ||
| --output-dir "${RUNNER_TEMP}/maven-repo" | ||
| - name: Upload combined Maven repository artifact | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: cuopt_java_maven_repo | ||
| path: ${{ runner.temp }}/maven-repo | ||
| if-no-files-found: error | ||
|
|
||
| java-build: | ||
| needs: [conda-cpp-build, changed-files] | ||
| permissions: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #!/bin/bash | ||
|
|
||
| # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| # Builds a self-contained Java classifier JAR and checks that it is actually self-contained. | ||
| # | ||
| # Unlike ci/build_java.sh, which installs a prebuilt libcuopt and links it as a shared library, | ||
| # this compiles libcuopt from source as a static archive and embeds it, so the JAR is the only | ||
| # thing a consumer installs. See #1817. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| if [[ -e /opt/conda/etc/profile.d/conda.sh ]]; then | ||
| . /opt/conda/etc/profile.d/conda.sh | ||
| fi | ||
|
|
||
| rapids-logger "Configuring conda strict channel priority" | ||
| conda config --set channel_priority strict | ||
|
|
||
| rapids-logger "Generating Java static build dependencies" | ||
| ENV_YAML_DIR=$(mktemp -d) | ||
| rapids-dependency-file-generator \ | ||
| --output conda \ | ||
| --file-key java_static \ | ||
| --matrix "cuda=${RAPIDS_CUDA_VERSION%.*};arch=$(arch)" | tee "${ENV_YAML_DIR}/env.yaml" | ||
|
|
||
| rapids-mamba-retry env create --yes -f "${ENV_YAML_DIR}/env.yaml" -n java_static | ||
|
|
||
| # Temporarily allow unbound variables for conda activation. | ||
| set +u | ||
| conda activate java_static | ||
| set -u | ||
|
|
||
| rapids-print-env | ||
|
|
||
| export CUOPT_PREFIX="${CONDA_PREFIX}" | ||
| STATIC_BUILD_DIR="${PWD}/cpp/build-static" | ||
| JNI_BUILD_DIR="${PWD}/java/cuopt/build/native-static" | ||
| JAR_OUTPUT_DIR="${PWD}/java/cuopt/classifier-jars" | ||
|
|
||
| rapids-logger "Building the scoped static libcuopt" | ||
| BUILD_DIR="${STATIC_BUILD_DIR}" bash java/cuopt/ci/build_static_libcuopt.sh | ||
|
|
||
| rapids-logger "Linking libcuopt into cuopt_jni" | ||
| cmake -S java/cuopt -B "${JNI_BUILD_DIR}" -GNinja \ | ||
| -DCMAKE_BUILD_TYPE=Release \ | ||
| -DCUOPT_PREFIX="${CUOPT_PREFIX}" \ | ||
| -DCUOPT_STATIC_BUILD_DIR="${STATIC_BUILD_DIR}" \ | ||
| -DCUOPT_EXTRA_INCLUDE_DIRS="${PWD}/cpp/include;${STATIC_BUILD_DIR}/include" | ||
| cmake --build "${JNI_BUILD_DIR}" --parallel "${PARALLEL_LEVEL:-$(nproc)}" | ||
|
|
||
| rapids-logger "Packaging the classifier JAR" | ||
| bash java/cuopt/ci/build_cuopt_java_jar.sh \ | ||
| --native-lib "${JNI_BUILD_DIR}/libcuopt_jni.so" \ | ||
| --cuda-version "${RAPIDS_CUDA_VERSION}" \ | ||
| --output-dir "${JAR_OUTPUT_DIR}" | ||
|
|
||
| # The JAR looking fine on this machine proves nothing: the build environment supplies every | ||
| # dependency by construction. This resolves them the way a consumer's machine would. | ||
| rapids-logger "Verifying the JAR is self-contained" | ||
| CLASSIFIER_JAR=$(find "${JAR_OUTPUT_DIR}" -name 'cuopt-*.jar' \ | ||
| ! -name '*-sources.jar' ! -name '*-javadoc.jar' -print -quit) | ||
| bash java/cuopt/ci/verify_jar_dependencies.sh --jar "${CLASSIFIER_JAR}" | ||
|
|
||
| # The gather job combines the classifier directories from every matrix entry into one Maven | ||
| # repository layout; this job uploads its own directory as-is. | ||
| rapids-logger "Result" | ||
| du -h "${CLASSIFIER_JAR}" | sed 's/^/ /' | ||
| find "${JAR_OUTPUT_DIR}" -type f | sed "s|^${JAR_OUTPUT_DIR}/| |" | sort |
Uh oh!
There was an error while loading. Please reload this page.