构建 Clang 工具链

Fuchsia 将 Clang 用作官方编译器。

前提条件

您需要使用 CMake 3.13.4 或更高版本才能执行这些命令。这是构建 LLVM 的最低版本

虽然 CMake 支持不同的构建系统,但建议使用 Ninja

这两者都应该作为预构建文件显示在 Fuchsia 结账文件中。以下命令假定您的 PATH 中包含 cmakeninja

export PATH=${FUCHSIA_DIR}/prebuilt/third_party/cmake/${platform}/bin:${PATH}
export PATH=${FUCHSIA_DIR}/prebuilt/third_party/ninja/${platform}/bin:${PATH}

其中 ${FUCHSIA_DIR} 是指 Fuchsia 源代码树的根目录。

获取来源

以下示例命令使用 ${LLVM_SRCDIR} 来引用 LLVM 源代码树检出的根目录。您可以使用由 LLVM 社区维护的官方 monorepo https://github.com/llvm/llvm-project

LLVM_SRCDIR=${HOME}/llvm/llvm-project
git clone https://github.com/llvm/llvm-project ${LLVM_SRCDIR}
cd ${LLVM_SRCDIR}
git checkout ${REVISON_NUMBER}

紫红 IDK

在构建与工具链一起构建的运行时库之前,您需要 Fuchsia IDK(以前称为 SDK)。IDK 必须位于 ${IDK_DIR} 变量指向的目录中:

IDK_DIR=${HOME}/fuchsia-idk

如需下载最新的 IDK,请使用以下命令:

# For Linux
cipd install fuchsia/sdk/core/linux-amd64 latest -root ${IDK_DIR}

#### Generating RISC-V Libraries and Sysroot for the Fuchsia IDK

To build RISC-V LLVM runtime libraries for Fuchsia, you need to generate
RISC-V libraries and sysroot for Fuchsia IDK.

Since the script is going to change the content of
`${IDK_DIR}/pkg/sysroot/meta.json`, we need to make this file writable:

