MyFirstTypoFix

简介

本教程将指导您完成对 LLVM 进行更改并将其贡献回 LLVM 项目的过程。

注意

此处展示的代码更改仅为示例,并非您实际应提交给 LLVM 项目的内容。对于您对 LLVM 的第一个实际更改,代码会有所不同,但本指南的其余部分仍然适用。

我们将对 Clang 进行更改,但 LLVM 其他部分的步骤是相同的。即使我们将要进行的更改很简单,我们也将涵盖诸如构建 LLVM、运行测试和代码审查之类的步骤。这是一个很好的实践,您将为进行更大的更改做好准备。

我们假设您

  • 知道如何使用编辑器,

  • 具有基本的 C++ 知识,

  • 知道如何在您的系统上安装软件,

  • 熟悉命令行,

  • 具有 git 的基本知识。

我们所做的更改

Clang 对无限递归有一个警告

$ echo "void foo() { foo(); }" > ~/test.cc
$ clang -c -Wall ~/test.cc
test.cc:1:12: warning: all paths through this function will call itself [-Winfinite-recursion]

这已经足够清楚了,但不够吸引人。让我们稍微改进一下措辞

test.cc:1:12: warning: to understand recursion, you must first understand recursion [-Winfinite-recursion]

依赖项

我们需要一些工具

  • git:用于检出 LLVM 源代码,

  • C++ 编译器:用于编译 LLVM 源代码。您需要 Clang、GCC 或 Visual Studio 的最新版本 <host_cpp_toolchain>

  • CMake:用于配置如何在您的系统上构建 LLVM,

  • ninja:运行 C++ 编译器以(重新)构建 LLVM 的特定部分,

  • python:用于运行 LLVM 测试。

例如,在 Ubuntu 上

$ sudo apt-get install git clang cmake ninja-build python

构建 LLVM

检出

源代码存储在 Github 上的一个大型仓库(“monorepo”)中。

下载可能需要一段时间!

$ git clone https://github.com/llvm/llvm-project.git

这将创建一个名为“llvm-project”的目录,其中包含所有源代码。(匿名检出是可以的 - 推送提交使用不同的机制,我们稍后会看到。)

配置您的工作区

在构建代码之前,我们必须通过运行 CMake 来配置确切的构建方式。CMake 结合了来自三个来源的信息

  • 您做出的显式选择(这是调试构建吗?)

  • 从您的系统检测到的设置(库安装在哪里?)

  • 项目结构(哪些文件是 ‘clang’ 的一部分?)

首先,创建一个用于构建的目录。通常,这是 llvm-project/build

$ mkdir llvm-project/build
$ cd llvm-project/build

现在,运行 CMake

$ cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang

如果一切顺利,您会看到很多“正在执行测试”行,最后

Configuring done
Generating done
Build files have been written to: /path/llvm-project/build

您应该在当前目录中看到一个 build.ninja 文件。

让我们分解一下最后一个命令

  • -G Ninja:告诉 CMake 我们将使用 ninja 进行构建,并创建 build.ninja 文件。

  • ../llvm:这是“main” LLVM 项目的源代码路径

  • 两个 -D 标志设置 CMake 变量,这些变量会覆盖 CMake/项目默认值

    • CMAKE_BUILD_TYPE=Release:以优化模式构建,这是(令人惊讶地)最快的选项。

      如果您想在调试器下运行,则应使用默认的 Debug(完全未优化,并且会导致 >10 倍的测试运行速度减慢)或 RelWithDebInfo,这是一个折衷方案。

      断言默认情况下在 Release 构建中未启用。您可以使用 LLVM_ENABLE_ASSERTIONS=ON 启用它们。

    • LLVM_ENABLE_PROJECTS=clang:除了 LLVM 本身之外,此列表列出了您有兴趣构建的 LLVM 子项目。可以列出多个项目,用分号分隔,例如 clang;lldb。在本例中,我们将对 Clang 进行更改,因此我们只添加 clang。

最后,创建 llvm-project/build/compile-commands.jsonllvm-project/ 的符号链接(或副本)

$ ln -s build/compile_commands.json ../

(这对于构建和测试不是绝对必要的,但允许诸如 clang-tidy、clang-query 和 clangd 之类的工具在您的源代码树中工作)。

构建和测试

最后,我们可以构建代码了!首先执行此操作非常重要,以确保我们在进行更改之前处于良好状态。但是要构建什么呢?在 ninja 中,您指定一个 目标。如果我们只想构建 clang 二进制文件,我们的目标名称是“clang”,我们运行

$ ninja clang

第一次构建会非常慢 - Clang + LLVM 是很多代码。但是增量构建很快:ninja 只会重建已更改的部分。当它最终完成时,您应该有一个可用的 clang 二进制文件。尝试运行

$ bin/clang --version

