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 源代码。您需要 最新版本的 <host_cpp_toolchain> Clang、GCC 或 Visual Studio。

  • 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:这是“主”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.json 的符号链接(或副本)到 llvm-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

这些是调用 LLVM 函数并验证结果的 C++ 程序。它们位于像 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 周围的各个“团队”。团队成员定期为这些特定领域贡献和审查代码,因此,如果您没有指定任何特定的人员,团队成员之一会审查您的变更。

审查流程

当您发起一个 Pull Request 时,一些自动化操作会添加评论并根据您修改的部分通知子项目的不同成员。

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

评论

审查者可以在变更上留下评论,您可以回复。一些评论附加到特定的行上,并与代码交错显示。您可以回复这些评论。例如,澄清提问内容或告知审查者您已完成其要求。

更新您的变更

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

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

审查期望

为了使 LLVM 成为一项长期可持续的努力,代码需要具有可维护性和良好的测试性。代码审查有助于实现这一目标。特别是对于新贡献者而言,这通常意味着多次审查和对不适合项目整体架构的设计决策进行回退。

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

  • 要友好,并期望审查者也友好——LLVM 有一份 行为准则,每个人都应该遵守;

  • 要有耐心——理解新功能如何融入项目的架构通常需要花费时间,并且人们需要在生活中处理其他责任;如果一周内没有收到回复,请提醒一下审查

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

接受 Pull Request

当审查者对变更感到满意时,他们会**批准**Pull Request。他们可能会留下一些较小的评论,您应该在合并之前解决这些评论,但此时审查已完成。是时候合并了!

提交权限

代理提交

由于这是您的第一次变更,您还没有权限自行合并。审查者**并不知道这一点**,因此您需要告诉他们!在审查中留下如下评论:

感谢 @<审查者用户名>。我没有提交权限,您能否帮我合并此 PR?

Pull Request 将被关闭,您会收到 GitHub 的通知。

获取提交权限

一旦您为 LLVM 贡献了一些补丁,就开始考虑获取自己的提交权限。如果您满足以下条件,这可能是一个好主意:

  • 您已完成 3-5 个比“修复错别字”范围更大的补丁

  • 您愿意审查与您的补丁相关的变更

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

流程在 开发者策略文档 中进行了描述。

能力越大,责任越大

实际上,现在是时候阅读其余的 开发者策略 了。

PR 合并后的问题

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

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

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

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

注意

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

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

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

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

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

回退

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

如果您不确定您的更改是否可以快速修复,请将其回退。然后您有充足的时间来调查并产生一个可靠的修复。

其他人可能会为您回退您的更改,或者您可以使用 GitHub 界面 创建一个回退 Pull Request。如果可能,将您原来的审查者添加到此新的 Pull Request 中。

结论

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

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

因此,无论您贡献什么,请知道我们并不期望完美。如果您不确定,请随时提问,并期望如果您错过了什么,会有人礼貌地指出并帮助您解决。