☰
cuDF Java JAR 构建与发布流水线:基于 ci-wheel 容器的一站式打包实践
2026/9/25 3:43:41 网站建设 项目流程
  • 数据分析
  • 数据工程
  • 机器学习

【免费下载链接】cudf

cuDF - GPU DataFrame Library

项目地址:https://gitcode.com/gh_mirrors/cu/cudf
点击查看免费下载

cuDF 的 Java API 依赖一个内嵌静态 libcudf 的 JNI 层,其打包过程涉及静态库编译、Maven classifier 管理和多架构发布。本文基于 java/ci/README.md 完整解析java/ci/目录下自包含构建脚本的三步流水线——静态 libcudf 构建、单 classifier JAR 打包、Maven 仓库布局组装,并结合各脚本源码说明其参数语义、并发隔离机制与版本策略,读完后可在本地一条命令复刻 CI 的完整 Java 发布产物。

流水线设计总览

java/ci/下的脚本在本地与 CI 中以完全相同的方式构建 cuDF Java JAR,覆盖所有 Maven classifier;GitHub Actions 只是一个薄封装,额外提供产物的上传/下载。每个脚本的工作模式一致:

  • 拉取 RAPIDS 的ci-wheel构建镜像(无需本地docker build);
  • 在一次性(throwaway)容器中执行构建;
  • 输出写回宿主目录,且构建产物归宿主用户所有(容器退出时chown),因此宿主机上可以直接rm -rf清理。

镜像标签由 ci_wheel_image.sh 中的cudf_java_ci_wheel_image函数生成:取仓库根目录VERSION文件前两段(当前仓库为26.12)拼接 CUDA 完整版本号与 Python 版本,得到形如rapidsai/ci-wheel:26.12-cuda12.9.2-rockylinux8-py3.11的标签。注意--cuda-version必须是镜像标签中使用的完整 toolkit 版本(如12.9.2),且RAPIDS_PY_VERSION环境变量可覆盖默认的 Python 版本3.11。

前置条件

  1. 已安装 Docker,且当前用户可执行docker;
  2. 具备拉取rapidsai/ci-wheel:<rapids>-cuda<ver>-rockylinux8-py3.11镜像的网络访问权限。

构建无需 GPU。只有第 3 步之后的"打包 JAR 测试"环节才需要 GPU。

脚本职责划分

脚本角色
build_static_libcudf.sh宿主编排:拉镜像、挂卷、起容器,产出静态 libcudf 安装树
build_static_libcudf_in_container.sh容器内实际执行 CMake 配置/构建/安装
build_cudf_java_jar.sh宿主编排:针对单个 classifier 打包 JAR + POM
build_cudf_java_jar_in_container.sh容器内执行 Maven 打包,含发布版 POM 改写/还原
assemble_maven_repo.sh将各 classifier 产物聚合成标准 Maven 仓库目录
test_java_build_local.sh本地端到端跑通 Step 1–3(CUDA 12 + 13 双构建)
test_packaged_java_local.sh在 GPU 容器中对打包好的 JAR 跑 Java 测试
java_classifier.sh、argparse.sh、ci_wheel_image.sh被 source 的公共辅助函数(classifier 推导、参数校验、镜像标签)

Step 1:构建静态 libcudf 安装树

./java/ci/build_static_libcudf.sh --output-dir /tmp/libcudf-cuda12 --cuda-version 12.9.2