还有一个用于构建和运行所有 clang 测试的目标

$ ninja check-clang

这是 LLVM 中的常见模式:check-llvm 是所有针对 LLVM 核心的检查,其他项目具有类似 check-lldbcheck-flang 等的目标。

进行更改

更改

我们需要找到包含错误消息的文件。

$ git grep "all paths through this function" ..
../clang/include/clang/Basic/DiagnosticSemaKinds.td:  "all paths through this function will call itself">,

出现在 DiagnosticSemaKinds.td 中的字符串是 Clang 打印的字符串。*.td 文件定义表 - 在本例中,它是 clang 可以发出的警告和错误及其消息的列表。让我们在您喜欢的编辑器中更新消息

$ vi ../clang/include/clang/Basic/DiagnosticSemaKinds.td

找到消息(它应该在 warn_infinite_recursive_function 下)。将消息更改为“为了理解递归,你必须首先理解递归”。

再次测试

为了验证我们的更改,我们可以构建 clang 并手动检查它是否有效。

$ ninja clang
$ bin/clang -c -Wall ~/test.cc
test.cc:1:12: warning: in order to understand recursion, you must first understand recursion [-Winfinite-recursion]

我们还应该运行测试以确保我们没有破坏任何东西。

$ ninja check-clang

请注意,这次构建速度更快,但测试运行时间仍然很长。Ninja 不知道哪些测试可能会受到影响,因此它会运行所有测试。

********************
Failing Tests (1):
    Clang :: SemaCXX/warn-infinite-recursion.cpp

嗯,这很有道理……测试输出表明它正在寻找旧字符串“call itself”,但找到了我们的新消息。请注意,随着时间的推移添加新测试,可能会有更多测试以类似方式失败。

让我们通过更新测试中的期望来修复它。

$ vi ../clang/test/SemaCXX/warn-infinite-recursion.cpp

在我们看到 // expected-warning{{call itself}}(或来自原始警告文本的类似内容)的任何地方,让我们将其替换为 // expected-warning{{to understand recursion}}

现在我们可以再次运行所有测试,但这是一种缓慢的迭代更改方式!相反,让我们找到一种方法来重新运行特定的测试。LLVM 中主要有两种类型的测试

  • lit 测试(例如 SemaCXX/warn-infinite-recursion.cpp)。

这些是花哨的 shell 脚本,它们运行命令行工具并验证输出。它们存在于类似 clang/**test**/FixIt/dereference-addressof.c 的文件中。像这样重新运行

