构建 LLVM 发行版¶
简介¶
本文档面向希望构建和打包 LLVM 以及任何 LLVM 子项目工具组合以进行分发的人员。本文档涵盖了 LLVM 构建系统的有用功能,以及关于打包 LLVM 的最佳实践和一般信息。
如果您不熟悉 CMake,您可能会发现使用 CMake 构建 LLVM 或CMake 入门 文档很有用。本文档中介绍的一些内容是高级构建配置 文档中描述的构建的内部工作原理。
通用发行版指南¶
在构建编译器的发行版时,通常建议执行编译器的引导构建。这意味着使用主机工具链构建“阶段 1”编译器,然后使用“阶段 1”编译器构建“阶段 2”编译器。这样做是为了使您分发的编译器受益于新编译器提供的所有错误修复、性能优化和一般改进。
在决定如何构建您的发行版时,您需要评估一些权衡。主要的两项是
发行版的编译时间与构建的编译器的性能
发行版的二进制文件大小与构建的编译器的性能
最大化生成的编译器性能的指导原则是使用 LTO、PGO 并静态链接所有内容。这将导致整体发行版更大,并且生成时间更长,但它为编译器提供了最大的优化机会。
最小化发行版大小的指导原则是将 LLVM 和 Clang 库动态链接到工具中以减少代码重复。这将对生成的二进制文件造成很大的性能损失,因为这减少了优化机会,并且因为动态链接需要在进程启动时解析符号,这对 C++ 代码来说可能非常慢。
构建具有合理性能的发行版的最简单示例捕获在位于 clang/cmake/caches/DistributionExample.cmake 中的 DistributionExample CMake 缓存文件中。以下命令将执行并安装发行版构建
$ cmake -G Ninja -C <path to clang>/cmake/caches/DistributionExample.cmake <path to LLVM source>
$ ninja stage2-distribution
$ ninja stage2-install-distribution
install
和 install-distribution
之间的区别¶
需要注意的一件微妙但重要的事情是install
和install-distribution
目标之间的区别。install
目标预计将安装您的构建配置为生成的 LLVM 的每个部分,除了 LLVM 测试工具。或者,install-distribution
目标(推荐用于构建发行版)仅安装在配置时由LLVM_DISTRIBUTION_COMPONENTS 指定的 LLVM 的特定部分。
此外,默认情况下,install
目标会将 LLVM 测试工具安装为公共工具。可以通过将LLVM_INSTALL_TOOLCHAIN_ONLY 设置为On
来很好地更改这一点。LLVM 工具旨在用于 LLVM 的开发和测试,并且仅应包含在支持 LLVM 开发的发行版中。
当使用LLVM_DISTRIBUTION_COMPONENTS 构建时,构建系统还会生成一个distribution
目标,该目标构建列表中指定的所有组件。这是一个方便的构建目标,允许仅构建已分发的部分,而无需构建所有配置的目标。
多发行版配置¶
上面描述的install-distribution
目标用于构建单个发行版。LLVM 的构建系统还支持构建多个发行版,这可以用于例如拥有一个仅包含工具的发行版和另一个用于库的发行版(以启用开发)。这些通过将LLVM_DISTRIBUTIONS 变量设置为保存所有发行版名称的列表(通常以大写字母开头,例如“Development”),然后将LLVM_<distribution>_DISTRIBUTION_COMPONENTS 变量设置为该发行版的目标列表来配置。对于每个发行版,构建系统都会生成一个install-${distribution}-distribution
目标,其中${distribution}
是小写的发行版名称,以安装该发行版。
每个发行版都会创建自己的一组 CMake 导出,而安装特定发行版的项目的 CMake 导出的目标名为${project}-${distribution}-cmake-exports
,其中${project}
是小写的项目名称,而${distribution}
是小写的发行版名称,除非项目是 LLVM,在这种情况下,目标仅命名为${distribution}-cmake-exports
。为了作为发行版的一部分包含这些目标,需要在LLVM_<distribution>_DISTRIBUTION_COMPONENTS 变量中明确包含它们。
与单个发行版设置不同,在构建多个发行版时,LLVM_RUNTIME_DISTRIBUTION_COMPONENTS 中指定的任何组件都不会自动添加到任何发行版中。相反,您必须在某个LLVM_<distribution>_DISTRIBUTION_COMPONENTS 列表中显式包含这些目标。
默认情况下,每个目标都可能出现在多个发行版中;目标将作为其出现在的所有发行版的一部分进行安装,并且它将由其最后出现的发行版导出(发行版的顺序是它们出现在LLVM_DISTRIBUTIONS 中的顺序)。我们还定义了一些覆盖目标(例如llvm-libraries
以安装所有 LLVM 库);目标可能出现在与其覆盖目标不同的发行版中,在这种情况下,目标将由其出现的发行版导出(而不是其覆盖目标出现的发行版)。如果您希望强制目标仅出现在一个发行版中,并且覆盖发行版与目标发行版保持一致,请将LLVM_STRICT_DISTRIBUTIONS 设置为On
。
我们强烈建议查看clang/cmake/caches/MultiDistributionExample.cmake
作为配置多个发行版的示例。
仅限库发行版的特殊说明¶
LLVM 最强大的功能之一是其优先考虑库的设计理念以及您可以使用 LLVM 的不同部分组合各种工具的方式。即使在这种情况下,也不支持使用BUILD_SHARED_LIBS。如果您想将 LLVM 作为共享库分发以在工具中使用,建议的方法是使用LLVM_BUILD_LLVM_DYLIB,并且您可以使用LLVM_DYLIB_COMPONENTS 配置 libLLVM 中包含哪些 LLVM 组件。注意:LLVM_BUILD_LLVM_DYLIB 在 Windows 上不可用。
优化 LLVM 的选项¶
我们的 CMake 构建系统支持四种主要的构建优化。在执行引导构建时,除了将阶段 1 编译器的CMAKE_BUILD_TYPE 设置为Release
之外,没有必要执行任何其他操作。这是因为更密集的优化执行起来成本很高,并且阶段 1 编译器会被丢弃。所有进一步描述的选项都应在阶段 2 编译器上设置,方法是使用 CMake 缓存文件,或在选项前加BOOTSTRAP_。
第一个也是最简单的使用方式是通过设置CMAKE_BUILD_TYPE 选项来设置编译器优化级别。感兴趣的主要值是Release
或RelWithDebInfo
。默认情况下,Release
选项使用-O3
优化级别,而RelWithDebInfo
使用-O2
。如果您想生成调试信息并使用-O3
,您可以覆盖 C 和 CXX 的CMAKE_<LANG>_FLAGS_RELWITHDEBINFO 选项。DistributionExample.cmake 这样做了。
另一个易于使用的选项是链接时优化。您可以在阶段 2 构建上将LLVM_ENABLE_LTO 选项设置为Thin
或Full
以启用使用 LTO 构建 LLVM。这些选项会显着增加发行版中二进制文件的链接时间,但它会创建更快的二进制文件。如果您的发行版包含静态归档文件,则不应使用此选项,因为归档文件内的对象将是 LLVM 位代码,这是不可移植的。
高级构建配置 文档描述了用于生成 LLVM 分析信息以驱动 Profile-Guided-Optimization 的内置工具。树内分析测试非常有限,并且生成配置文件需要大量时间,但它可以显着提高生成的二进制文件的性能。
除了 PGO 分析之外,我们还在树内对生成链接器顺序文件提供了有限的支持。这些文件为链接器提供了一个建议的函数在最终二进制文件布局中的顺序。这可以通过物理地对在时间上彼此接近调用的函数进行分组来显着加快 clang 的速度。当前的工具仅在具有dtrace(1)
的 Darwin 系统上可用。值得注意的是,dtrace 是不确定的,因此使用 dtrace 的顺序文件生成也是不确定的。
减小尺寸的选项¶
警告
采取的任何减少二进制文件大小的措施都会以生成的二进制文件运行时性能为代价。
减少二进制文件大小最简单且影响最小的方式是将CMAKE_BUILD_TYPE变量设置为MinSizeRel
,这将把编译器优化级别设置为-Os
,该级别针对二进制文件大小进行优化。这种方法对大小的改善效果最小,对性能的影响也最小。
减少二进制文件大小最有效的方式是将LLVM动态链接到所有工具中。通过减少基于LLVM的工具之间公共代码的重复,从而减小代码大小。可以通过将以下两个CMake选项设置为On
来实现:LLVM_BUILD_LLVM_DYLIB和LLVM_LINK_LLVM_DYLIB。
警告
发行版构建时绝不应使用BUILD_SHARED_LIBS CMake选项。(请参阅上面的警告以获取更多解释)。
相关的CMake选项¶
本节提供有关旨在帮助构建发行版的CMake选项的文档。这不是一个详尽的列表,许多其他选项在使用CMake构建LLVM页面中有记录。一些已经记录的关键选项包括:LLVM_TARGETS_TO_BUILD、LLVM_ENABLE_PROJECTS、LLVM_ENABLE_RUNTIMES、LLVM_BUILD_LLVM_DYLIB和LLVM_LINK_LLVM_DYLIB。
- LLVM_ENABLE_RUNTIMES:STRING
在构建包含LLVM运行时项目(例如libcxx、compiler-rt、libcxxabi、libunwind等)的发行版时,务必使用刚构建的编译器来构建这些项目。
- LLVM_DISTRIBUTION_COMPONENTS:STRING
此变量可以设置为LLVM构建系统组件的以分号分隔的列表,用于安装。所有基于LLVM的工具都是组件,大多数库和运行时也是如此。组件名称与构建系统目标的名称匹配。
- LLVM_DISTRIBUTIONS:STRING
此变量可以设置为发行版的以分号分隔的列表。有关此变量和其他用于配置多个发行版的CMake变量的详细信息,请参阅上面的多发行版配置部分。
- LLVM_RUNTIME_DISTRIBUTION_COMPONENTS:STRING
此变量可以设置为运行时库组件的以分号分隔的列表。它与LLVM_ENABLE_RUNTIMES一起使用,用于指定要包含在发行版中的运行时库组件。与LLVM_DISTRIBUTION_COMPONENTS一样,组件名称与构建系统目标的名称匹配。
- LLVM_DYLIB_COMPONENTS:STRING
此变量可以设置为LLVM库组件的以分号分隔的名称。LLVM库组件要么是删除了LLVM前缀的库名称(例如Support、Demangle等),要么是LLVM目标名称,要么是特殊用途的组件名称。特殊用途的组件名称为
all
- 所有可用的LLVM组件库Native
- 本机系统的LLVM目标AllTargetsAsmParsers
- 所有包含的目标ASM解析器库AllTargetsDescs
- 所有包含的目标描述库AllTargetsDisassemblers
- 所有包含的目标反汇编器库AllTargetsInfos
- 所有包含的目标信息库
- LLVM_INSTALL_TOOLCHAIN_ONLY:BOOL
此选项默认为
Off
:当设置为On
时,它会从默认的install
目标中删除许多LLVM开发和测试工具以及组件库。对于发行版,不建议包含开发工具,因为许多LLVM工具仅用于开发和测试。