Mac 编译 Apache Arrow Java


Apache Arrow 是一个内存的列式存储格式,其生态已经非常丰富,包含了计算引擎(gandiva、arrow compute layer等,计算向量化)、IPC 格式、网络传输 flight等。

Arrow 文档相对较少,java 包含了 jni 的部分,导致需要额外编译 jni 动态库。

本文介绍了在 Mac 下 Apache Arrow java 项目的编译流程以及一些常见问题。

本文环境说明:

  • java version: 1.8.0_311
  • maven version: 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
  • Mac OS 12.3.1
  • arrow master分支,版本:9.0.0 commit: 3a646b35b29e52ca8eca995eb83af29c44b191c4

编译 java jni

arrow java 的 jni 分为两个部分:

  • arrow c data structure
  • arrow jni: 包含 gandiva、dataset、orc 三个部分的 jni

准备环境

# 在 arrow 目录下执行
$ cd path/to/arrow

# 通过 brew 安装所有依赖,可以查看 cpp/Brewfile 来查看其依赖
$ brew bundle --file=cpp/Brewfile
Homebrew Bundle complete! 25 Brewfile dependencies now installed.

关于 homebrew 的部分,可以去官网去进行安装,通过 brew 可以非常方便的安装编译所需要的环境。

下载速度比较慢,时间较长,请耐心等待。

build c data jni

# 在 arrow 目录下执行
$ cd path/to/arrow

$ mkdir -p java-dist java-native-c
$ cd java-native-c

$ cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_LIBDIR=lib \
    -DCMAKE_INSTALL_PREFIX=../java-dist \
    ../java/c

# 这一步会进行编译,稍等片刻
$ cmake --build . --target install

编译完成后,会在 ../java-dist/lib 有一个 libarrow_cdata_jni.dylib

build cpp jni (gandiva, dataset, orc)

# 在 arrow 目录下执行
$ cd path/to/arrow

$ mkdir -p java-dist java-native-cpp
$ cd java-native-cpp
$ cmake \
    -DARROW_BOOST_USE_SHARED=OFF \
    -DARROW_BROTLI_USE_SHARED=OFF \
    -DARROW_BZ2_USE_SHARED=OFF \
    -DARROW_GFLAGS_USE_SHARED=OFF \
    -DARROW_GRPC_USE_SHARED=OFF \
    -DARROW_LZ4_USE_SHARED=OFF \
    -DARROW_OPENSSL_USE_SHARED=OFF \
    -DARROW_PROTOBUF_USE_SHARED=OFF \
    -DARROW_SNAPPY_USE_SHARED=OFF \
    -DARROW_THRIFT_USE_SHARED=OFF \
    -DARROW_UTF8PROC_USE_SHARED=OFF \
    -DARROW_ZSTD_USE_SHARED=OFF \
    -DARROW_JNI=ON \
    -DARROW_PARQUET=ON \
    -DARROW_FILESYSTEM=ON \
    -DARROW_DATASET=ON \
    -DARROW_GANDIVA_JAVA=ON \
    -DARROW_GANDIVA_STATIC_LIBSTDCPP=ON \
    -DARROW_GANDIVA=ON \
    -DARROW_ORC=ON \
    -DARROW_PLASMA_JAVA_CLIENT=ON \
    -DARROW_PLASMA=ON \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_LIBDIR=lib \
    -DCMAKE_INSTALL_PREFIX=../java-dist \
    -DCMAKE_UNITY_BUILD=ON \
    -Dre2_SOURCE=BUNDLED \
    -DBoost_SOURCE=BUNDLED \
    -Dutf8proc_SOURCE=BUNDLED \
    -DSnappy_SOURCE=BUNDLED \
    -DORC_SOURCE=BUNDLED \
    -DZLIB_SOURCE=BUNDLED \
    ../cpp

# 编译时间比较久,耐心等待
$ cmake --build . --target install

编译完成后会有3个动态库:

  • libarrow_dataset_jni.dylib
  • libarrow_orc_jni.dylib
  • libgandiva_jni.dylib

编译 java project

only arrow-c-data:

$ mvn -Darrow.c.jni.dist.dir=../java-dist/lib -Parrow-c-data clean install -Drat.skip=true

only arrow-jni:

$ mvn -Darrow.cpp.build.dir=../java-dist/lib -Parrow-jni clean install -Drat.skip=true

arrow-c-data 和 arrow-jni 一起编译的时候会报错,分别编译是可以的。官方文档也是分开编译的。

编译碰到的问题

unapproved license 报错

如果 mvn 编译报下面错误的时候,请在 mvn 中添加配置 -Drat.skip=true

Too many files with unapproved license: 1 See RAT report in: /Users/icejoywoo/git/arrow/java/target/rat.txt

jni 动态库找不到的问题

jni 动态库的查找逻辑是在 classpath 中,默认编译的目录是在 ../java-dist/lib 中,这个目录默认不在其中,导致无法找到动态库。

下面提供一种方法,可以将动态库拷贝到对应项目的 test resources 文件夹中。

c/src/test/resources/libarrow_cdata_jni.dylib
gandiva/src/test/resources/libgandiva_jni.dylib
adapter/orc/src/test/resources/libarrow_orc_jni.dylib
adapter/orc/src/test/resources/libarrow_cdata_jni.dylib
dataset/src/test/resources/libarrow_dataset_jni.dylib

也可以使用下面的脚本,在 java 目录下执行即可。

#!/bin/bash
# file: update_jni_lib.sh

CURRENT_DIR=$(cd `dirname $0`; pwd)

cd ${CURRENT_DIR}

function link_jni_lib()
{
  dst=$1
  name=$2
  mkdir -p ${dst}
  cd ${dst}
  rm -f ${name}
  ln -s ${CURRENT_DIR}/../java-dist/lib/${name} ${name}
  file ${name}
  if [[ $? -ne 0 ]] ; then
    echo "failed to link jni lib ${name}"
  fi
  cd ${CURRENT_DIR}
}

link_jni_lib c/src/test/resources/ libarrow_cdata_jni.dylib
link_jni_lib gandiva/src/test/resources/ libgandiva_jni.dylib
link_jni_lib adapter/orc/src/test/resources/ libarrow_orc_jni.dylib
link_jni_lib adapter/orc/src/test/resources/ libarrow_cdata_jni.dylib
link_jni_lib dataset/src/test/resources/ libarrow_dataset_jni.dylib

idea jdk 11 找不到 Unsafe

官网文档有说明,在 Preference 中,修改配置 Java Compiler,不使用 --release

idea配置

For JDK 11, due to an IntelliJ bug, you must go into Settings > Build, Execution, Deployment > Compiler > Java Compiler and disable “Use ‘–release’ option for cross-compilation (Java 9 and later)”. Otherwise you will get an error like “package sun.misc does not exist”.

这个是一个 idea 的 bug

idea 找不到 ArrowType 等类

官网文档有说明,在 Project Structure 中,将 vector/target/generated-sources 目录添加到 sources 目录中。

这部分代码是通过 drill-fmpp-maven-plugin 生成的代码,idea 未自动识别到。

idea配置

In the Files tool window, find the path vector/target/generated-sources, right click the directory, and select Mark Directory as > Generated Sources Root. There is no need to mark other generated sources directories, as only the vector module generates sources.

动态库加载报错 incompatible architecture

报错信息为 mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64')

jdk 分为 x86_64 和 aarch64(M1),默认 jni 编译出来的是 aarch64(M1)的,需要使用的 jdk 也必须是 aarch64 的。否则会报动态库不 match 错误。

jdk 8 和 jdk 11 的 aarch64 的版本可以去zulu下载。

DirectReservationListener 报错 reservedMemory 找不到

TestReservationListener 单测报错,找不到 field reservedMemory

在 jdk11 中,java.nio.BitsreservedMemoryRESERVED_MEMORY,只需要修改即可。

在 jdk 8 中,无需修改。

cmake Error: could not load cache

cmake 编译 jni 动态库的时候,有可能会碰到该问题,根据stackoverflow,这个是因为 cmake 的缓存文件CMakeCache.txt导致,删除缓存文件后,重新编译即可。

删除可以使用下面的命令,删除当前文件中所有的CMakeCache.txt

find . -name CMakeCache.txt | xargs rm -v

参考资料

  1. Building Arrow Java

拓展资料

  1. Apache Arrow - High-Performance Columnar Data Framework: b站 youtube

如果觉得文章对您有帮助,用微信请作者喝杯咖啡吧!这样他会更有动力,分享更多更好的知识!

wechat赞赏
 Toc