```bash
chmod 644 "${IDK_DIR}/pkg/sysroot/meta.json"

下一步是运行脚本以生成 RISC-V 库和 sysroot:

python3 ${FUCHSIA_DIR}/scripts/clang/generate_sysroot.py --sdk-dir=${IDK_DIR} \
  --arch=riscv64 \
  --ifs-path=${FUCHSIA_DIR}/prebuilt/third_party/clang/${platform}/bin/llvm-ifs

对于 Linux x64 平台,${platform} 应为 linux-x64;在 Mac x64 平台上,${platform} 应为 mac-x64

适用于 Linux 的 Sysroot

如需添加适用于 Linux 的编译器运行时和 C++ 库,请下载 sysroot。该文件必须位于 ${SYSROOT_DIR} 变量所指向的目录中。

SYSROOT_DIR=${HOME}/fuchsia-sysroot/

您可以使用以下命令下载 sysroot:

cipd install fuchsia/third_party/sysroot/linux integration -root ${SYSROOT_DIR}

为 Fuchsia 构建 Clang 工具链

Clang CMake 构建系统支持引导加载程序(也称为多阶段)build。Fuchsia 为 Clang 编译器使用两阶段引导 build。不过,对于工具链相关的开发,建议使用单阶段构建

如果您的目标是使用 Clang 进行实验,单阶段 build 可能是您想要的。 第一阶段编译器是主机专用编译器,第二阶段需要设置一些选项。第二阶段编译器是面向用户提供的经过全面优化的编译器。

设置这些编译器需要很多选项。为了简化配置,CMake 缓存文件包含 Fuchsia Clang 构建设置,这些文件是 Clang 代码库(Fuchsia.cmakeFuchsia-stage2.cmake)的一部分。

mkdir llvm-build
mkdir llvm-install  # For placing stripped binaries here
INSTALL_DIR=${pwd}/llvm-install
cd llvm-build

单阶段 build Fuchsia 配置

为 Fuchsia 开发 Clang 时,您可以使用缓存文件测试 Fuchsia 配置,但仅运行第二阶段(在停用 LTO 的情况下)。这样一来,即使是增量开发,也能缩短构建时间,而无需手动指定所有选项:

cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DLLVM_ENABLE_LTO=OFF \
  -DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DFUCHSIA_SDK=${IDK_DIR} \
  -DCMAKE_INSTALL_PREFIX= \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
  ${LLVM_SRCDIR}/llvm
ninja toolchain-distribution  -j1000  # Build the distribution

如果上述操作失败并出现与 Ninja 相关的错误,那么您可能需要将 ninja 添加到 PATH 中。您可以在 ${FUCHSIA_DIR}//prebuilt/third_party/ninja/${platform}/bin 中找到预构建的可执行文件。

ninja toolchain-distribution 应该足以构建所有二进制文件,但 Fuchsia 构建会假定某些库已被删除,因此有必要使用 ninja install-toolchain-distribution-stripped

两阶段 build Fuchsia 配置

这大致相当于在 prod 构建器上运行的代码,用于构建 Fuchsia 提供给用户的工具链。

cmake -GNinja \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DCMAKE_INSTALL_PREFIX= \
  -DSTAGE2_LINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DSTAGE2_LINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DSTAGE2_FUCHSIA_SDK=${IDK_DIR} \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia.cmake \
  ${LLVM_SRCDIR}/llvm
ninja stage2-toolchain-distribution -j1000
DESTDIR=${INSTALL_DIR} ninja stage2-install-toolchain-distribution-stripped -j1000

runtime.json

如果由于缺少 runtime.json 文件而导致 Fuchsia build 失败,您必须运行以下命令来生成新的 runtime.json 文件:

python3 ${FUCHSIA_DIR}/scripts/clang/generate_runtimes.py  \
  --clang-prefix ${INSTALL_DIR} --sdk-dir ${IDK_DIR}          \
  --build-id-dir ${INSTALL_DIR}/lib/.build-id > ${INSTALL_DIR}/lib/runtime.json

生成的文件包含 Fuchsia build 用于了解工具链中各种库的位置的相对路径。

融会贯通

复制并粘贴用于构建单阶段工具链的代码。此代码可以从 LLVM build 目录中运行,并假定使用 Linux 环境。

cd ${LLVM_BUILD_DIR}  # The directory your toolchain will be installed in

# Environment setup
FUCHSIA_DIR=${HOME}/fuchsia/  # Replace with wherever Fuchsia lives
LLVM_SRCDIR=${HOME}/llvm/llvm-project  # Replace with wherever llvm-project lives
IDK_DIR=${HOME}/fuchsia-idk/
SYSROOT_DIR=${HOME}/fuchsia-sysroot/
CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/
GOMA_DIR=${FUCHSIA_DIR}/prebuilt/third_party/goma/linux-x64/

# Download necessary dependencies
cipd install fuchsia/sdk/core/linux-amd64 latest -root ${IDK_DIR}
cipd install fuchsia/third_party/sysroot/linux integration -root ${SYSROOT_DIR}

# CMake invocation
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DLLVM_ENABLE_LTO=OFF \
  -DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DFUCHSIA_SDK=${IDK_DIR} \
  -DCMAKE_INSTALL_PREFIX= \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
  ${LLVM_SRCDIR}/llvm

# Build and strip binaries and place them in the install directory
ninja toolchain-distribution -j1000
DESTDIR=${INSTALL_DIR} ninja install-toolchain-distribution-stripped -j1000

# Generate runtime.json

python3 ${FUCHSIA_DIR}/scripts/clang/generate_runtimes.py    \
  --clang-prefix ${INSTALL_DIR} --sdk-dir ${IDK_DIR}            \
  --build-id-dir ${INSTALL_DIR}/lib/.build-id > ${INSTALL_DIR}/lib/runtime.json

使用自定义 Clang 构建 Fuchsia

如需指定用于构建 Fuchsia 的自定义 Clang 工具链,请将 --args clang_prefix=\"${INSTALL_DIR}/bin\" --no-goma 传递给 fx set 命令并运行 fx build

fx set core.x64 --args=clang_prefix=\"${INSTALL_DIR}/bin\" --no-goma
fx build

此文件包含 Fuchsia build 用于了解工具链中各种库的位置的相对路径。

开发 Clang

在开发 Clang 时,您可能希望使用更适合增量开发和较短周转时间的设置。

构建 LLVM 的最简单方法是使用以下命令:

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" \
  ${LLVM_SRCDIR}/llvm
ninja

您可以使用 LLVM_ENABLE_PROJECTS 变量启用其他项目。如需启用所有常见项目,您应使用:

  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;compiler-rt;libcxx;libcxxabi;libunwind"

同样,您还可以允许将某些项目构建为运行时,这意味着这些项目将使用刚构建的而不是主机编译器来构建:

  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" \
  -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \

LLVM_ENABLE_PROJECTSLLVM_ENABLE_RUNTIMES 已在 CMake 缓存文件中进行设置,因此,除非您想要明确添加更多项目或运行时,否则通常无需设置这两项。

Clang 是一个大项目,编译器性能极为关键。为了缩短构建时间,建议使用 Clang 作为主机编译器,在可能的情况下,使用 LLD 作为主机链接器。理想情况下,应使用 LTO 进行构建,并结合使用配置文件引导型优化 (PGO) 以获得最佳性能。

如需将主机编译器设置为 Clang 并将主机链接器设置为 LLD,您可以使用以下额外标志:

  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_ENABLE_LLD=ON

这假定 ${CLANG_TOOLCHAIN_PREFIX} 指向带有尾随斜杠的 Clang 安装的 bin 目录(因为此 Make 变量在 Zircon build 中可用)。例如,如需在 Fuchsia Checkout(在 Linux 上)中使用编译器,请执行以下命令:

CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/

消毒用品

您可以在 LLVM 工具中使用大多数排错程序,只需将 LLVM_USE_SANITIZER=<sanitizer name> 添加到 CMake 调用中即可。不过,MSan 比较特殊,因为某些 LLVM 工具会触发假正例。要使用 MSan 支持进行构建,您首先需要构建具有 MSan 支持的 libc++。您可以在同一 build 中执行此操作。如需设置支持 MSan 的 build,请先使用 LLVM_USE_SANITIZER=MemoryLLVM_ENABLE_LIBCXX=ON 运行 CMake。

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;libcxx;libcxxabi;libunwind" \
  -DLLVM_USE_SANITIZER=Memory \
  -DLLVM_ENABLE_LIBCXX=ON \
  -DLLVM_ENABLE_LLD=ON \
  ${LLVM_SRCDIR}/llvm

通常,此时您会运行 Ninja,但是我们希望使用经过排错的 libc++ 版本构建所有内容,但如果现在进行构建,它将使用 ${CLANG_TOOLCHAIN_PREFIX} 中的 libc++,并且未经过排错。首先,我们仅构建 cxx 和 cxxabi 目标。当工具动态链接到 libcxx 时,这些变量将用于取代 ${CLANG_TOOLCHAIN_PREFIX} 中的变量。

ninja cxx cxxabi

现在您已经有了经过净化的 libc++ 版本,接下来您可以将 build 设置为使用该版本,而不是 ${CLANG_TOOLCHAIN_PREFIX} 中的版本,然后构建所有内容。

ninja

综上所述:

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_USE_SANITIZER=Address \
  -DLLVM_ENABLE_LIBCXX=ON \
  -DLLVM_ENABLE_LLD=ON \
  ${LLVM_SRCDIR}/llvm
ninja libcxx libcxxabi
ninja

测试 Clang

如需运行 Clang 测试,您可以使用 check-<component> 目标:

ninja check-llvm check-clang

您可以使用 check-all 运行所有测试,但请注意,此过程可能会耗费大量时间,具体取决于您在 build 中启用的项目数量。

如需仅测试一项特定测试,您可以使用环境变量 LIT_FILTER。如果测试路径是 clang/test/subpath/testname.cpp,您可以使用:

LIT_FILTER=testname.cpp ninja check-clang

通过指定不同的 check-<component>,可以在其他子项目中运行测试时应用相同的技巧。

从 CAS 下载工具链

我们的 Clang 工具链 CI 构建器会将所有构建工件上传到内容地址存储 (CAS)。它提供了一种便捷的方式,您可以快速下载特定工具链,而无需从头开始构建。 这可以大大加快调查工具链问题的速度,因为除了构建 Fuchsia 之外,还可以避免较长的 LLVM 构建时间。

以下示例展示了如何从头开始安装 cas 工具并将特定工具链下载到语料库目录中:

$ cipd install infra/tools/luci/cas/linux-amd64 latest -root luci
$ ./luci/cas download -cas-instance chromium-swarm -digest \
    ad53e1f315a849955190594fde6b07e11e76b40563db5779fcc69d6a6e04dc71/267 -dir corpus

在上面的示例中,系统会向 -digest 字段传递一个唯一 ID,cas 工具使用该 ID 提取正确的工件。如需从 Fuchsia 的 CI 构建器中获取 digest,请选择所需工具链对应的构建器,然后展开 clang->cas->archive 字段,然后点击 CAS_UI 链接。随后出现的页面将显示有关 CAS 上传情况的一些信息,包括摘要。

实用的 CMake 标志

还有许多其他 CMake 标志对构建非常有用,但这些标志可能对工具链构建有用。

增加可并行(本地)运行的关联作业的数量。链接作业的数量取决于 RAM 大小。对于 LTO build,每个作业至少需要 10GB 空间。

其他资源

文档:

讲座: