Skip to content

Align native UDF builds with jar dependency pins#632

Open
nvliyuan wants to merge 1 commit into
NVIDIA:mainfrom
nvliyuan:fix-native-udf-jar-pins-v2
Open

Align native UDF builds with jar dependency pins#632
nvliyuan wants to merge 1 commit into
NVIDIA:mainfrom
nvliyuan:fix-native-udf-jar-pins-v2

Conversation

@nvliyuan

@nvliyuan nvliyuan commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Resolve spark-rapids-jni and cuDF revisions from the selected rapids-4-spark jar, then fetch the matching cudf-pins and rapids-cmake entrypoint.
  • Build native UDF examples against jar-matched cuDF headers, RMM, CCCL, and rapids-cmake pins instead of moving branches.
  • Document the ABI alignment flow for prebuilt native UDF builds.

Closes #630

Test plan

  • bash -n examples/UDF-Examples/RAPIDS-accelerated-UDFs/resolve-jni-cudf-pins.sh
  • bash -n examples/UDF-Examples/RAPIDS-accelerated-UDFs/clone-cudf-repo.sh
  • bash -n examples/UDF-Examples/RAPIDS-accelerated-UDFs/extract-cudf-libs.sh
  • git diff --check
  • Checked Cursor lints for examples/UDF-Examples/RAPIDS-accelerated-UDFs.
  • On spark-yuanli, with ~/work/jars/v26.08/rapids-4-spark_2.12-26.08.0-SNAPSHOT-cuda12.jar, ran mvn package -Pudf-native-examples -Drapids4spark.version=26.08.0-SNAPSHOT -DskipTests inside the native UDF build container and produced target/rapids-4-spark-udf-examples_2.12-26.06.0-SNAPSHOT.jar.
  • Ran a JVM smoke test using the packaged UDF jar plus the v26.08 plugin jar; native StringWordCount completed with native udf completed rows=3.

Signed-off-by: liyuan <yuali@nvidia.com>
@nvliyuan nvliyuan requested a review from GaryShen2008 June 1, 2026 09:08
@nvliyuan

nvliyuan commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator Author

verified worked in local

@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the branch-name-based cuDF clone with a fully pinned dependency resolution flow: it extracts the embedded spark-rapids-jni and cudf-java version metadata from the rapids-4-spark jar, downloads the matching versions.json and RAPIDS.cmake from the JNI repo's raw GitHub URL, and passes those pinned artifacts into both the shell clone step and the CMake build.

  • resolve-jni-cudf-pins.sh (new) reads two embedded .properties files from the jar, validates the downloaded versions.json structure, and writes a jar-native-deps.properties file consumed by both extract-cudf-libs.sh and the Maven Ant cmake execution.
  • clone-cudf-repo.sh is generalized from a branch-only shallow clone to a --filter=blob:none + fetch --depth 1 origin <ref> flow that accepts any git ref (branch, tag, or commit SHA) and compares FETCH_HEAD against HEAD to avoid unnecessary re-checkouts.
  • CMakeLists.txt gains RAPIDS_CMAKE_FILE and RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE cache variables for the pre-downloaded entrypoint and versions override; the SHA-detection block that sets rapids-cmake-sha/rapids-cmake-fetch-via-git is dead code since neither variable is consumed downstream.

Confidence Score: 3/5

Core pin-resolution and cmake integration work end-to-end for the happy path, but three unaddressed issues from prior review threads (CPM override never applied, unconditional python3 call after unzip fallback, awk URL truncation) remain unresolved in this revision.

The three issues carried over from previous review threads — the CPM override file being validated but silently ignored by rapids_cpm_init(), the python3-required validation block that breaks unzip-only hosts, and the awk -F= URL truncation — are substantive gaps in the ABI-alignment guarantee this PR is trying to establish. The new dead-code SHA-detection block and the unguarded cmake property load on a fresh skipCudfExtraction=true invocation are additional rough edges. The flow is architecturally sound and tested on the happy path, but the CPM override being a no-op means the advertised CCCL/RMM pin alignment is not actually enforced in the prebuilt build path.

resolve-jni-cudf-pins.sh (python3 fallback and awk truncation), src/main/cpp/CMakeLists.txt (CPM override never passed to rapids_cpm_init, dead SHA-detection variables), pom.xml (unguarded cmake property load)

Important Files Changed

Filename Overview
examples/UDF-Examples/RAPIDS-accelerated-UDFs/resolve-jni-cudf-pins.sh New script that extracts spark-rapids-jni/cuDF pins from the jar and downloads matching versions.json and RAPIDS.cmake; contains a python3-required validation block that can fail when the unzip fallback was used, and an awk -F= URL parsing path that truncates values containing '='
examples/UDF-Examples/RAPIDS-accelerated-UDFs/src/main/cpp/CMakeLists.txt Adds RAPIDS_CMAKE_BRANCH/FILE/CPM_OVERRIDE_VERSION_FILE cache variables and a local-file include path for pre-downloaded RAPIDS.cmake; the SHA-detection block sets rapids-cmake-sha and rapids-cmake-fetch-via-git variables that are never used, and RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE is validated but not passed to rapids_cpm_init()
examples/UDF-Examples/RAPIDS-accelerated-UDFs/pom.xml Adds resolve-jni-cudf-pins.sh invocation and property loading in the extract phase, then passes jar.rapids.cmake.* values to cmake in a separate execution; if jar-native-deps.properties is absent (e.g. skipCudfExtraction=true on a fresh checkout), cmake receives literal Ant property token strings and fails with a confusing error
examples/UDF-Examples/RAPIDS-accelerated-UDFs/clone-cudf-repo.sh Generalized from branch-based shallow clone to arbitrary git-ref checkout using filter=blob:none + fetch --depth 1; correctly handles both new and existing repos by comparing HEAD to FETCH_HEAD
examples/UDF-Examples/RAPIDS-accelerated-UDFs/extract-cudf-libs.sh Adds RAPIDS_JAR_PATH env-var override for explicit jar path, makes TARGET_DIR overridable, and delegates pin resolution + cuDF clone to the new scripts; logic is sound
examples/UDF-Examples/RAPIDS-accelerated-UDFs/README.md Documentation updated to reflect the new pin-resolution flow and expanded stale-artifact cleanup instructions

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Maven
    participant extract as extract-cudf-libs.sh
    participant resolve as resolve-jni-cudf-pins.sh
    participant GH as raw.githubusercontent.com
    participant clone as clone-cudf-repo.sh
    participant cmake as CMake

    Maven->>extract: mvn package -Pudf-native-examples
    extract->>extract: Locate rapids-4-spark jar
    extract->>resolve: resolve-jni-cudf-pins.sh JAR PINS_DIR PROPS_FILE
    resolve->>resolve: Read spark-rapids-jni revision+url from jar
    resolve->>resolve: Read cudf-java revision from jar
    resolve->>GH: Download versions.json (JNI_REVISION/thirdparty/cudf-pins/)
    resolve->>GH: Download rapids-cmake.sha
    resolve->>resolve: Validate versions.json (CCCL, rmm present)
    resolve->>GH: Download RAPIDS.cmake (at rapids-cmake SHA)
    resolve->>resolve: Write jar-native-deps.properties
    extract->>clone: clone-cudf-repo.sh CUDF_DIR CUDF_REVISION
    clone->>clone: "git clone --filter=blob:none --no-checkout"
    clone->>clone: git fetch --depth 1 origin CUDF_REVISION
    clone->>clone: git checkout --detach FETCH_HEAD
    Maven->>cmake: "cmake -DRAPIDS_CMAKE_FILE=... -DRAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE=..."
    cmake->>cmake: include(RAPIDS.cmake) from pre-downloaded file
    cmake->>cmake: rapids_cpm_init()
    cmake->>cmake: rapids_cpm_cccl() / rapids_cpm_rmm()
    cmake->>cmake: Link prebuilt libcudf.so
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Maven
    participant extract as extract-cudf-libs.sh
    participant resolve as resolve-jni-cudf-pins.sh
    participant GH as raw.githubusercontent.com
    participant clone as clone-cudf-repo.sh
    participant cmake as CMake

    Maven->>extract: mvn package -Pudf-native-examples
    extract->>extract: Locate rapids-4-spark jar
    extract->>resolve: resolve-jni-cudf-pins.sh JAR PINS_DIR PROPS_FILE
    resolve->>resolve: Read spark-rapids-jni revision+url from jar
    resolve->>resolve: Read cudf-java revision from jar
    resolve->>GH: Download versions.json (JNI_REVISION/thirdparty/cudf-pins/)
    resolve->>GH: Download rapids-cmake.sha
    resolve->>resolve: Validate versions.json (CCCL, rmm present)
    resolve->>GH: Download RAPIDS.cmake (at rapids-cmake SHA)
    resolve->>resolve: Write jar-native-deps.properties
    extract->>clone: clone-cudf-repo.sh CUDF_DIR CUDF_REVISION
    clone->>clone: "git clone --filter=blob:none --no-checkout"
    clone->>clone: git fetch --depth 1 origin CUDF_REVISION
    clone->>clone: git checkout --detach FETCH_HEAD
    Maven->>cmake: "cmake -DRAPIDS_CMAKE_FILE=... -DRAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE=..."
    cmake->>cmake: include(RAPIDS.cmake) from pre-downloaded file
    cmake->>cmake: rapids_cpm_init()
    cmake->>cmake: rapids_cpm_cccl() / rapids_cpm_rmm()
    cmake->>cmake: Link prebuilt libcudf.so
