那么,你有一些让 Arrow 变得更好的想法,或者可能你发现了一个错误。无论是哪种情况,你都希望能够与 Arrow 项目沟通你的需求和想法,或者贡献代码更改。本章就是关于如何进行这些操作的入门指南。无论这是你第一次为开源项目做贡献,还是你已经是经验丰富的开发者,我们都会介绍你需要知道的所有内容,以便你能够做出一些贡献。像任何开源项目一样,Arrow 的发展和成败都取决于社区的参与和开发者的使用。参与的人越多,项目的前景就越好。
本章将涵盖以下内容:
- 如何与开源项目(尤其是 Apache 项目)互动并做出贡献
- Arrow 仓库的架构、构建脚本和自动化任务
- 如何找到你可以贡献的内容
- 在 Arrow 项目中拉取请求的生命周期
本章的很多内容都是我基于官方 Arrow 文档中有关贡献和开发的内容所进行的个性化解读。如果你希望获得有关这里讨论的任何主题的更多信息,可以参考官方文档:arrow.apache.org/docs/develo...。现在,事不宜迟,让我们开始吧!
技术要求
以下是你在本章中需要的内容:
- 一台连接互联网的电脑和网络浏览器。
- Python 3.9 或更高版本。
- Go 1.20 或更高版本。
- 你需要能够访问电脑的命令行。在 Windows 系统中,首选 PowerShell,可以从 docs.microsoft.com/en-us/power... 安装。
所有其他必要的应用程序将在提到时给出安装说明。
为开源项目做贡献
如今,整个世界依赖于开源软件:Linux、Android、Mozilla Firefox、Chromium(Google Chrome 和 Microsoft Edge 的底层代码)、Visual Studio Code、WordPress、Vue.js、React 等等。即使我们没有意识到,每个人几乎每天都在与某种开源技术进行交互。你还可以找到许多非常有趣的开源代码!甚至原始的阿波罗 11 号导航计算机的源代码也已经开源(github.com/chrislgarry...)!然而,如果没有人使用和为这些项目做贡献,它们就无法像今天这样蓬勃发展。
参与和贡献开源社区的方式在这些年里发生了巨大的变化,尤其是随着 GitHub 的崛起。在许多方面,贡献比以往任何时候都容易。GitHub 提供了出色的搜索功能、标准化的源代码管理和通用术语。尽管如此,在为任何开源项目做贡献时,有一些关键点你需要牢记。
沟通至关重要
大多数开源社区都有行为准则。所有 Apache 软件基金会的项目都遵循 Apache 行为准则,可以在其网站上找到:www.apache.org/foundation/...。目标是合作,这意味着人们需要频繁互动。在与项目的其他成员互动时,请保持耐心和友好。如果你想报告一个错误或问题,提供一些上下文信息!解释你试图做什么,并给出可复现的步骤。如果你是在建议改进,解释这个改进的用例以及它对你和其他人为什么有用。
不了解某些事情是完全可以的;没有人知道所有的东西。但是请确保你已经尽了一些力。在请求帮助之前,先阅读项目的 README.md 文件和文档,做个简单的 Google 搜索。人们会很欣赏你表现出学习的意愿,而不仅仅是让别人为你做事情。比如,"X 出问题了!请修复!" 和 "我不知道如何做 X;我查阅了文档,但没有找到相关参考。" 之间有很大的区别,后者更有可能得到回应。记住,通过书面文字传递语气和情绪非常困难,尤其是当项目的合作者可能来自不同国家和文化时。在对话中保持善意,尊重社区决策,并保持耐心。相信我,人们会因此感激你的态度。
你不一定非要贡献代码
是的,没错。你可以为开源项目做出贡献和改进,而无需写一行代码。非代码部分往往是项目中最容易被忽视的部分。也许你可以改进或增强一些文档,或者编写缺失的新文档!也许你擅长设计,想提高项目的可用性和设计。你还可以将文档翻译成另一种语言,帮助增加项目的采用率,或者整理一个示例文件夹,展示如何使用这个项目。像商业项目一样,开源项目的需求往往远超源代码本身。通过从这些领域开始,你可以更容易地上手,并迅速熟悉项目的其他贡献者。这通常会为你打开其他贡献机会的大门,包括通过代码贡献,如果这是你的偏好。
为什么你应该贡献?
如果你对为开源项目贡献还不太感兴趣,我们来看看几个你绝对应该参与的理由!为开源项目做贡献可以是一种极具回报的方式,它可以帮助你学习、教学,并积累几乎任何你能想到的技能:
- 你可以改进你依赖的软件!如果你发现了一个你使用的软件或库中的错误,可能你想自己动手修复,而不是等别人来解决。这样你不仅可以更快地得到修复,其他人也能从中受益。
- 练习你想提高的技能!无论你是想改进 UI 设计、写作、组织能力,还是想学习特定编程语言的 API 设计或编码技巧,都可以找到需要这些技能的项目。你只需要去寻找它。
- 结识有共同兴趣的人!拥有积极社区的开源项目可以让你结交全球的朋友。你可能会通过合作项目结交朋友,或找到有趣的对话伙伴。
- 提高人际交往能力!开源项目的合作提供了许多锻炼领导力和人际交往能力的机会,如工作优先级排序、解决冲突以及团队协作/组织团队的技能。
- 找到导师或帮助他人!也许你是经验丰富的工程师,也许你是编程新手。无论如何,分享项目并进行协作需要每个人解释自己如何处理问题,并可能需要就概念和实现寻求帮助。教学对所有参与者来说都可能是值得且有益的。
- 建立声誉甚至职业生涯!从定义上讲,你为开源项目做的任何工作都是公开可见的。通过与多个项目合作,你可以为自己建立起一个强大的公共声誉,以及一个可以轻松分享的工作和示例作品集。随着时间的推移,你甚至可能有机会参加或在相关工作会议上发言,扩展你的职业网络。事实上,这就是我如何开始写这本书的!
这并不是人们为开源项目做贡献的唯一原因;只是我想到的一些。最终,能够发现问题或提出一个想法,并自己解决它,这是一件非常令人满意的事情。或许你会想为 Arrow 项目做贡献;也许你会找到一个更适合你的项目。找到适合你的方式!
接下来的章节中,我们将重点介绍如何在 Arrow 代码库中找到自己的方向,并为项目做出贡献。概念上,许多开源项目的组织结构非常相似:持续集成(CI)配置、构建和部署脚本及工具、问题跟踪、邮件列表以及贡献规则。希望一旦你开始为开源项目做贡献,你会享受这个过程并坚持下去!现在我们开始吧!
准备你的第一个拉取请求
Apache Arrow 是一个非常庞大的项目,涉及许多不同的编程语言、构建系统、概念和交互。这意味着每个贡献者在某个时候都会遇到需要学习的新知识。你可能已经写了 10 年的 C++ 代码,但仍然需要询问一些关于 R 或 Ruby 的基本问题。在 Arrow 的贡献者中,你会发现各种专家、工程师和数据科学家,但也有很多用户、新工程师和爱好者在不断学习。
寻找帮助的主要方法 是订阅用户或开发者邮件列表,或在 GitHub 上创建问题。有关邮件列表和订阅说明的更多信息,你可以访问 Arrow 网站:arrow.apache.org/community/。对于错误跟踪、功能增强请求以及其他工作,Arrow 使用 GitHub Issues 来跟踪所有工作。
创建和浏览 GitHub Issues
由于问题是直接在 GitHub 上跟踪的,你可以通过搜索现有的未解决问题,轻松筛选出已经提出、计划或请求的工作。如果你想建议要做的工作,在你开始任何实际工作之前,应该创建一个 Issue 与其他开发者讨论你的问题或你计划如何实现修复或增强。尽管你认为某个功能是一个好主意,但维护者和社区中的其他成员不一定会同意。他们的观点往往不同,并且更关注如何维护某个新功能或更改,而不仅仅是考虑该功能有多实用。你不想在某个功能上花费大量时间,最后却被告知其他开发者不认为这是个好主意。
登录 GitHub 账号后,你可以创建一个 Issue,并选择问题类型,如图 11.1 所示: 准备你的第一个拉取请求
Apache Arrow 是一个非常庞大的项目,涉及许多不同的编程语言、构建系统、概念和交互。这意味着每个贡献者在某个时候都会遇到需要学习的新知识。你可能已经写了 10 年的 C++ 代码,但仍然需要询问一些关于 R 或 Ruby 的基本问题。在 Arrow 的贡献者中,你会发现各种专家、工程师和数据科学家,但也有很多用户、新工程师和爱好者在不断学习。
寻找帮助的主要方法 是订阅用户或开发者邮件列表,或在 GitHub 上创建问题。有关邮件列表和订阅说明的更多信息,你可以访问 Arrow 网站:arrow.apache.org/community/。对于错误跟踪、功能增强请求以及其他工作,Arrow 使用 GitHub Issues 来跟踪所有工作。
创建和浏览 GitHub Issues
由于问题是直接在 GitHub 上跟踪的,你可以通过搜索现有的未解决问题,轻松筛选出已经提出、计划或请求的工作。如果你想建议要做的工作,在你开始任何实际工作之前,应该创建一个 Issue 与其他开发者讨论你的问题或你计划如何实现修复或增强。尽管你认为某个功能是一个好主意,但维护者和社区中的其他成员不一定会同意。他们的观点往往不同,并且更关注如何维护某个新功能或更改,而不仅仅是考虑该功能有多实用。你不想在某个功能上花费大量时间,最后却被告知其他开发者不认为这是个好主意。
登录 GitHub 账号后,你可以创建一个 Issue,并选择问题类型,如图 11.1 所示:
无论你是在创建 GitHub issue 还是发送邮件到某个邮件列表,确保使用合适的标题。什么是合适的标题?首先,你需要在标题前加上相关组件的前缀,使用方括号表示。例如,如果你是关于 Python 库的内容,应该在标题前加上 [Python] 作为前缀。当你创建 issue 时,确保 issue 的相应 Component(s) 字段与你使用的前缀相匹配。图 11.2 展示了一个正确设置标题和组件字段的 issue 示例,标题前缀和组件字段都已正确设置:
设置这些组件字段可以方便每个人快速搜索问题并了解其上下文。在创建 issue 之前,强烈建议你先搜索现有问题,避免重复创建相同的问题。
寻找你的第一个问题
针对新贡献者,有几个标签可以帮助找到适合入门的 issue,通常标记为 good-first-issue
或 good-second-issue
。通常,这些标签的任务在几天内或最多一个周末就能解决。不过,不要害怕在 issue 的评论中提问。在处理一段时间后,你可能会发现问题比最初报告者想象的更复杂。一定要添加评论说明这一点!我们很友好!
那么,假设你已经找到一个要处理的 issue,无论是代码还是文档,你已经决定要为项目做出贡献。接下来该做什么?让我们开始设置环境吧!
设置 Git
如你所料,Arrow 使用 Git 进行版本控制,Git 几乎可以在任何操作系统上轻松使用。如果你还没有安装它,可以访问 GitHub 网站,找到简单的安装指南。安装完成后,如果还没配置 Git,不要忘记使用以下命令配置你的姓名和电子邮件:
lua
$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com
有用的小提示!
你也可以按照 GitHub 网站上的文档,了解如何通过 Git 进行身份验证,以便无需在每次运行 Git 命令时重新输入用户名和密码:GitHub 身份验证指南。
要为 Arrow 仓库做贡献,首先你需要在自己的 GitHub 账号下创建该仓库的分叉(fork)。幸运的是,这非常简单!只需前往 Arrow 的 GitHub 仓库 github.com/apache/arro... 并点击 Fork 按钮,如图 11.3 所示:
最简单的方法是将项目分叉(fork)到你的用户名下,创建一个具有 https://github.com/<username>/arrow
URL 的仓库副本。现在,你可以克隆仓库并设置上游(upstream)远程仓库,引用官方的 Apache Arrow 仓库。假设你已经按照文档配置了 GitHub 身份验证,克隆仓库有两种方法:
一种方式是通过 HTTPS 克隆:
shell
$ git clone https://github.com/<username>/arrow.git
另一种方式是使用安全外壳协议(SSH)克隆:
shell
$ git clone git@github.com:<username>/arrow.git
默认情况下,当你克隆一个仓库时,系统会将其称为 origin
。在开发 Arrow 时,通常将官方 Arrow 仓库引用为 upstream
。你可以通过以下简单命令配置它:
shell
$ cd arrow # 首先进入克隆的目录
$ git remote add upstream https://github.com/apache/arrow
你可以轻松确认设置是否正确!运行以下命令,输出应该类似于下方的内容:
ruby
$ git remote -v
origin https://github.com/<username>/arrow.git (fetch)
origin https://github.com/<username>/arrow.git (push)
upstream https://github.com/apache/arrow (fetch)
upstream https://github.com/apache/arrow (push)
现在,你应该在本地机器上有一个名为 arrow
的 Arrow 源代码副本,且有两个远程仓库:origin
是指向你自己的 fork,upstream
是指向官方的 Arrow GitHub 仓库。现在,你已经完成了设置,可以开始浏览代码和文档了!
熟悉代码库
大多数支持 Arrow 的语言实现都包含在 Arrow 仓库的各自子目录中:
cpp
-- C++csharp
-- C#js
-- JavaScriptc_glib
-- GLibgo
-- Golangjava
-- Javajulia
-- Juliamatlab
-- MATLABpython
-- Pythonr
-- Rruby
-- Ruby
注意 :Rust 的实现位于另一个单独的 GitHub 仓库:github.com/apache/arro...。
自动化和配置脚本以及文档位于其他子目录中:
.github
: GitHub Actions 的 CI 工作流定义文件。这些工作流由各种操作触发,如打开拉取请求(PR)或提出 issue。ci
: CI 任务使用的脚本和配置文件,包括 Docker 定义文件、构建和测试脚本。dev
: 开发人员用于打包、提交、部署和测试 Arrow 包的脚本。它还包含按需运行的 CI 任务定义,而非通过触发操作运行的任务,如模糊测试。docs
: 文档及其构建代码库的脚本。format
: FlatBuffers 和 Protocol Buffers 文件,用于定义 Arrow 格式的二进制协议,例如进程间通信(IPC)协议和 Arrow Flight RPC 定义。
无论你决定处理什么问题(编写测试、添加新功能、修复错误等),下一步都是确定要查看哪些文件。知道从哪个目录开始是一回事,但有时你需要一些帮助来确定需要查看的具体文件。
GitHub 的搜索功能非常强大,提供了许多特殊语法,可以帮助你精确找到所需内容。确保你在查看官方 Arrow 仓库(而不是分叉)时使用搜索功能,利用与语言相关的搜索功能来查找函数定义或特定引用。你还可以使用它查找特定的 issue 或提交,它们可能包含与你工作相关的关键字。查看与类似问题相关的 PR、提交或 issue,可能会对你非常有帮助。查阅现有功能的单元测试是了解其用法的宝贵资源。
另一种选择是使用你喜欢的 IDE 中的扩展和搜索功能。由于你已经将代码本地复制到机器上,大多数 IDE 提供的扩展和功能都能帮助你轻松搜索大型代码库。
如果你要进行代码更改,显然需要了解如何构建 Arrow 库!虽然我们将在本章介绍如何构建一些库,但 Arrow 网站通常为不同的实现提供开发者指南(例如,C++ 实现的指南:arrow.apache.org/docs/develo...)。
构建 Arrow 库
到目前为止,本书的重点是 Arrow 提供库的三种语言:C++、Python 和 Golang。接下来我们将继续介绍如何构建这些库并运行三种语言中的单元测试。我们从 C++ 库开始!
构建 Arrow C++ 库
大多数 Arrow 库是独立实现的,但 C(GLib)、MATLAB、Python、R 和 Ruby 库实际上是基于 C++ Arrow 实现之上的绑定库。如果你打算对这些语言做出贡献或进行更改,可能需要修改 C++ 代码,或至少在本地编译 C++ 代码。因此,无论你是否直接参与 C++ 库的开发,本节内容对你来说都很重要。
Arrow 使用一种名为 CMake 的工具来管理其 C++ 库的构建。CMake 是一个跨平台的构建系统生成器;它生成配置并将构建任务交给其他程序(如 make 或 ninja)执行。如果你使用的是 CMake 3.21.0 或更高版本,可以利用提供的预设配置。让我们开始吧!
构建 C++ 库的技术要求
构建 C++ 库需要以下条件:
- 支持 C++17 或更高版本的 C++ 编译器。使用 Linux/Unix 风格的系统,例如 gcc 7.1 或更高版本。对于 Windows,你至少需要 Visual Studio 2017 或更新版本。
- CMake 3.21 或更高版本。
- 在 Linux 或 macOS 上,你需要 make 或 ninja 构建工具。
- 最小构建需要至少 1 GB 内存,带有测试的最小调试构建需要至少 4 GB 内存。如果你想使用 Docker 进行完整构建,至少需要 8 GB 内存。现代机器通常自带至少 8 GB 或更多内存。
以下是在大多数系统上安装这些最低要求的简单方法:
对于 Ubuntu/Debian 系统:
csharp
$ sudo apt-get install build-essential cmake
$ sudo apt-get install python3-dev autoconf flex bison # 如果你想构建 Python pyarrow 模块
对于 Alpine Linux:
csharp
$ apk add autoconf bash cmake g++ gcc make
对于 CentOS/RHEL 系统:
ruby
$ sudo yum install gcc gcc-c++ make cmake3
$ sudo yum install autoconf bison python39-devel # 如果你想构建 Python 模块
对于 Fedora Linux:
ruby
$ sudo dnf install cmake gcc gcc-c++ make
$ sudo dnf install autoconf bison python39-devel # 如果构建 pyarrow
对于 macOS:
使用 Homebrew (brew.sh/):
shell
$ git clone https://github.com/apache/arrow.git
$ cd arrow
$ brew update && brew bundle --file=cpp/Brewfile
使用 vcpkg:
shell
$ git clone https://github.com/apache/arrow.git
$ cd arrow
$ vcpkg install --x-manifest-root cpp --feature-flags=versions --clean-after-build
使用 conda:
shell
$ conda create -y -n pyarrow-dev -c conda-forge \
--file arrow/ci/conda_env_unix.txt \
--file arrow/ci/conda_env_cpp.txt \
--file arrow/ci/conda_env_python.txt \
compilers python=3.9 pandas # 构建 pyarrow
$ conda activate pyarrow-dev
使用 MSYS2:
shell
$ 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
目录。准备好了吗?我们开始构建吧!
开始构建!
在开始构建之前,让我们先看看可用的 CMake 构建预设配置!
注意
CMake 预设是 CMake 3.20 版本中引入的功能。虽然你可以使用旧版本的 CMake 构建 Arrow 库,但只有使用 CMake 3.20 或更高版本时才能使用提供的预设配置。
你可以使用 cmake --list-presets
命令获取预设列表。看起来会像这样:
rust
$ cmake --list-presets # 确保你在 'cpp' 目录下
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
...
如果你想查看某个预设启用了哪些选项,可以运行以下命令:
ini
$ cmake -N --preset ninja-release-minimal
Preset CMake variables:
ARROW_BUILD_STATIC="OFF"
ARROW_WITH_RE2="OFF"
ARROW_WITH_UTF8PROC="OFF"
CMAKE_BUILD_TYPE="Release"
注意 这些预设配置使用了 ninja 构建工具,而不是 make。虽然 make 更常见且标准,但许多开发人员更喜欢使用 ninja,因为它可以加快增量构建速度。在大多数情况下,你只需在安装软件包时添加 ninja 即可。在使用 CMake 时,传递 -GNinja
选项以使用 ninja。
构建 C++ 库时,有两种主要的构建方式:
- 源内构建 :在
cpp
子目录中直接运行 cmake。如果你计划维护多个构建环境(例如单独的发布和调试构建),这会比较困难且不灵活。 - 源外构建 :在不同目录中运行 cmake,以保持构建之间相互隔离。常见方法是创建一个子目录,例如
cpp/debug-build
,并在该目录中运行cmake $ARGS ..
。这是推荐的方式,也是我们将要采用的方式。
让我们从 ninja-debug-minimal
预设开始构建:
ini
$ mkdir debug-build # 在 'cpp' 目录中
$ cd debug-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.22.1
...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matt/arrow/cpp/debug-build
你还可以在预设选项之外传递自定义选项:
ini
$ cmake .. --preset ninja-debug-minimal -DCMAKE_INSTALL_PREFIX=/usr/local
假设没有任何错误或问题,你可以通过以下单条命令编译库:
bash
$ cmake --build . # 在 debug-build 目录中
[143/143] Creating library symlink debug/libarrow.so.800 debug/libarrow.so
完成了!你已经成功构建了基本的 Arrow C++ 库!
虽然提供的预设配置有助于初学者理解最常见的构建配置和选项,但它们并不一定保持不变。个别开发者的反馈可能会导致预设随着时间而更改。如果你要准备任何自动化构建、CI 或脚本,应该手动配置构建并明确指定所需的选项,而不是依赖预设。
手动配置构建
你可以通过 CMAKE_BUILD_TYPE
选项指定三种构建类型:
- Debug:不进行编译器优化,并在生成的二进制文件中添加调试信息。
- RelWithDebInfo:应用编译器优化,但仍保留调试信息在编译的二进制文件中。
- Release:应用编译器优化,并从最终二进制文件中移除调试信息。这会生成最小的输出文件。
另外,你还可以设置 -DARROW_EXTRA_ERROR_CONTEXT=ON
选项,这将在错误检查代码中提供额外的上下文和调试输出。
在 Windows 上开发?
如果你在 Windows 上使用 Visual Studio 构建库,则需要执行更多步骤并设置更多选项。最好的资源是 Arrow 官方文档:
Arrow Windows 开发文档。
要进行最小化的发布构建,只需不传递任何选项,使用默认设置即可:
shell
$ mkdir build-release # 在 cpp 目录中
$ cd build-release
$ cmake ..
$ make -j8 # 对应 8 个 CPU 核心,可根据实际情况调整 8
默认情况下,单元测试不会被构建,你需要显式启用它们。在某些 Linux 发行版上运行测试套件时,可能会遇到区域设置相关的错误。如果遇到此类错误,可以尝试设置 LC_ALL
环境变量(需要安装 locales
包或等效软件包):
ini
$ export LC_ALL="en_US.UTF-8"
接下来,我们修改构建选项以构建并运行单元测试:
shell
$ git submodule update --init --recursive # 拉取测试数据
$ export ARROW_TEST_DATA=$PWD/../testing/data
$ export PARQUET_TEST_DATA=$PWD/cpp/submodules/parquet-testing/data
$ mkdir debug-build # 如果之前未创建,需新建该目录
$ cd debug-build
$ cmake -DCMAKE_BUILD_TYPE=Debug -DARROW_BUILD_TESTS=ON ..
$ make -j8 # 对应 8 个 CPU 核心,可根据实际情况调整 8
$ make unittest # 运行单元测试
在 Arrow 项目中,有很多可选组件。默认情况下,你将获得最小化构建,但你可以传递多个布尔标志来启用所需的组件。以下是一些有用的组件选项:
-DARROW_BUILD_UTILITIES=ON
:构建 Arrow 命令行工具。-DARROW_COMPUTE=ON
:构建 Arrow 计算库。-DARROW_CUDA=ON
:启用与 CUDA 集成的 GPU 开发。-DARROW_DATASET=ON
:启用数据集 API。-DARROW_FLIGHT=ON
:构建 Arrow Flight 库。-DARROW_GANDIVA=ON
:构建 Gandiva 表达式编译器。-DARROW_ORC=ON
:构建与 Apache ORC 文件格式的集成。-DARROW_PARQUET=ON
:构建 Parquet 库及其与 Arrow 的集成。-DARROW_PYTHON=ON
:构建 Python C++ 库,必须与 Python 版本匹配,且需要安装numpy
模块。-DARROW_IPC=ON
:构建 IPC 扩展。
Arrow 项目几乎所有的其他依赖项都可以通过 C++ 构建从源码编译,CMake 会负责处理这些依赖项。
要控制依赖项的解析方式,可以使用 CMake ARROW_DEPENDENCY_SOURCE
选项。可用的解析选项包括:
- AUTO:CMake 将尝试在系统默认位置查找依赖项,如果找不到,则从源码构建。
- BUNDLED:忽略系统位置,完全从源码构建所有依赖项。
- SYSTEM :使用 CMake 的
find_package
功能定位依赖项,对于不受支持的库,使用pkg-config
。 - CONDA :类似于 SYSTEM,但使用
$CONDA_PREFIX
代替 SYSTEM 路径。 - VCPKG :使用 VCPKG 查找依赖项,或者如果找不到,则通过
vcpkg install
安装它们。 - BREW:类似于 SYSTEM,但使用 Homebrew 的默认路径。
默认情况下,使用 AUTO
作为依赖项解析方式,除非你在一个活跃的 conda 环境中进行开发,在这种情况下,CONDA
将成为默认值。此选项设置了所有包的全局默认值,但你也可以为单个依赖项覆盖解析策略!
现在你已准备好开始构建,可以运行 make
或 ninja
来启动构建。构建 C++ 库并安装之后,如果你启用了 ARROW_PYTHON=ON
选项,现在可以继续构建 Python 库了!
构建 Arrow Python 模块
如果你已经按照上一节的步骤操作,那么现在应该已经构建了 Arrow C++ 库的本地版本。太好了!接下来,我们将设置环境以构建 pyarrow
Python 模块。根据你使用的工具链,可能会有不同的环境配置方式。以下示例假定 $ARROW_DIR
环境变量代表你克隆 Arrow 仓库的路径。你可以设置这个环境变量,这样可以轻松复制粘贴代码片段。
在继续之前!
确保你已经为环境添加了之前在构建 Arrow C++ 库时提到的 PyArrow 开发所需的包。这些依赖项是构建 Python 模块所必需的。如果你在没有它们的情况下使用 ARROW_PYTHON=ON
选项构建 C++ 库,可能已经遇到了错误。
为什么要这么多关于 C++ 的讨论?这不是 Python 模块吗? ------我听到你这么说。其实像其他几个 Apache Arrow 库实现一样,Python 模块主要是围绕 C++ 库的包装器,通过绑定 Python 类和函数来提供功能。
PyArrow 的架构------Cython 不只是一个聪明的名字
pyarrow
模块主要是将 C++ Arrow 库中的功能通过一个更加 Python 风格的接口暴露出来。在某些情况下,它几乎是一一映射的;但在其他情况下,C++ 类和函数则是作为基础,用于创建更易于使用的对象和结构。
Cython (cython.org) 在此过程中起到很大帮助,它使代码管理更容易,并且可以很轻松地创建绑定。
实际上,Cython 是一种编程语言。它的目标是成为 Python 语言的超集,同时提供对静态类型声明的支持。简单来说,Cython 将专门的 Cython 代码转换为优化的 C/C++ 代码,并将其编译为 Python 扩展模块。结果是,你可以通过编写大部分 Python 代码,同时享受编译 C/C++ 程序的性能优势,以及与其他库的紧密集成。
在 pyarrow 模块中,Cython 的架构如图 11.4 所示。
正如你所见,代码设计分为四个层次(从上到下):
*.py
文件构成了pyarrow
模块的公共 Python 接口。其下的所有内容都应视为pyarrow
模块的"内部"部分。lib.pyx
文件将大部分核心 C++ Arrow 库功能暴露给 Python。它包含特定实现的*.pxi
文件,*.pyx
文件则包含胶合代码,用于将 C++ 功能作为 Python 类和方法暴露。该文件通过pyarrow.lib
暴露给 Python。includes/*.pxd
文件包含来自 Arrow 库的原始 C++ API 声明。在这一点上,它们类似于 C++ 头文件,在其他*.pyx
文件中被包含并用于实现类、函数和辅助工具。- 最后,我们有 C++ Arrow 库本身,以及位于 C++ 源代码
cpp/src/arrow/python
目录中的专用代码。这些代码提供了更底层的功能,比如与numpy
或pandas
对象的相互转换,以及在 C++ 代码中使用 Python 对象和回调的类。通过 CMake 添加ARROW_PYTHON=ON
选项编译后,生成了libarrow_python.dll
或libarrow_python.so
库。
现在我们已经完成了对 Cython 的简短解释,接下来可以回到构建该模块上。
回到构建模块...
为了管理我们的 Python 环境,我们将使用一个名为 venv
的工具。如果你使用的是 conda
工具,则应该使用 conda-activate
来激活开发环境。要使用 venv
设置环境,可以使用以下命令:
shell
$ python3 -m venv pyarrow-dev
$ source pyarrow-dev/bin/activate
$ pip install -r $ARROW_DIR/python/requirements-build.txt \
-r $ARROW_DIR/python/requirements-test.txt
$ mkdir dist # 这是我们将安装 Arrow 库的地方
如果你使用的是 Ubuntu/Debian 系统,可能需要运行以下命令单独安装 venv
:
arduino
$ sudo apt-get install -y python3-venv
现在我们已经安装了 Python 依赖项,让我们为 Arrow 的构建工具链设置几个环境变量。我们需要告诉 Python 构建在哪里可以找到 Arrow 库:
shell
$ export ARROW_HOME=$(pwd)/dist
$ export LD_LIBRARY_PATH=$(pwd)/dist/lib:$LD_LIBRARY_PATH
接下来,回到你之前创建并构建 Arrow C++ 库的目录。我们需要告诉它我们想把这些库安装在哪里;别担心------它不会重新构建库。你只需要再次运行 cmake
,传递两个新选项:
ini
$ cmake -DCMAKE_INSTALL_PREFIX=$ARROW_HOME \
-DCMAKE_INSTALL_LIBDIR=lib \
..
然后,运行 make install
或 ninja install
,具体取决于你之前使用的工具。运行 install
命令会将我们创建的 dist
目录填充为包含 Arrow 头文件和已编译库,从而使它们可用于 Python 构建。这也是确保你希望在 Python 模块中使用的任何功能在 C++ 构建中通过相应功能标志设置为 ON 的好时机。例如,如果你想在 pyarrow
构建中使用 Arrow Flight,你需要确保在构建 C++ 库时传递了 -DARROW_FLIGHT=ON
选项,等等。
处理构建问题
如果在 C++ 构建或安装命令中遇到问题,请参考上一节 Building the Arrow C++ libraries ,或查看 Arrow 的官方 C++ 开发文档,网址为 arrow.apache.org/docs/develo...。如果在 Windows 上进行构建,还会找到相应的说明,告诉你需要对这些说明进行哪些修改。
假设现在你已经解决了依赖关系,并将 C++ 库安装到了 ARROW_HOME
指向的目录中,你终于可以运行 Python 构建了。如果你构建了任何可选的 C++ 组件,例如 Flight 或 Gandiva,需要为每个组件设置相应的环境变量 PYARROW_WITH_<COMPONENT>=1
,以便在 PyArrow 中启用它们。让我们开始构建吧!
shell
$ cd $ARROW_DIR/python
$ export PYARROW_WITH_PARQUET=1
$ python3 setup.py build_ext --inplace
如果你希望构建一个包含 Arrow 和 Parquet C++ 库的自包含 Python wheel 以便轻松分发,可以通过如下方式设置 --bundle-arrow-cpp
选项:
ini
$ pip install wheel # 如果你还没有安装它
$ python3 setup.py build_ext --build-type=<release|debug> --bundle-arrow-cpp bdist_wheel
如果一切构建成功,你应该会在我们创建的 dist
目录中看到 PyArrow
的 .whl
文件。你可以通过运行以下命令安装该 wheel 包:
ruby
$ pip install <whl file>
如果在构建 Python 库时遇到困难,可以查看 Arrow 仓库中的基于 Docker 的示例。Docker 文件位于 $ARROW_DIR/python/examples/minimal_build
目录中。
构建 pyarrow
模块后,你现在可以运行单元测试了。我们使用 pytest
工具运行单元测试,测试可以使用以下命令运行:
shell
$ pytest $ARROW_DIR/python/pyarrow
pytest
有许多选项,可以控制运行哪些测试以及如何运行。你可以通过运行以下命令查看所有选项:
shell
$ pytest pyarrow --help
只剩下一个构建任务了!接下来我们来构建 Arrow 的 Go 模块。
构建 Arrow Go 模块
就个人而言,我发现 Go 的模块管理使构建 Go 模块比 C++ 和 Python 模块要简单得多。撰写本文时,Go 的最低版本要求为 1.16。如果你还没有安装 Go,可以按照 go.dev/doc/install 上的说明进行安装。
执行 Arrow Go 模块的最小化构建非常简单:
shell
$ cd $ARROW_DIR/go
$ go build ./...
就是这么简单。运行 go build
命令将自动下载依赖项,然后尝试编译模块。然后你可以使用 go test
实用程序运行单元测试:
shell
$ git submodule update --init --recursive # 如果你还没有拉取测试数据
$ export PARQUET_TEST_DATA=$ARROW_DIR/cpp/submodules/parquet-testing/data # 如果尚未设置
$ go test ./...
ok github.com/apache/arrow/go/v18/arrow 0.015s
...
Go 模块还使用了一些不同的 Go 构建标签来控制某些功能,可以通过在构建和测试命令中添加 -tags "tag1,tag2,..."
来启用这些标签。可用的标签如下:
- assert :在库中启用调试断言,在某些特定情况下可能触发
panic
或打印信息,例如多次调用Release
释放数组时。 - cdata_test :构建/运行使用
C Data API
的测试,测试位于cdata/test
目录中。 - test :构建并运行需要
cgo
的额外测试。 - ccalloc :构建
CgoAllocator
,需要可访问并进行链接的 C++ Arrow 库。 - noasm:禁用优化的汇编代码,强制使用纯 Go 实现的一些低级模块。
最后,你可以使用 go install
命令来安装集成测试命令行二进制文件:
shell
$ go install ./...
这将安装几个命令行实用程序,例如 parquet_reader
和 parquet_schema
,以及用于运行集成测试的二进制文件。
无论你是想更改 C++ 代码、Python 代码还是 Go 代码,我们已经介绍了如何构建你的更改并使用单元测试进行测试。一旦你的更改准备就绪,接下来只剩下涉及使用 Git 的几个步骤!
创建 Pull Request(PR)
如果你之前没有使用过 Git,可能需要一些时间来理解其概念。基本上,你有一个存储库的副本(在"设置 Git"部分中创建的 fork),以及官方的副本(我们称之为 upstream)。在存储库内,有各种分支,它们实际上是指向一组更改快照的指针。所谓的"真相源"是主分支(main 或 master 分支)。当你创建一个 PR 时,你实际上是在请求将你的更改合并到官方存储库的主分支中。为了封装你的更改并隔离工作空间,你应该在自己的存储库中创建分支。
创建分支时,显然需要命名它们!在处理 Arrow 库时,常见的约定是以 Jira 卡片编号命名分支。例如,如果你选择的 Jira 卡片编号是 arrow-12345,你可以使用以下命令创建分支:
css
$ git checkout -b arrow-12345-<clever descriptive name>
遵循此命名约定可以轻松跟踪你的工作分支与 Jira 中的特定问题相关联。当然,随着时间推移,你的本地版本可能会落后于官方存储库。只要你是在 fork 上创建分支进行工作,而不是直接对主分支进行更改,就可以轻松更新你的 fork!在 GitHub 上你的 fork 页面中,有一个非常方便的 "Fetch upstream" 按钮,如图 11.5 所示:
点击"Fetch and merge"按钮后,你的 fork 主分支将自动更新到官方存储库的最新提交。完成此操作后,你可以使用 git 命令更新本地版本:
ruby
$ git checkout main
$ git fetch
$ git pull
最后,使用 rebase
命令将所有这些更改拉入到你的工作分支中------简单明了:
css
$ git rebase main arrow-12345-<clever descriptive name>
如果没有冲突,那就大功告成了!所有内容都会更新完毕,你可以简单地使用 git push -f
命令推送更新后的分支。如果有冲突,则需要逐个文件手动解决。修复冲突后,使用 git add
重新添加修复的文件,并使用 git rebase --continue
继续你的 rebase 操作。关于更多 git 的使用细节和高级操作技巧,或者遇到问题时的帮助,你可以参考 Stack Overflow 或 Git 官方文档。
一旦你在本地 git 存储库的分支上提交了一些更改并将它们推送到你的 fork 中,就可以创建一个 PR。创建 PR 时,会触发一系列自动化作业,包括在不同环境中进行构建、运行测试以及检查代码格式。如果任何这些作业失败,你需要调查问题并解决。因此,接下来我们将介绍 CI 工作流和自动化作业的主要组件。
理解 Archery 和 CI 配置
Arrow 尽可能地兼容多种平台、编译器、操作系统和包管理器。因此,自动化工作流需要非常复杂,以应对这些不同的组合情况。
自动触发的作业
大部分的自动化作业都定义在 .github/workflows
文件夹中的 YAML 文件里。这些工作流大多是针对特定 Arrow 实现的,只会在相应代码有变更时运行,例如 PR 中包含 C++ 源文件时,会运行 C++ 构建。这些文件通常很容易找到,因为它们的命名与其运行的语言对应。此外,还有一些其他工作流需要注意:
-
archery.yml:用于验证 Archery 工具或其任务的更改。
-
comment_bot.yml:允许通过在 PR 上发表评论手动触发动作。它监听以下评论指令:
@github-actions crossbow submit ...
:Crossbow 是一个用于打包和测试 Arrow 部署的工具,这样的评论形式会运行指定的 Crossbow 命令。@github-actions autotune
:运行代码格式化工具,并将格式化结果提交回分支。它还会构建部分文档并将其提交回分支。当你修改文档时,这是一个有用且必要的操作。@github-actions rebase
:自动将 PR 变基到主分支。
-
dev_pr.yml :所有 PR 都会运行一系列检查。PR 的标题应始终为
ARROW-####: [组件] 标题
形式,其中ARROW-####
是该 PR 关联的 Jira 票号。如果标题格式不正确,会有一条评论提示用户修改。该工作流还会根据 PR 中修改的文件自动添加相关的 GitHub 标签,比如lang-c++
或lang-go
。 -
dev.yml:当 PR 中有任何活动发生时,此作业会运行代码检查器,并测试 PR 是否可以合并。
在 Arrow 仓库的根目录中,还有两个用于自动化作业的配置文件:
- .travis.yml :使用 Travis-CI 在 Arm 和 s390x 架构上运行自动化测试。
- appveyor.yml:定义了 appveyor 作业,用于在 Python 和 C++ 提交时运行 Windows 构建。
为了让开发任务更容易配置和执行,Arrow 仓库还包含一个名为 Archery 的 Python 工具。Archery 可以用于运行基准测试、对 Arrow 源代码进行 lint 检查,并构建 CI 任务中使用的 Docker 镜像。如果你希望在提交 PR 之前在本地运行这些任务和检查,可以使用 Archery 工具来执行这些操作,以及其他常见的开发任务。
使用 Archery 进行开发
Archery 需要 Python 3.8 或更高版本运行,同时需要 Docker 和 Docker Compose 工具。你可以在 Docker 官方文档 和 Docker Compose 文档 上找到安装说明。为了确保 Arrow 仓库中的任何更改(例如当你拉取仓库的最新版本时)能够自动与 Archery 安装进行同步,你应该以可编辑模式安装 Archery,这样它会保留对工作目录的引用,而不是将所有内容复制到 Python 环境中。你可以通过运行以下命令来完成:
shell
$ cd $ARROW_DIR
$ pip install -e "dev/archery[all]"
然后你可以在命令行中运行 archery
命令,并使用 --help
选项来查看使用说明。每个子命令都是独立的,并且经常有自己的帮助输出,因此可以探索各个选项。接下来,我们只会介绍其中的一部分。
使用 Archery 和 Docker 运行本地构建
Docker 构建是设计为可重用的容器,定义在组件的层级结构中。虽然这使得 Docker Compose 配置变得有些复杂,但它通过允许不同的语言绑定复用 C++ 环境配置,减少了重复。例如,依赖于 C++ 环境的各种语言绑定可以共享该环境的配置,而不是在每个环境中重复配置。
我不会在此介绍所有构建参数和选项,只会介绍足够帮助你理解构建过程的内容。如果需要更多详细信息,可以访问 Arrow 文档网站,该页面有更全面的说明。
以下是用于操作 Archery 和 Docker 镜像的基本命令:
- 列出可用的镜像 :
archery docker images
- 执行构建 :
archery docker run <build name>
,例如archery docker run debian-go-cgo
。运行构建时会拉取所有依赖的镜像,如果无法拉取则会在本地构建,然后运行请求的构建。 - 显示命令而不执行 :
archery docker run --dry-run <build name>
- 禁用镜像拉取并只在本地构建 :
archery docker run --no-cache <build name>
- 为构建添加环境变量 :
archery docker run --env VAR=value <build name>
,例如archery docker run --env CMAKE_BUILD_TYPE=release ubuntu-cpp
,强制进行 release 构建而不是 debug。 - 运行自定义命令而非默认命令 :
archery docker run <build name> <command>
,例如archery docker run ubuntu-cpp bash
,这会启动一个交互式 Bash 会话,允许你在交互模式下调试构建。
大多数 Docker 镜像都会调用构建和测试脚本,这些脚本位于 ci/scripts
目录中,并且通常按其功能命名。例如,cpp_build.sh
用于构建 C++ 库而不运行测试,而 cpp_test.sh
会被调用来运行这些测试。
这些脚本会利用环境变量来确保参数化,以便于配置。所有的 Docker 镜像都在仓库根目录的 docker-compose.yml
文件中定义。该文件有详细的注释,描述了如何添加新镜像以及所有内容的定义位置。查看此文件并了解变量的传递方式是最快的理解整体构建过程的途径。
现在,你已经掌握了如何进行更改、测试和检查自动化作业的知识。但如果你想要贡献却不知道如何或从何开始,该怎么办呢?接下来,我们来看看如何解决这个问题!
找到你的兴趣点并加以扩展
在本章的第一个部分《贡献开源项目》中,我们讨论了你为什么以及如何可以(或应该)为开源项目做出贡献。现在,我想提供一些友好的建议,帮助你找到一个适合开始贡献的地方,特别是如果你想为 Arrow 项目做出贡献的话。是的,最明显的方式就是查看你偏好编程语言的实现,但即便如此,大多数代码库仍然非常庞大,即便是只看与某种语言相关的部分代码。
我的第一个建议是找出你感兴趣的领域。我还绘制了一个小流程图(见图 11.6),帮助你产生一些想法:
当然,这并不能涵盖所有内容,也并非其目的。Arrow 库包含了许多不同的思想和多种实现方式。如果你对数据传输感兴趣,可以研究 Flight RPC 和 IPC。如果你对性能优化感兴趣,可以查看基准测试,并通过阅读注释找到与你想改进的部分相关的代码。也许你所喜欢的编程语言的库缺少某个功能?那么,其他语言的实现对你如何为你所用的库添加该功能会有很大的帮助。
最重要的是,订阅邮件列表!在 arrow-dev 邮件列表中,总是有很多有趣的讨论。这是一个形成想法并在深入研究 Jira 票据之前扩展想法的好地方。如果你不确定从哪里开始或如何做某件事,直接在邮件列表中提问,你很可能会得到有用的回复。另一个很好的参考点是现有的单元测试和示例,它们经常是如何利用尚未更新文档的新功能的极佳资源。
记住------虽然我不认识你,但我相信你能做到。你可以的!加入贡献者社区吧!
获得那令人期待的批准
所以,你找到了一个完美的 issue 来处理,创建了 PR,并且 CI 测试成功通过。接下来是什么?如何让你的贡献被接受并合并呢?
实际上,Apache Arrow 的核心开发者,或者那些在你修改的 Arrow 库相关领域有利益相关的人,将会审查你的 PR。为了确保获得良好的审查,请记住以下几点:
- 除了通过现有的单元测试,预期新功能还应添加新的单元测试,确保它得到了充分测试。
- 尽量将你的工作拆分为较小的、单一目的的补丁。要合并一个包含不相关功能的大型变更会困难得多。
- 遵循你正在修改的库的风格指南(关于这一点将在接下来的"以风格完成收尾!"部分讨论)。
一旦所有反馈都得到处理并且 PR 获得批准,其中一位提交者会将你的 PR 合并。恭喜!你刚刚贡献了你的第一个补丁!
以风格完成收尾!
在结束本章之前,我必须提到一个最后的话题------代码风格!为了避免在代码风格上的争论,Arrow 库的大多数实现都定义了非常明确的风格检查和代码格式化规则。这些规则通过我们之前提到的自动化 CI 检查来执行,并且要通过这些检查,才能合并 PR。我们不会花太多时间在这上面,因为这些规则是通过工具强制执行的,但了解使用了哪些工具还是很有帮助的。
C++ 代码风格
Arrow 遵循 Google 的 C++ 风格指南,但有一些例外:
- 行长度放宽到 90 个字符。
- 在头文件中使用
NULLPTR
宏,而不是nullptr
,以支持更多的 C++ 版本。 - 在使用结构体时有一些放宽的规则,方便开发。
代码风格使用 clang-format
和 clang-tidy
工具强制执行,可以通过 Archery 使用以下命令运行:
css
$ archery lint --cpplint --clang-format --clang-tidy --fix
源文件和头文件应使用下划线作为单词分隔符,而编译生成的可执行文件则使用连字符。更多关于 C++ 代码风格的细节,包括注释风格等,参考 Arrow C++ 开发文档 和 Arrow C++ 约定。
Python 代码风格
Arrow 的 Python 代码遵循类似 PEP 8 的风格,这与 pandas 项目的代码风格相似。同样地,Python 代码的风格检查也可以通过 Archery 本地运行,命令如下:
css
$ archery lint --python
如果添加 --fix
参数,部分发现的问题会自动修复,简单明了。
Go 代码风格
Arrow 的 Go 模块代码需要使用标准的 go fmt
工具进行格式化。CI 中会使用 staticcheck
工具来对 Go 代码进行静态分析。你可以通过以下命令在本地安装这个工具:
go
$ go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck
工具会安装在 $GOPATH/bin
目录中,最简单的使用方法是将该路径添加到 PATH
环境变量中。然后,你可以在 Arrow 仓库的 go/
子目录中,使用 staticcheck ./...
命令对本地代码进行检查。
总结
为任何开源项目做贡献都可以成为一种丰富的体验。只要项目能够建立起开发者和用户的社区,它就能持续发展。这些贡献者和用户是任何成功的开源项目的生命线;因此,如果你对项目的持续存在有兴趣,贡献吧!Apache Arrow 项目目前是一个相当活跃的项目,拥有一个充满活力的社区,社区成员对数据和分析充满热情。看到你想要的功能得到优先开发的最简单方法就是参与讨论,或者更好的是,如果你可以,自己贡献这些功能!这个社区就是新功能和新版本发布的动力来源。Arrow 项目管理委员会通过开发邮件列表公开投票,决定是否接受新的提议功能或发布新版本。加入讨论吧!
我们即将接近旅程的尾声了,只剩下最后一章------第 12 章,未来开发和计划。我们刚刚回顾了社区成员如何为 Arrow 库贡献和扩展内容的过程。最后,我们将让你兴奋起来,了解即将推出的功能、讨论和技术。我们将讨论如何使用 Arrow 进行地理空间数据处理、如何持续推动整个生态系统的协作,以及如何扩展 ADBC 的采用工作。
欢迎加入社区,享受其中吧!现在,来帮助我们成长!