构建 Arrow C++#

系统设置#

Arrow 使用 CMake 作为构建配置系统。我们建议进行源码外构建。如果您不熟悉这个术语

  • **源码内构建**: 直接从 `cpp` 目录调用 `cmake`。当您希望维护多个构建环境(例如,一个用于调试构建,另一个用于发布构建)时,这可能会缺乏灵活性。

  • **源码外构建**: 从另一个目录调用 `cmake`,创建一个与任何其他构建环境不交互的隔离构建环境。例如,您可以创建 `cpp/build-debug` 并从此目录调用 `cmake $CMAKE_ARGS ..`。

构建需要

  • 支持 C++17 的编译器。在 Linux 上,gcc 7.1 及更高版本应该足够了。对于 Windows,至少需要 Visual Studio VS2017。

  • CMake 3.16 或更高版本

  • 在 Linux 和 macOS 上,`make` 或 `ninja` 构建工具

  • 最小构建至少需要 1GB 内存,使用测试的最小调试构建需要 4GB,使用docker 的完整构建需要 8GB。

在 Ubuntu/Debian 上,您可以使用以下命令安装这些要求

sudo apt-get install \
     build-essential \
     ninja-build \
     cmake

在 Alpine Linux 上

apk add autoconf \
        bash \
        cmake \
        g++ \
        gcc \
        ninja \
        make

在 Fedora Linux 上

sudo dnf install \
     cmake \
     gcc \
     gcc-c++ \
     ninja-build \
     make

在 Arch Linux 上

sudo pacman -S --needed \
     base-devel \
     ninja \
     cmake

在 macOS 上,您可以使用 Homebrew

git clone https://github.com/apache/arrow.git
cd arrow
brew update && brew bundle --file=cpp/Brewfile

使用 vcpkg

git clone https://github.com/apache/arrow.git
cd arrow
vcpkg install \
  --x-manifest-root cpp \
  --feature-flags=versions \
  --clean-after-build

在 MSYS2 上

pacman --sync --refresh --noconfirm \
  ccache \
  git \
  mingw-w64-${MSYSTEM_CARCH}-boost \
  mingw-w64-${MSYSTEM_CARCH}-brotli \
  mingw-w64-${MSYSTEM_CARCH}-cmake \
  mingw-w64-${MSYSTEM_CARCH}-gcc \
  mingw-w64-${MSYSTEM_CARCH}-gflags \
  mingw-w64-${MSYSTEM_CARCH}-glog \
  mingw-w64-${MSYSTEM_CARCH}-gtest \
  mingw-w64-${MSYSTEM_CARCH}-lz4 \
  mingw-w64-${MSYSTEM_CARCH}-protobuf \
  mingw-w64-${MSYSTEM_CARCH}-python3-numpy \
  mingw-w64-${MSYSTEM_CARCH}-rapidjson \
  mingw-w64-${MSYSTEM_CARCH}-snappy \
  mingw-w64-${MSYSTEM_CARCH}-thrift \
  mingw-w64-${MSYSTEM_CARCH}-zlib \
  mingw-w64-${MSYSTEM_CARCH}-zstd

构建#

以下所有说明都假设您已克隆 Arrow git 存储库并导航到 `cpp` 子目录。

$ git clone https://github.com/apache/arrow.git
$ cd arrow/cpp

CMake 预设#

使用 CMake 3.21.0 或更高版本,提供了一些用于各种构建配置的预设。您可以使用 `cmake --list-presets` 获取可用预设的列表。

$ cmake --list-presets   # from inside the `cpp` subdirectory
Available configure presets:

  "ninja-debug-minimal"     - Debug build without anything enabled
  "ninja-debug-basic"       - Debug build with tests and reduced dependencies
  "ninja-debug"             - Debug build with tests and more optional components
   [ etc. ]

您可以使用 `cmake -N --preset <preset name>` 检查给定预设启用的特定选项。

$ cmake --preset -N ninja-debug-minimal
Preset CMake variables:

  ARROW_BUILD_INTEGRATION="OFF"
  ARROW_BUILD_STATIC="OFF"
  ARROW_BUILD_TESTS="OFF"
  ARROW_EXTRA_ERROR_CONTEXT="ON"
  ARROW_WITH_RE2="OFF"
  ARROW_WITH_UTF8PROC="OFF"
  CMAKE_BUILD_TYPE="Debug"

您还可以从给定预设创建构建

$ mkdir build   # from inside the `cpp` subdirectory
$ cd build
$ cmake .. --preset ninja-debug-minimal
   Preset CMake variables:

     ARROW_BUILD_INTEGRATION="OFF"
     ARROW_BUILD_STATIC="OFF"
     ARROW_BUILD_TESTS="OFF"
     ARROW_EXTRA_ERROR_CONTEXT="ON"
     ARROW_WITH_RE2="OFF"
     ARROW_WITH_UTF8PROC="OFF"
     CMAKE_BUILD_TYPE="Debug"

   -- Building using CMake version: 3.21.3
   [ etc. ]

然后请求编译构建目标

$ cmake --build .
[142/142] Creating library symlink debug/libarrow.so.700 debug/libarrow.so

$ tree debug/
debug/
├── libarrow.so -> libarrow.so.700
├── libarrow.so.700 -> libarrow.so.700.0.0
└── libarrow.so.700.0.0

0 directories, 3 files

$ cmake --install .

创建构建时,除了预设定义的选项外,还可以传递自定义选项,例如

$ cmake .. --preset ninja-debug-minimal -DCMAKE_INSTALL_PREFIX=/usr/local

注意

CMake 预设是为了帮助您开始 Arrow 开发并了解常见的构建配置而提供的。它们不保证一成不变,将来可能会根据反馈进行更改。

强烈建议自动化构建、持续集成、发布脚本等使用手动配置,而不是依赖 CMake 预设,如下所述。

手动配置#

构建系统默认使用 `CMAKE_BUILD_TYPE=release`,因此如果省略此参数,则将生成发布版本。

注意

您需要设置更多选项才能在 Windows 上构建。有关详细信息,请参阅在 Windows 上开发

几种构建类型是可行的

  • `Debug`:不应用任何编译器优化,并在二进制文件中添加调试信息。

  • `RelWithDebInfo`:应用编译器优化,同时在二进制文件中添加调试信息。

  • `Release`:应用编译器优化并从二进制文件中删除调试信息。

注意

这些构建类型默认提供合适的优化/调试标志,但您可以通过指定 `-DARROW_C_FLAGS_${BUILD_TYPE}=...` 和/或 `-DARROW_CXX_FLAGS_${BUILD_TYPE}=...` 来更改它们。`${BUILD_TYPE}` 是构建类型的大写形式。例如,`Debug` 构建类型的 `DEBUG` (`-DARROW_C_FLAGS_DEBUG=...` / `-DARROW_CXX_FLAGS_DEBUG=...`),以及 `RelWithDebInfo` 构建类型的 `RELWITHDEBINFO` (`-DARROW_C_FLAGS_RELWITHDEBINFO=...` / `-DARROW_CXX_FLAGS_RELWITHDEBINFO=...`)。

例如,您可以通过传递 `-DARROW_CXX_FLAGS_RELEASE=-O3` 将 `-O3` 用作 `Release` 构建类型的优化标志。您可以通过传递 `-DARROW_CXX_FLAGS_DEBUG=-g3` 将 `-g3` 用作 `Debug` 构建类型的调试标志。

您还可以使用标准变量 `CMAKE_C_FLAGS_${BUILD_TYPE}` 和 `CMAKE_CXX_FLAGS_${BUILD_TYPE}`,但建议使用变量 `ARROW_C_FLAGS_${BUILD_TYPE}` 和 `ARROW_CXX_FLAGS_${BUILD_TYPE}`。`CMAKE_C_FLAGS_${BUILD_TYPE}` 和 `CMAKE_CXX_FLAGS_${BUILD_TYPE}` 变量替换 CMake 提供的所有默认标志,而 `ARROW_C_FLAGS_${BUILD_TYPE}` 和 `ARROW_CXX_FLAGS_${BUILD_TYPE}` 只是追加指定的标志,这允许选择性地覆盖某些默认值。

您还可以使用标志 `-DARROW_EXTRA_ERROR_CONTEXT=ON` 运行默认构建,请参阅额外的调试帮助

最小发布版本(建议 1GB 或更多内存用于构建)

$ mkdir build-release
$ cd build-release
$ cmake ..
$ make -j8       # if you have 8 CPU cores, otherwise adjust
$ make install

带单元测试的最小调试版本(建议 4GB 或更多内存用于构建)

$ git submodule update --init --recursive
$ export ARROW_TEST_DATA=$PWD/../testing/data
$ mkdir build-debug
$ cd build-debug
$ cmake -DCMAKE_BUILD_TYPE=Debug -DARROW_BUILD_TESTS=ON ..
$ make -j8       # if you have 8 CPU cores, otherwise adjust
$ make unittest  # to run the tests
$ make install

默认情况下不构建单元测试。构建后,还可以使用 CMake 提供的 `ctest` 工具调用单元测试(请注意,`test` 依赖于可用的 `python`)。

在某些 Linux 发行版上,运行测试套件可能需要设置显式区域设置。如果您看到任何与区域设置相关的错误,请尝试设置环境变量(需要 `locales` 包或等效物)

$ export LC_ALL="en_US.UTF-8"

使用 Ninja 加速构建#

许多贡献者使用Ninja 构建系统来加快构建速度。它尤其加快了增量构建的速度。要使用 `ninja`,请在调用 `cmake` 时传递 `-GNinja`,然后使用 `ninja` 命令代替 `make`。

Unity 构建 (统一构建)#

CMake Unity 构建选项可以显着加快完整构建速度,但也会增加内存需求。如果内存消耗不是问题,请考虑启用它(使用 `-DCMAKE_UNITY_BUILD=ON`)。

可选组件#

默认情况下,C++ 构建系统会创建一个相当精简的构建。我们有几个可选的系统组件,您可以通过向 `cmake` 传递布尔标志来选择构建它们。

  • -DARROW_BUILD_UTILITIES=ON : 构建 Arrow 命令行实用程序

  • -DARROW_COMPUTE=ON:构建所有计算内核函数

  • -DARROW_CSV=ON:CSV 读取器模块

  • -DARROW_CUDA=ON:用于 GPU 开发的 CUDA 集成。依赖于 NVIDIA CUDA 工具包。可以使用 `$CUDA_HOME` 环境变量自定义用于构建库的 CUDA 工具链。

  • -DARROW_DATASET=ON:数据集 API,包含文件系统 API

  • -DARROW_FILESYSTEM=ON:用于访问本地和远程文件系统的文件系统 API

  • -DARROW_FLIGHT=ON:Arrow Flight RPC 系统,至少依赖于 gRPC

  • -DARROW_FLIGHT_SQL=ON:Arrow Flight SQL

  • -DARROW_GANDIVA=ON:Gandiva 表达式编译器,依赖于 LLVM、Protocol Buffers 和 re2

  • -DARROW_GANDIVA_JAVA=ON:Gandiva JNI 绑定,用于 Java

  • -DARROW_GCS=ON:构建支持 GCS 的 Arrow(需要用于 C++ 的 GCloud SDK)

  • -DARROW_HDFS=ON:Arrow 与 libhdfs 集成,用于访问 Hadoop 文件系统

  • -DARROW_JEMALLOC=ON:构建基于 jemalloc 的 Arrow 分配器,默认启用

  • -DARROW_JSON=ON:JSON 读取器模块

  • -DARROW_MIMALLOC=ON:构建基于 mimalloc 的 Arrow 分配器

  • -DARROW_ORC=ON:Arrow 与 Apache ORC 集成

  • -DARROW_PARQUET=ON:Apache Parquet 库和 Arrow 集成

  • -DPARQUET_REQUIRE_ENCRYPTION=ON:Parquet 模块化加密

  • -DARROW_PYTHON=ON:此选项自 10.0.0 起已弃用。将在未来的版本中删除。请改用 CMake 预设。或者,您可以直接启用 `ARROW_COMPUTE`、`ARROW_CSV`、`ARROW_DATASET`、`ARROW_FILESYSTEM`、`ARROW_HDFS` 和 `ARROW_JSON`。

  • -DARROW_S3=ON:支持与 Amazon S3 兼容的文件系统

  • -DARROW_SUBSTRAIT=ON:构建支持 Substrait

  • -DARROW_WITH_RE2=ON:使用 re2 库构建对正则表达式的支持,默认启用,并在 `ARROW_COMPUTE` 或 `ARROW_GANDIVA` 为 `ON` 时使用

  • -DARROW_WITH_UTF8PROC=ON:使用 utf8proc 库构建对 Unicode 属性的支持,默认启用,并在 `ARROW_COMPUTE` 或 `ARROW_GANDIVA` 为 `ON` 时使用

  • -DARROW_TENSORFLOW=ON:构建启用 TensorFlow 支持的 Arrow

Arrow 中可用的压缩选项有:

  • -DARROW_WITH_BROTLI=ON:构建对 Brotli 压缩的支持

  • -DARROW_WITH_BZ2=ON:构建对 BZ2 压缩的支持

  • -DARROW_WITH_LZ4=ON:构建对 lz4 压缩的支持

  • -DARROW_WITH_SNAPPY=ON:构建对 Snappy 压缩的支持

  • -DARROW_WITH_ZLIB=ON:构建对 zlib (gzip) 压缩的支持

  • -DARROW_WITH_ZSTD=ON:构建对 ZSTD 压缩的支持

如果您的应用程序不需要核心 Arrow 共享库的某些功能,可以将其关闭以缩短构建时间

  • -DARROW_IPC=ON:构建 IPC 扩展

注意

如果您的用例仅限于读/写 Arrow 数据,则默认选项应该足够。但是,如果您希望构建任何测试/基准测试,则还需要 `ARROW_JSON`(它将自动启用)。如果需要扩展格式支持,则添加 `ARROW_PARQUET`、`ARROW_CSV`、`ARROW_JSON` 或 `ARROW_ORC` 不应启用任何其他组件。

注意

通常情况下,如果您预计会使用 `cast` 之外的任何计算内核,最好启用 `ARROW_COMPUTE`。虽然默认情况下会内置一些额外的内核(截至 12.0.0),但此列表将来可能会更改,因为它部分基于当前格式实现中的内核使用情况。

可选目标#

对于开发版本,您通常需要启用其他目标,以便使用以下 `cmake` 选项来测试您的更改。

  • -DARROW_BUILD_BENCHMARKS=ON:构建可执行基准测试。

  • -DARROW_BUILD_EXAMPLES=ON:构建使用 Arrow C++ API 的示例。

  • -DARROW_BUILD_INTEGRATION=ON:构建用于测试不同 Arrow 实现之间协议互操作性的其他可执行文件。

  • -DARROW_BUILD_UTILITIES=ON:构建可执行实用程序。

  • -DARROW_BUILD_TESTS=ON:构建可执行单元测试。

  • -DARROW_ENABLE_TIMING_TESTS=ON:如果构建单元测试,则启用那些依赖于挂钟计时的单元测试(此标志在 CI 上禁用,因为它会导致测试结果不稳定)。

  • -DARROW_FUZZING=ON:构建模糊测试目标和相关可执行文件。

可选检查#

以下特殊检查也可用。它们以各种方式检测生成的代码,以便在运行时(例如,执行单元测试时)检测所选类别的问题。

  • -DARROW_USE_ASAN=ON:启用地址清理器以检查内存泄漏、缓冲区溢出或其他类型的内存管理问题。

  • -DARROW_USE_TSAN=ON:启用线程清理器以检查多线程代码中的竞争条件。

  • -DARROW_USE_UBSAN=ON:启用未定义行为清理器以检查触发 C++ 未定义行为的情况。

其中一些选项互不兼容,因此如果您想测试所有选项,可能需要使用不同的选项构建多次。

CMake 版本要求#

我们支持 CMake 3.16 及更高版本。

LLVM 和 Clang 工具#

我们目前使用 LLVM 进行库构建和其他开发工具,例如使用 `clang-format` 进行代码格式化。LLVM 可以通过大多数现代软件包管理器(apt、yum、conda、Homebrew、vcpkg、chocolatey)安装。

构建依赖项管理#

构建系统支持许多第三方依赖项

  • AWSSDK:用于 S3 支持,需要系统 cURL,并且可以使用下面描述的 `BUNDLED` 方法

  • benchmark:Google benchmark,用于测试

  • Boost:用于跨平台支持

  • Brotli:用于数据压缩

  • BZip2:用于数据压缩

  • c-ares:gRPC 的依赖项

  • gflags:用于命令行实用程序(以前称为 Googleflags)

  • GLOG:用于日志记录

  • google_cloud_cpp_storage:用于 Google Cloud Storage 支持,需要系统 cURL,并且可以使用下面描述的 `BUNDLED` 方法

  • gRPC:用于远程过程调用

  • GTest:Googletest,用于测试

  • LLVM:Gandiva 的依赖项

  • Lz4:用于数据压缩

  • ORC:用于 Apache ORC 格式支持

  • re2:用于计算内核和 Gandiva,gRPC 的依赖项

  • Protobuf:Google Protocol Buffers,用于数据序列化

  • RapidJSON:用于数据序列化

  • Snappy:用于数据压缩

  • Thrift:Apache Thrift,用于数据序列化

  • utf8proc:用于计算内核

  • ZLIB:用于数据压缩

  • zstd:用于数据压缩

CMake 选项 ARROW_DEPENDENCY_SOURCE 是一个全局选项,指示构建系统如何解析每个依赖项。有几个选项:

  • AUTO:尝试在系统默认位置查找软件包,如果找不到则从源代码构建

  • BUNDLED:从源代码自动构建依赖项

  • SYSTEM:使用 CMake 内置的 find_package 函数在系统路径中查找依赖项,或者对没有此功能的软件包使用 pkg-config

  • CONDA:使用 $CONDA_PREFIX 作为备用的 SYSTEM 路径

  • VCPKG:查找 vcpkg 安装的依赖项,如果找不到,则运行 vcpkg install 来安装它们

  • BREW:使用 Homebrew 默认路径作为备用的 SYSTEM 路径

默认方法是 AUTO,除非您在活动的 conda 环境中进行开发(通过存在 $CONDA_PREFIX 环境变量来检测),在这种情况下,默认方法是 CONDA

单个依赖项解析#

虽然 -DARROW_DEPENDENCY_SOURCE=$SOURCE 为所有软件包设置了全局默认值,但可以通过设置 -D$PACKAGE_NAME_SOURCE=.. 来覆盖单个软件包的解析策略。例如,要从源代码构建 Protocol Buffers,请设置

-DProtobuf_SOURCE=BUNDLED

不幸的是,此变量区分大小写;上面列出了每个软件包使用的名称,但最新的列表可以在 cpp/cmake_modules/ThirdpartyToolchain.cmake 中找到。

捆绑依赖项版本#

当使用 BUNDLED 方法从源代码构建依赖项时,将使用 cpp/thirdparty/versions.txt 中的版本号。还有一个依赖项源下载器脚本(见下文),可用于设置离线构建。

当使用 BUNDLED 进行依赖项解析时(并且如果您使用推荐的 jemalloc 或 mimalloc 分配器),在第三方项目中静态链接 Arrow 库会更加复杂。有关如何在这种情况下的配置构建系统,请参阅下文。

离线构建#

如果您不使用上述变量来指示 Arrow 构建系统预安装依赖项,它们将由 Arrow 构建系统自动构建。每个依赖项的源代码存档将通过互联网下载,这在互联网访问受限的环境中可能会导致问题。

要启用离线构建,您可以自己下载源代码工件,并使用格式为 ARROW_$LIBRARY_URL 的环境变量来指示构建系统从本地文件读取,而不是访问互联网。

为了方便您,我们准备了一个脚本 thirdparty/download_dependencies.sh,它将把每个依赖项的正确版本下载到您选择的目录中。它将在最后打印一系列 bash 样式的环境变量语句,供您的构建脚本使用。

# Download tarballs into $HOME/arrow-thirdparty
$ ./thirdparty/download_dependencies.sh $HOME/arrow-thirdparty

然后,您可以调用 CMake 来创建构建目录,它将使用声明的环境变量(指向下载的存档)而不是下载它们(每个构建目录一个!)。

静态链接#

当使用 -DARROW_BUILD_STATIC=ON 时,所有由 Arrow 构建系统构建为静态库的构建依赖项都将合并在一起,以创建静态库 arrow_bundled_dependencies。在类 UNIX 环境(Linux、macOS、MinGW)中,这被称为 libarrow_bundled_dependencies.a,在使用 Visual Studio 的 Windows 上被称为 arrow_bundled_dependencies.lib。此“依赖包”库安装在与其他 Arrow 静态库相同的位置。

如果您正在使用 CMake,如果您使用 arrow_static CMake 目标,则链接时将自动包含捆绑的依赖项。在其他构建系统中,您可能需要显式链接到依赖包。我们创建了一个基于 CMake 的示例构建配置,向您展示一个有效的示例。

在 Linux 和 macOS 上,如果您的应用程序尚未链接到 pthread 库,则必须在链接器设置中包含 -pthread。在 CMake 中,可以使用内置的 Threads 包来完成此操作

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_target PRIVATE Threads::Threads)

额外的调试帮助#

如果您使用 CMake 选项 -DARROW_EXTRA_ERROR_CONTEXT=ON,它将在编译库时在 RETURN_NOT_OK 宏内的错误检查中添加额外的调试信息。在使用 ASSERT_OK 的单元测试中,这将产生如下错误输出:

../src/arrow/ipc/ipc-read-write-test.cc:609: Failure
Failed
../src/arrow/ipc/metadata-internal.cc:508 code: TypeToFlatbuffer(fbb, *field.type(), &children, &layout, &type_enum, dictionary_memo, &type_offset)
../src/arrow/ipc/metadata-internal.cc:598 code: FieldToFlatbuffer(fbb, *schema.field(i), dictionary_memo, &offset)
../src/arrow/ipc/metadata-internal.cc:651 code: SchemaToFlatbuffer(fbb, schema, dictionary_memo, &fb_schema)
../src/arrow/ipc/writer.cc:697 code: WriteSchemaMessage(schema_, dictionary_memo_, &schema_fb)
../src/arrow/ipc/writer.cc:730 code: WriteSchema()
../src/arrow/ipc/writer.cc:755 code: schema_writer.Write(&dictionaries_)
../src/arrow/ipc/writer.cc:778 code: CheckStarted()
../src/arrow/ipc/ipc-read-write-test.cc:574 code: writer->WriteRecordBatch(batch)
NotImplemented: Unable to convert type: decimal(19, 4)

弃用和 API 更改#

我们使用宏 ARROW_DEPRECATED 来包装 C++ 已弃用的 API 属性。最佳实践是使用 -Werror=deprecated-declarations(对于 GCC/Clang 或其他编译器的类似标志)编译第三方应用程序,以主动捕获 API 更改并对其进行说明。

模块化构建目标#

由于 C++ 项目有几个主要部分,我们提供了模块化 CMake 目标来构建每个库组件、单元测试和基准测试组及其依赖项

  • make arrow 用于 Arrow 核心库

  • make parquet 用于 Parquet 库

  • make gandiva 用于 Gandiva(LLVM 表达式编译器)库

注意

如果您选择 Ninja 作为 CMake 生成器,请将 make arrow 替换为 ninja arrow,依此类推。

要构建单元测试或基准测试,请在目标名称后添加 -tests-benchmarks。因此 make arrow-tests 将构建 Arrow 核心单元测试。使用 -all 目标,例如 parquet-all,将构建所有内容。

如果您只想构建并安装一个或多个项目子组件,我们提供了 CMake 选项 ARROW_OPTIONAL_INSTALL 来仅安装已构建的目标。例如,如果您只想构建 Parquet 库、其测试及其依赖项,可以运行

cmake .. -DARROW_PARQUET=ON \
      -DARROW_OPTIONAL_INSTALL=ON \
      -DARROW_BUILD_TESTS=ON
make parquet
make install

如果您在调用 make 时省略了显式目标,则将构建所有目标。

在 macOS 上使用 Xcode 进行调试#

Xcode 是 macOS 附带的 IDE,可用于通过生成 Xcode 项目来开发和调试 Arrow

cd cpp
mkdir xcode-build
cd xcode-build
cmake .. -G Xcode -DARROW_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=DEBUG
open arrow.xcodeproj

这将生成一个项目并在 Xcode 应用程序中打开它。或者,命令 xcodebuild 将使用生成的项目执行命令行构建。建议在首次启动项目时使用“自动创建方案”选项。选择自动生成的方案将允许您构建并运行启用了断点的单元测试。