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.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-lldb
、check-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 blame
和 git 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 上其他地方可能习惯的流程有所不同。在项目内部,不同子项目的期望也可能有所不同。
因此,无论您为哪个项目做贡献,都要知道我们不期望完美。如果您不确定,请随时提问,并期望如果您遗漏了某些内容,会有人礼貌地指出并帮助您解决。
评论¶
审查者可以在更改上留下评论,您可以回复。有些评论附加到特定行,并与代码交错出现。您可以回复这些评论。也许是为了澄清所问的问题,或者告诉审查者您已完成所要求的内容。