$ bin/llvm-lit -v ../clang/test/SemaCXX/warn-infinite-recursion.cpp
  • 单元测试(例如 ToolingTests/ReplacementTest.CanDeleteAllText

这些是 C++ 程序,它们调用 LLVM 函数并验证结果。它们存在于像 ToolingTests 这样的套件中。像这样重新运行

$ ninja ToolingTests && tools/clang/unittests/Tooling/ToolingTests --gtest_filter=ReplacementTest.CanDeleteAllText

本地提交

我们将更改保存到本地 git 分支。这使我们可以在审查更改时处理其他事情。更改应具有标题和描述,以向审查者和代码的未来读者解释进行更改的原因。

现在,我们只添加标题。

$ git checkout -b myfirstpatch
$ git commit -am "[clang][Diagnostic] Clarify -Winfinite-recursion message"

现在我们准备将此更改发送到世界!

[clang][Diagnostic] 前缀是我们称之为标签的东西。这种宽松的约定告诉 git 日志的读者,更改正在修改哪些区域。如果您不知道您更改的模块的标签,您可以查看存储库这些区域的提交历史记录。

$ git log --oneline ../clang/

或者使用 GitHub,例如 https://github.com/llvm/llvm-project/commits/main/clang

标记是不精确的,因此如果您不确定要放什么,请不要担心。如果审查者认为需要,他们会提出一些建议。

代码审查

上传更改以供审查

LLVM 代码审查通过 GitHub 上的拉取请求进行,请参阅 GitHub 文档,了解如何在 GitHub 上打开拉取请求。

寻找审查者

LLVM 社区中的任何人都可以审查更改。对于更大更复杂的更改,重要的是审查者具有 LLVM 领域的经验,并且非常了解设计目标。更改的作者通常会分配特定的审查者。git blamegit log 可用于查找可以进行审查的以前的作者。

我们的 GitHub 机器人还会标记和通知 LLVM 周围的各个“团队”。团队成员定期为这些特定领域贡献和审查代码,因此如果您不选择任何特定的人,他们中的一位将审查您的更改。

审查流程

当您打开拉取请求时,一些自动化会添加评论并根据您更改的部分通知子项目的不同成员。

在几天之内,应该有人开始审查。他们可能会将自己添加为审查者,或者只是开始留下评论。每当审查更新时,您都会收到另一封电子邮件。有关更多详细信息,请参阅 代码审查政策

评论

审查者可以在更改上留下评论,您可以回复。有些评论附加到特定行,并与代码交错出现。您可以回复这些评论。也许是为了澄清所问的问题,或者告诉审查者您已完成所要求的内容。

更新您的更改

如果您根据审查者的评论进行更改,只需使用更多提交更新您的分支,然后推送到您的 GitHub llvm-project 分支。最好直接回复审查者的评论,而不是期望他们再次阅读所有更改。

例如,您可能会评论“我已经完成了。”或“我能够完成这部分,但对……有疑问”。

审查期望

为了使 LLVM 成为一项长期的可持续努力,代码需要是可维护的且经过良好测试的。代码审查有助于实现这一目标。特别是对于新的贡献者,这通常意味着多轮审查和对不符合项目整体架构的设计决策的反驳。

对于您的第一个补丁,这意味着

  • 友善,并期望审查者也友善回应 - LLVM 有一个 行为准则,每个人都应该遵守;

  • 耐心 - 理解新功能如何适应项目的架构通常需要花费时间,人们必须在生活中将此与其他责任相权衡;如果没有回复,请每周 ping 一次审查

  • 如果您无法达成一致,通常最好的方法是按照审查者的要求去做;我们针对代码的可读性进行优化,审查者更适合判断;如果感觉这不是正确的选择,您可以在评论中询问他们或添加另一位审查者以获得第二意见。

接受拉取请求

当审查者对更改感到满意时,他们将 批准 拉取请求。他们可能会留下一些您应在合并之前解决的较小评论,但在此时审查已完成。是时候将其合并了!

提交权限

通过代理提交

由于这是您的第一个更改,您尚无权自行合并它。审查者 不知道这一点,因此您需要告诉他们!在审查中留下类似以下的评论

谢谢 @<审查者的用户名>。我没有提交权限,您可以为我合并此 PR 吗?

拉取请求将被关闭,您将收到 GitHub 的通知。

获取提交权限

一旦您向 LLVM 贡献了一些补丁,就开始考虑自己获取提交权限。如果出现以下情况,这可能是一个好主意

  • 您已经合并了 3-5 个范围大于“修复拼写错误”的补丁

  • 您愿意审查与您的更改密切相关的更改

  • 您希望继续为 LLVM 做出贡献。

该过程在 开发者政策文档 中进行了描述。

能力越大

实际上,现在也是阅读其余 开发者政策 的好时机。

PR 合并后出现的问题

提交更改后,它将被自动构建机器人拾取,这些机器人将在各种配置中构建和测试您的补丁。

http://lab.llvm.org/buildbot/#/console 上的“控制台”视图显示特定提交的结果。如果您想跟踪您的更改如何影响构建机器人,这应该是首先查看的地方。

列是构建配置,行是单个提交。沿着行是彩色气泡。气泡的颜色代表构建的状态。绿色表示通过,红色表示失败,黄色表示正在构建中。

在提交您的更改之前,红色构建可能已经失败。这意味着您没有破坏构建,但您应该检查您是否通过添加新问题使其变得更糟。

注意

控制台视图中仅显示最近的更改。如果您的更改不在那里,请依靠 PR 评论和构建机器人电子邮件来通知您任何问题。

如果包含您的更改的构建中存在问题,您可能会收到电子邮件报告或作为 PR 上的评论。请检查问题是否是由您的更改专门引起的。由于构建包含来自许多作者的更改,有时会由于不相关的基础设施问题而失败。

要查看构建的详细信息,请单击控制台视图中的气泡,或问题报告中提供的链接。您将能够查看和下载该构建每个阶段的日志。

如果您需要帮助理解问题,或有任何其他问题,您可以在您的 PR 上作为评论提出,或在 Discord 上提出。

如果您没有收到任何问题报告,则无需您采取任何行动。您的更改按预期工作,做得好!

回退

如果您的更改导致了问题,应尽快将其回退。这是 LLVM 开发 的正常部分,每个提交者(无论经验多么丰富)都会经历。

如果您对您的更改是否可以快速修复有任何疑问,请回退它。然后您有足够的时间来调查并生成可靠的修复程序。

其他人可能会为您回退您的更改,或者您可以使用 GitHub 界面 创建回退拉取请求。如果可能,请将您的原始审查者添加到这个新的拉取请求中。

结论

现在您应该了解向 LLVM 项目贡献的生命周期。

如果某些细节仍然不清楚,请不要担心。LLVM 项目的流程与您在 GitHub 上其他地方可能习惯的流程有所不同。在项目内部,不同子项目的期望也可能有所不同。

因此,无论您为哪个项目做贡献,都要知道我们不期望完美。如果您不确定,请随时提问,并期望如果您遗漏了某些内容,会有人礼貌地指出并帮助您解决。