Fuchsia 使用 Clang 作为官方编译器。
前提条件
您需要使用 CMake 3.13.4 版或更高版本才能执行这些命令。这是构建 LLVM 所需的最低版本。
虽然 CMake 支持不同的构建系统,但建议使用 Ninja。
两者都应以预构建的形式出现在 Fuchsia 结账中。以下命令假定 cmake 和 ninja 位于您的 PATH 中:
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}
Fuchsia 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}
适用于 Linux 的 sysroot
如需包含 Linux 的编译器运行时和 C++ 库,请下载 sysroot。它必须位于 ${SYSROOT_DIR} 变量指向的目录中。
SYSROOT_DIR=${FUCHSIA_DIR}/prebuilt/third_party/sysroot
为 Fuchsia 构建 Clang 工具链
Clang CMake 构建系统支持引导(也称为多阶段)构建。Fuchsia 使用 Clang 编译器的两阶段引导加载程序 build。不过,对于与工具链相关的开发,建议使用单阶段 build。
如果您的目标是尝试使用 clang,那么单阶段 build 可能正是您所需要的。第一阶段编译器是仅限主机的编译器,其中设置了一些第二阶段所需的选项。第二阶段编译器是完全优化的编译器,旨在提供给用户。
设置这些编译器需要很多选项。为了简化配置,Fuchsia Clang 构建设置包含在 CMake 缓存文件中,这些文件是 Clang 代码库(Fuchsia.cmake 和 Fuchsia-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 \
-DLLVM_ENABLE_LTO=OFF \
-DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_riscv64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/ubuntu20.04 \
-DLINUX_armv7-unknown-linux-gnueabihf_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_i386-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-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 build 假设某些库已被剥离,因此需要 ninja
install-toolchain-distribution-stripped。
两阶段构建 Fuchsia 配置
这大致相当于在生产 buildbot 上运行的内容,用于构建 Fuchsia 向用户提供的工具链。
cmake -GNinja \
-DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
-DCMAKE_INSTALL_PREFIX= \
-DSTAGE2_LINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DSTAGE2_LINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DSTAGE2_LINUX_riscv64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/ubuntu20.04 \
-DSTAGE2_LINUX_armv7-unknown-linux-gnueabihf_SYSROOT=${SYSROOT_DIR}/linux \
-DSTAGE2_LINUX_i386-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-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
如果 Fuchsia build 因缺少 runtime.json 文件而失败,您必须运行以下命令来生成新的 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=${FUCHSIA_DIR}/prebuilt/third_party/sysroot
CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/
# Download necessary dependencies
cipd install fuchsia/sdk/core/linux-amd64 latest -root ${IDK_DIR}
# CMake invocation
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
-DLLVM_ENABLE_LTO=OFF \
-DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_riscv64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/ubuntu20.04 \
-DLINUX_armv7-unknown-linux-gnueabihf_SYSROOT=${SYSROOT_DIR}/linux \
-DLINUX_i386-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR}/linux \
-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\" 传递给 fx set 命令并运行 fx build。
fx set core.x64 --args=clang_prefix=\"${INSTALL_DIR}/bin\"
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_PROJECTS 和 LLVM_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 代码库中的编译器(在 Linux 上),请执行以下操作:
CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/
消毒用品
通过在 cmake 调用中添加 LLVM_USE_SANITIZER=<sanitizer name>,大多数排错程序都可用于 LLVM 工具。不过,MSan 比较特殊,因为某些 LLVM 工具会触发误报。如需构建支持 MSan 的 libc++,您首先需要构建支持 MSan 的 libc++。您可以在同一 build 中执行此操作。如需设置支持 MSan 的 build,请先使用 LLVM_USE_SANITIZER=Memory 和 LLVM_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++,而该 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 构建器会将所有构建工件上传到 Content Addressed Storage (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 标志对构建很有用,但以下标志可能对构建工具链很有用。
-DLLVM_PARALLEL_LINK_JOBS
增加可并行运行(本地)的关联作业数量。链接作业的数量取决于 RAM 大小。对于 LTO build,您需要为每个作业预留至少 10 GB 的空间。
其他资源
文档:
讲座: