- 数据分析
- 数据工程
- 机器学习
【免费下载链接】cudf
cuDF - GPU DataFrame Library
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。
前置条件
- 已安装 Docker,且当前用户可执行
docker; - 具备拉取
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.x | CUDA 13.x |
|---|---|---|
x86_64 | cuda12 | cuda13 |
aarch64/arm64 | cuda12-arm64 | cuda13-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)中:
java-build矩阵任务按 (CUDA × 架构) 每个条目分别执行 Step 1–2,并将每个 classifier 子目录上传为该条目的 artifact;- 独立的
java-gather任务用merge-multiple: true下载所有条目(全部子目录汇入同一个父目录),执行 Step 3,上传合并后的cudf_java_maven_repoartifact; 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
相关推荐
OOTDiffusion 中的 detectron2 Linux Wheel 打包指南:基于 manylinux 容器为 CUDA × Python 矩阵构建发布包
OOTDiffusion 中的 detectron2 Linux Wheel 打包指南:基于 manylinux 容器为 CUDA × Python 矩阵构建发
人工智能计算机视觉媒体生成AI 应用cuDF Java 开发实战:基于 nightly libcudf 包快速构建 cudf-java JNI(免源码编译)
cuDF Java 开发实战:基于 nightly libcudf 包快速构建 cudf java JNI(免源码编译) 本篇技术指南讲解如何在 cudf 仓库
数据分析数据工程机器学习Apache MXNet PyPI 包持续交付流水线实战:基于 Jenkins 的 wheel 构建、凭据管理与发布机制解析
Apache MXNet PyPI 包持续交付流水线实战:基于 Jenkins 的 wheel 构建、凭据管理与发布机制解析 Apache MXNet 的 Py
人工智能深度学习机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考