该步骤在给定输出目录下产出一个静态 libcudf 安装树:lib/libcudf.a及其全部静态依赖。脚本执行结束后会显式校验lib/libcudf.a或lib64/libcudf.a存在,否则以非零码退出(见 build_static_libcudf.sh#L137-L142)。

参数说明

参数必填说明
-o, --output-dir是宿主机接收静态安装树的目录
-c, --cuda-version是CUDA toolkit 完整版本(如12.9.2),必须与某个rapidsai/ci-wheel镜像标签匹配
-A, --cmake-cuda-architectures否覆盖 CUDA 架构列表(如"80"或"80;90")。不设置时使用 cuDF 默认的 RAPIDS 全架构列表。⚠️ 后续打包 JAR 时必须传相同的值,否则libcudfjni.so对libcudf.a的 device 链接会失败
-j, --parallel否构建并行度,默认nproc

容器内构建的关键 CMake 配置

容器内脚本 build_static_libcudf_in_container.sh#L51-L68 揭示了静态树的具体形态,这些配置决定了为什么该树可以直接打进 JAR:

  • -DBUILD_SHARED_LIBS=OFF:产物为libcudf.a静态库;
  • -DCUDF_USE_ARROW_STATIC=ON:Arrow 也以静态形式并入;
  • -DCUDF_ENABLE_ARROW_S3=OFF、-DCUDF_KVIKIO_REMOTE_IO=OFF:关闭远程 IO,产物不依赖对象存储客户端;
  • -DCUDF_LARGE_STRINGS_DISABLED=ON:关闭 large strings 特性(与后文 Java 测试中LIBCUDF_LARGE_STRINGS_ENABLED=0保持一致);
  • -DCUDF_USE_PER_THREAD_DEFAULT_STREAM=ON:启用 per-thread default stream 语义;
  • 若检测到sccache,还会通过CMAKE_*_COMPILER_LAUNCHER转发编译器缓存(工具链由 setup_java_env.sh 统一导出)。

宿主脚本通过 Docker 环境变量把配置传入容器:仓库挂载到/repo、输出目录挂载到/output,并注入HOST_UID/HOST_GID——容器退出时的trap会用它们chown -R安装树,保证产物归宿主用户所有(build_static_libcudf_in_container.sh#L42-L49)。

Step 2:为单个 classifier 打包 cuDF Java JAR

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

该脚本消费 Step 1 的静态 libcudf 树,在容器内编译 JNI 层并输出三种产物到--output-dir下以 classifier 命名的子目录:

/tmp/jars/cuda12/ cudf-26.12.0-SNAPSHOT-cuda12.jar cudf-26.12.0-SNAPSHOT.pom

外加 classifier 无关的 sources jar 与 javadoc jar。

classifier 是如何决定的

classifier 由--cuda-version的主版本号 + 宿主架构(uname -m)推导,逻辑集中在 java_classifier.sh#L11-L28:

宿主架构CUDA 12.xCUDA 13.x
x86_64cuda12cuda13
aarch64/arm64cuda12-arm64cuda13-arm64

同一逻辑也存在于java/pom.xml的 Groovy 脚本中(依据os.arch生成cuda.classifier属性,供maven-jar-plugin使用),脚本注释明确要求两者镜像一致——因此生产 ARM classifier 必须在真实的 aarch64 宿主机上执行。

输出布局与并发安全

脚本刻意保持"布局无关":只负责产出一个 classifier 的产物,不知道组合仓库布局(那由 Step 3 负责)。几个值得注意的实现细节(build_cudf_java_jar.sh#L140-L158):

  • <output-dir>/<classifier>/若已存在则直接报错退出,要求先清理,避免陈旧产物混入;
  • 为每次调用创建独立的 scratch 目录.mvn-temp-target/<classifier>,并以嵌套 bind-mount 覆盖容器内的/repo/java/target。这样不同 classifier 的 SNAPSHOT 构建可以并发而互不干扰;.mvn-temp-target/前缀使该目录对assemble_maven_repo.sh的*/glob 发现逻辑不可见;
  • 容器内 Maven 无法清理 bind-mount 挂载点(rmdir会报 EBUSY),所以宿主包装脚本每次启动前都会重建该 scratch 目录,保证干净的target/起点;
  • 构建完成后调用cudf_java_assert_classifier_artifacts断言输出目录中恰好有一个 classifier JAR 与一个 POM(java_classifier.sh#L55-L81),用于捕获 POM 漂移。

限制条件:发布版构建会改写共享的java/pom.xml,因此多个发布版构建不能重叠执行(SNAPSHOT 并发安全,release 不并发安全)。

重复执行 Step 2 即可产出全部 classifier,每次将--libcudf-dir指向对应的静态 libcudf 树、复用同一个--output-dir,每个 classifier 自动落入各自子目录。

容器内 Maven 打包与版本策略

容器内脚本 build_cudf_java_jar_in_container.sh 的BUILD_ARG揭示了完整的 Maven 构建参数:

  • -DCUDF_JNI_LIBCUDF_STATIC=ON:JNI 层静态链接 libcudf(呼应 Step 1 的静态树);
  • -DskipTests=true:打包阶段跳过测试(打包 JAR 的测试由专门流程执行,见下文);
  • -Prelease:附加 sources jar;-Pjavadoc-jdk17:用 JDK 17 的 javadoc 构建 javadoc jar——两者均为 Maven Central 发布所必需;
  • 工具链与 sccache launcher 经-Dcmake.ccache.opts传给 pom 中的 CMake 调用。

Step 3:组装 Maven 仓库布局

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

该脚本遍历--jars-dir的每一个子目录(子目录名即 classifier),收集每个 classifier 的 JAR、一份共享 sources jar、一份共享 javadoc jar 与共享 POM,并以cuda12classifier 的副本作为无 classifier 的主 JAR(Maven Central 会把它服务给未指定 classifier 的ai.rapids:cudf消费者)。版本号从 JAR 文件名推导,且要求所有子目录的版本一致,否则快速失败。最终布局:

/tmp/maven-repo/ai/rapids/cudf/<CUDF_VERSION>-SNAPSHOT/ cudf-<CUDF_VERSION>-SNAPSHOT.jar cudf-<CUDF_VERSION>-SNAPSHOT-cuda12.jar cudf-<CUDF_VERSION>-SNAPSHOT-cuda13.jar cudf-<CUDF_VERSION>-SNAPSHOT-sources.jar cudf-<CUDF_VERSION>-SNAPSHOT-javadoc.jar cudf-<CUDF_VERSION>-SNAPSHOT.pom

输入集合的规则(来自 assemble_maven_repo.sh):

  • classifier 集合 =--jars-dir下实际存在的子目录集合;
  • 本地仅x86_64运行时,填充/tmp/jars/cuda12/与/tmp/jars/cuda13/;完整的四向发布构建需追加/tmp/jars/cuda12-arm64/与/tmp/jars/cuda13-arm64/;
  • cuda12子目录是必需的,因为无 classifier 主 JAR 从它复制而来——仅含 aarch64 子目录的集合不是合法的 gather 输入;
  • sources/javadoc jar 要求每个 classifier 子目录都存在(缺失说明该次构建未激活-Prelease或-Pjavadoc-jdk17),脚本取字典序第一个子目录的副本作为规范版本;
  • 组装未完成即异常退出时,trap会删除部分写出的输出目录,避免留下半成品仓库。

Release Tag 与 SNAPSHOT 版本策略

  • Release tag 触发(GITHUB_REF=refs/tags/vYY.MM.PP)的 CI 运行产出发布版 JAR(cudf-<CUDF_VERSION>-*.jar);其余一切运行产出-SNAPSHOT版本。判定由rapids-is-release-build工具门控,GITHUB_REF为可选:未设置或取值不是 tag 时保持 SNAPSHOT。
  • 容器内脚本的对应逻辑:release 构建先用mvn versions:set剥离-SNAPSHOT并重写 POM(先备份pom.xml.backup),退出 trap 保证无条件还原java/pom.xml;非 release 构建则反向校验 POM 必须携带-SNAPSHOT(否则快速失败,避免把发布版号发到 Sonatype snapshots)。
  • 本地演练 release 路径:
GITHUB_REF=refs/tags/vYY.MM.PP ./java/ci/test_java_build_local.sh

该脚本会为打包就地重写java/pom.xml,退出时自动还原。

GitHub Actions 集成

在 GitHub Actions(.github/workflows/build.yaml)中:

  1. java-build矩阵任务按 (CUDA × 架构) 每个条目分别执行 Step 1–2,并将每个 classifier 子目录上传为该条目的 artifact;
  2. 独立的java-gather任务用merge-multiple: true下载所有条目(全部子目录汇入同一个父目录),执行 Step 3,上传合并后的cudf_java_maven_repoartifact;
  3. java-publish任务把组装好的仓库交给共享工作流maven-publish.yaml,按rapids-is-release-build路由:release tag 发 Maven Central,否则发 Sonatype snapshots。

这套设计让"矩阵并行构建 + 单一聚合发布"与本地三步脚本完全同构,本地输出即可作为 CI 行为的可靠预演。

本地一键端到端验证

仅用于本地测试时,test_java_build_local.sh 一条命令在本机架构上端到端跑完 Step 1–3,覆盖 CUDA 12(脚本内固定12.9.2)与 CUDA 13(13.3.0):

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

从源码看(test_java_build_local.sh#L33-L36),两个 CUDA 版本常量要求与 CI 的java-build矩阵保持同步。脚本的关键行为:

  • 并行策略:两个静态 libcudf 构建并行、两个 JAR 构建并行;每个并发构建获得--parallel/2的并行度,防止两个 nvcc 并发运行造成内存压力;
  • GPU 架构自动检测:未显式传-A时,通过nvidia-smi --query-gpu=compute_cap自动探测本机 GPU 的 compute capability(如 Ampere →"80")以加速构建;字面值"all"是哨兵,表示"不向子脚本传递该参数",子脚本回退到 cuDF 默认的 RAPIDS 全架构列表(慢但可在无 GPU 宿主上正确构建);
  • 工作目录结构:<work-dir>/libcudf-cuda12、<work-dir>/libcudf-cuda13(静态树)、<work-dir>/jars/<classifier>(各 classifier 的 JAR + POM)、<work-dir>/maven-repo(组合仓库),日志分别落在<work-dir>/logs/{static,jar}_cuda{12,13}.log;每次启动先删除上次的输出子树,防止 cmake/mvn 看到陈旧产物;
  • 结束时打印各步骤耗时与总墙钟时间。

显式覆盖示例:

# 指定单架构,快速构建 ./java/ci/test_java_build_local.sh --work-dir /tmp/java-build-test \ --cmake-cuda-architectures 80 # 完整 RAPIDS 架构列表(慢,适合无 GPU 宿主) ./java/ci/test_java_build_local.sh --work-dir /tmp/java-build-test \ --cmake-cuda-architectures all

注意:每次调用只覆盖宿主架构——x86_64上产出cuda12/cuda13,aarch64上产出cuda12-arm64/cuda13-arm64(arm64后缀由子脚本依据uname -m自动追加)。覆盖全部四个发布 classifier 需在两种架构上各跑一次。

对打包 JAR 运行测试(需要 GPU + Docker)

普通的cd java && mvn test运行的是本地编译的target/classes,不会验证 classifier JAR 的打包正确性。要针对打包产物跑测试,使用:

./java/ci/test_packaged_java_local.sh --work-dir /tmp/java-build-test

或 CI 入口 ci/test_packaged_java.sh。工作机制(结合 test_packaged_java_local.sh 与 ci/test_packaged_java.sh):

  • 从--work-dir(即test_java_build_local.sh的输出)中解析 classifier JAR:按RAPIDS_CUDA_VERSION(默认12.9.2)推导 classifier,在<work-dir>/jars/<classifier>/下定位唯一的cudf-*-<classifier>.jar;
  • 以--gpus all启动 ci-wheel 容器,将 JAR 只读挂载为/product/cudf.jar,注入JAVA_JAR=/product/cudf.jar与LIBCUDF_LARGE_STRINGS_ENABLED=0;
  • 容器内实际执行timeout 30m mvn -B test -Ppackaged-jar-tests -Dcudf.jar.path=<jar>:packaged-jar-testsprofile(定义于 java/pom.xml)把打包 JAR 放入 surefire classpath 替代本地编译产物,跳过 native 复制与主源码编译,并额外运行PackagedJarOriginCheck测试(普通mvn test的 surefire 配置默认排除NativeDepsLoaderTest与PackagedJarOriginCheck);
  • 若JAVA_JAR未设置,CI 入口会改用rapids-download-from-github从匹配的java-buildartifact(命名cudf_java_<amd64|arm64>_cu<major>)下载 JAR 后再测试。

遗留路径:手动 Dockerfile.rocky 构建(已废弃)

java/ci/Dockerfile.rocky +build-in-docker.sh流程是旧构建路径,保留仅作参考,已被上述自包含脚本取代。

旧流程需要在 cuDF 仓库根目录先构建镜像:

docker build -f java/ci/Dockerfile.rocky --build-arg CUDA_VERSION=12.9.1 -t cudf-build:12.9.1-devel-rocky8 .

依赖 CUDA Enhanced Compatibility,支持 CUDA 12.2 及以上;可修改--build-arg CUDA_VERSION与镜像标签。随后启动带 GPU 的容器:

nvidia-docker run -it cudf-build:12.9.1-devel-rocky8 bash

容器内可下载或挂载 cuDF 仓库,然后:

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

产物位于java/target/,形如cudf-26.12.0-SNAPSHOT-cuda12.jar。与自包含脚本相比,该路径要求本地docker build、手动管理镜像标签,且无法表达多 classifier 聚合与发布版 POM 改写/还原逻辑,这也是 README 将其标记为 obsolete 的原因。

小结

java/ci/的自包含脚本体系把 cuDF Java 的发布链路拆成三个可独立、可并发、可复验的步骤:静态 libcudf 树(Step 1)、单 classifier JAR(Step 2)、Maven 仓库聚合(Step 3),每一步都有明确的输入契约、输出校验与失败清理策略;test_java_build_local.sh与test_packaged_java_local.sh则分别提供无 GPU 的构建预演和需要 GPU 的打包验证。本地执行./java/ci/test_java_build_local.sh --work-dir /tmp/java-build-test即可获得与 CI 同构的完整产物,是排查 Java 打包问题的最佳起点。

  • 数据分析
  • 数据工程
  • 机器学习

【免费下载链接】cudf

cuDF - GPU DataFrame Library

项目地址:https://gitcode.com/gh_mirrors/cu/cudf
点击查看免费下载

相关推荐

上一篇:Hugo 的 resources.Concat 函数:将多个资源拼接为一个资源
下一篇:better-escape.nvim完全指南:如何配置无延迟退出映射

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询