Loading

Reviews (1): Last reviewed commit: "Align native UDF builds with jar depende..." | Re-trigger Greptile

Comment on lines +75 to +82
if(RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE)
if(NOT EXISTS "${RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE}")
message(FATAL_ERROR
"RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE does not exist: "
"${RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE}")
endif()
message(STATUS "Using rapids-cmake package override: ${RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE}")
endif()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 RAPIDS_CMAKE_CPM_OVERRIDE_VERSION_FILE validated but never applied

The variable is checked for existence and logged (lines 75–82), but rapids_cpm_init() is called later (line 281 in the SHOULD_USE_PREBUILT path) with no OVERRIDE argument. As a result, rapids_cpm_cccl() and rapids_cpm_rmm() resolve package versions from the defaults baked into the downloaded rapids-cmake, not from the jar-matched versions.json. The override file is effectively a no-op and the whole ABI-pin flow silently fails to apply its pins for CCCL and RMM.

Comment on lines +125 to +153
python3 - "$VERSIONS_FILE" <<'PY'
import json
import sys

versions_file = sys.argv[1]
with open(versions_file, encoding="utf-8") as fh:
data = json.load(fh)

packages = data.get("packages")
if not isinstance(packages, dict) or not packages:
raise SystemExit(f"ERROR: {versions_file} does not contain a non-empty packages map")

missing_metadata = []
for name, package in sorted(packages.items()):
if "version" not in package:
missing_metadata.append(f"{name}: missing version")
has_git_source = "git_url" in package and "git_tag" in package
has_url_source = "url" in package and "url_hash" in package
if not (has_git_source or has_url_source):
missing_metadata.append(f"{name}: missing pinned git/url source")

if missing_metadata:
raise SystemExit("ERROR: invalid cudf-pins metadata:\n " + "\n ".join(missing_metadata))

required = ["CCCL", "rmm"]
missing_required = [name for name in required if name not in packages]
if missing_required:
raise SystemExit("ERROR: cudf-pins missing required packages: " + ", ".join(missing_required))
PY

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Validation block unconditionally requires python3 despite unzip fallback

read_property_from_jar (lines 40–63) handles environments where only unzip is installed. However, the versions.json validation inline script on line 125 calls python3 directly without any availability check. On a host where python3 is absent but unzip is present, property extraction succeeds, both files are downloaded, and then the build exits with an opaque python3: command not found (exit 127) rather than a meaningful diagnostic.

break
PY
elif command -v unzip >/dev/null 2>&1; then
unzip -p "$JAR_PATH" "$entry" 2>/dev/null | awk -F= -v key="$property" '$1 == key {print $2; exit}'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 awk fallback truncates property values containing =

The unzip fallback uses -F= and prints only $2, so a property value containing = is silently truncated. The url property is extracted this way; a query-string = in the JNI repository URL would produce a wrong RAW_BASE and silently download from an incorrect path.

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.

Align native UDF build dependencies with rapids-4-spark jar pins

2 participants