Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,73 @@ jobs:
with:
push: true
cuda: '["12.9", "13.3"]'
# Build the cuDF Java JAR for every Maven classifier.
java-build:
needs: [telemetry-setup]
strategy:
fail-fast: false
matrix:
include:
- { cuda: "12.9", cuda_major: "12", arch: "x86_64", runner: "linux-amd64-cpu16" }
- { cuda: "13.3", cuda_major: "13", arch: "x86_64", runner: "linux-amd64-cpu16" }
- { cuda: "12.9", cuda_major: "12", arch: "aarch64", runner: "linux-arm64-cpu16" }
- { cuda: "13.3", cuda_major: "13", arch: "aarch64", runner: "linux-arm64-cpu16" }
runs-on: ${{ matrix.runner }}
permissions:
contents: read
steps:
- name: Checkout code repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ inputs.sha }}
fetch-depth: 0
persist-credentials: false
- name: Build static libcudf
run: |
./java/ci/build_static_libcudf.sh \
--output-dir "${RUNNER_TEMP}/libcudf" \
--cuda-version "${{ matrix.cuda }}"
- name: Build cuDF Java JAR
run: |
./java/ci/build_cudf_java_jar.sh \
--libcudf-dir "${RUNNER_TEMP}/libcudf" \
--output-dir "${RUNNER_TEMP}/jars" \
--cuda-version "${{ matrix.cuda }}"
- name: Upload per-entry JAR artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: cudf_java_cuda${{ matrix.cuda_major }}_${{ matrix.arch }}
# Ship only the JARs and POMs. Exclude the per-classifier Maven build scratch dir.
path: |
${{ runner.temp }}/jars
!${{ runner.temp }}/jars/.mvn-temp-target
if-no-files-found: error
# Assemble the per-classifier JARs into one Maven-repository layout.
java-gather:
needs: [java-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-entry JAR artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: cudf_java_cuda*
path: ${{ runner.temp }}/jars
merge-multiple: true
- name: Assemble Maven repository layout
run: |
./java/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: cudf_java_maven_repo
path: ${{ runner.temp }}/maven-repo
if-no-files-found: error
24 changes: 24 additions & 0 deletions dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ files:
- depends_on_cudf_polars
- depends_on_ray
- depends_on_cudf_streaming
build_java:
# Toolchain for building static libcudf from source and packaging the cuDF
# Java JAR. `depends_on_libcudf` is excluded because the Java build links
# against a static libcudf built from source, not a conda shared libcudf.
output: none
includes:
- build_base
- build_all
- build_cpp
- cuda
- cuda_static
- cuda_version
- build_java
test_java:
output: none
includes:
Expand Down Expand Up @@ -632,6 +645,17 @@ dependencies:
- croaring==4.4.2
- flatbuffers==24.3.25
- librdkafka<2.15.0a0
build_java:
common:
- output_types: conda
packages:
- boost
# cuda_profiler_api.h is used by the JNI layer (CudaJni.cpp) but is
# not pulled in by the base `cuda` dev packages.
- cuda-profiler-api
- make
- maven
- openjdk=8.*
depends_on_libnvcomp:
common:
- output_types: conda
Expand Down
107 changes: 94 additions & 13 deletions java/ci/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,100 @@
# Build Jar artifact of cuDF

## Build the docker image
## Recommended: self-contained release build scripts

### Prerequisite
The scripts under `java/ci/` build the cuDF Java JAR for every Maven classifier the
same way locally and in CI (GitHub Actions is only a thin wrapper that adds
artifact upload/download). Each script pulls the RAPIDS `ci-conda` build image,
runs the build in a throwaway container, and writes its output to a host
directory. No local `docker build` is required, and no GPU is required to build.

1. Docker should be installed.
2. [nvidia-docker](https://github.com/NVIDIA/nvidia-docker) should be installed.
### Prerequisites

1. Docker is installed and the current user can run `docker`.
2. Network access to pull `rapidsai/ci-conda:<rapids_version>-latest`.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
### Local one-command shortcut

For local testing only, `java/ci/test_java_build_local.sh` runs Steps 1-3 end-to-end for both CUDA 12 and CUDA 13 on the host architecture.

```bash
./java/ci/test_java_build_local.sh --work-dir /tmp/java-build-test
```

### Step 1 - Build the static libcudf install tree

```bash
./java/ci/build_static_libcudf.sh --output-dir /tmp/libcudf-cuda12 --cuda-version 12.9
```

This produces a static libcudf install tree (`lib/libcudf.a` plus its static
dependencies) under the given output directory. Build outputs are host-user-owned
so plain `rm -rf` works.

### Step 2 - Package the cuDF Java JAR for one classifier

```bash
./java/ci/build_cudf_java_jar.sh \
--libcudf-dir /tmp/libcudf-cuda12 \
--output-dir /tmp/jars \
--cuda-version 12.9
```

This compiles the JNI layer against the static libcudf from Step 1 and emits a
single classifier JAR (e.g. `cudf-26.08.0-SNAPSHOT-cuda12.jar`) plus its POM
into a classifier-named subdirectory under `--output-dir`:

```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Label the directory-tree code fences.

markdownlint flags both fences as MD040. Use text for each opening fence.

Also applies to: 75-75

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 47-47: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@java/ci/README.md` at line 47, Update both directory-tree code fences in the
README to specify the text language on their opening fences, resolving the MD040
markdownlint violations.

Source: Linters/SAST tools

/tmp/jars/cuda12/
cudf-26.08.0-SNAPSHOT-cuda12.jar
cudf-26.08.0-SNAPSHOT.pom
```

The classifier is derived from `--cuda-version` (major) + host arch (`uname
-m`): `cuda12` / `cuda13` on `x86_64`, `cuda12-arm64` / `cuda13-arm64` on
`aarch64`. Producing the ARM classifiers requires a real `aarch64` host.
Repeat Step 2 for each classifier, pointing `--libcudf-dir` at the matching
static libcudf tree and using the same `--output-dir` (each classifier lands
in its own subdirectory). Concurrent invocations for different classifiers
are safe because each nests its own bind-mount over `/repo/java/target`
inside the container.

### Step 3 - Assemble the Maven repository layout

```bash
./java/ci/assemble_maven_repo.sh \
--jars-dir /tmp/jars \
--output-dir /tmp/maven-repo
```

This walks every subdirectory of `--jars-dir` (each subdir name IS the
classifier), gathers the per-classifier JAR and shared POM, derives the
artifact version from the JAR filenames (requiring a single unique version
across subdirs), and lays them out as:

```
/tmp/maven-repo/ai/rapids/cudf/26.08.0-SNAPSHOT/
cudf-26.08.0-SNAPSHOT-cuda12.jar
cudf-26.08.0-SNAPSHOT-cuda13.jar
cudf-26.08.0-SNAPSHOT.pom
```

The set of classifiers is whatever subdirectories are present under
`--jars-dir`. For a local `x86_64`-only run, populate `/tmp/jars/cuda12/`
and `/tmp/jars/cuda13/`. For the full four-way release build, add
`/tmp/jars/cuda12-arm64/` and `/tmp/jars/cuda13-arm64/`.

In GitHub Actions (`.github/workflows/build.yaml`), the `java-build` matrix job
runs Steps 1-2 per (CUDA x arch) entry and uploads each classifier subdir as a
per-entry artifact. The separate `java-gather` job downloads them (with
`merge-multiple: true`, so all subdirs land in a single parent dir), runs
Step 3, and uploads the combined `cudf_java_maven_repo` artifact.

## Legacy: manual Dockerfile.rocky build (obsolete)

> The `java/ci/Dockerfile.rocky` + `java/ci/build-in-docker.sh` flow below is the
> old build path. It is retained for reference but superseded by the
> self-contained scripts above.

### Build the docker image

Expand All @@ -20,32 +109,24 @@ The following CUDA versions are supported w/ CUDA Enhanced Compatibility:
Change the --build-arg CUDA_VERSION to what you need.
You can replace the tag "cudf-build:12.9.1-devel-rocky8" with another name you like.

## Start the docker then build

### Start the docker
### Start the docker then build

Run below command to start a docker container with GPU.
```bash
nvidia-docker run -it cudf-build:12.9.1-devel-rocky8 bash
```

### Download the cuDF source code

You can download the cuDF repo in the docker container or you can mount it into the container.
Here I choose to download again in the container.
```bash
git clone --recursive https://github.com/rapidsai/cudf.git -b main
```

### Build cuDF jar with devtoolset

```bash
cd cudf
export WORKSPACE=`pwd`
source java/ci/env.sh
${sclCMD} "java/ci/build-in-docker.sh"
```

### The output

You can find the cuDF jar in java/target/ like cudf-26.08.0-SNAPSHOT-cuda12.jar.
36 changes: 36 additions & 0 deletions java/ci/argparse.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Shared argparse helpers for the java/ci/ host orchestrator scripts.
# Meant to be sourced, not executed:
# . "${SCRIPT_DIR}/argparse.sh"

# require_value <flag_name> <value>
# Check: exit 1 when <value> is empty (i.e. the flag was passed
# without its argument, or was the last token on the command line).
require_value() {
local flag=$1
local value=$2
if [[ -z ${value} ]]; then
echo "Error: ${flag} requires a value" >&2
exit 1
fi
}

# require_arg <flag_name> <value>
# Check: assert that a required flag was actually supplied by the
# caller. Prints the script's print_help (if defined) then exits 1 on failure.
# Preserves the existing behavior of showing help after a "required flag missing"
# error.
require_arg() {
local flag=$1
local value=$2
if [[ -z ${value} ]]; then
echo "Error: ${flag} is required." >&2
if declare -F print_help > /dev/null; then
print_help
fi
exit 1
fi
}
Loading
Loading