distcc

出自 ArchWiki

distcc 是一个程序,用于在网络中的多台机器上分发 C、C++、Objective C 或 Objective C++ 代码的构建,以加速构建过程。它应该始终生成与本地构建相同的结果,安装和使用都很简单,并且通常比本地编译快得多。此外,还可以将其与 Arch 原生构建工具(如 makepkg)一起使用。

术语

客户端
客户端是发起编译的计算机。
志愿者
志愿者是接受客户端发送的编译请求的计算机。可以设置多个志愿者,也可以只设置一个。

安装

安装 distcc 软件包到 distcc 集群中所有参与的计算机上。对于其他发行版,甚至包括使用 Cygwin 的 Windows 操作系统,请参阅 distcc 文档 或包含的 man 手册页 distcc(1)distccd(1)。请确保允许通过 distcc 运行端口(默认为 3632/tcp)的流量,请参阅 Category:Firewalls

配置

运行模式

Distcc 可以以普通模式(默认)或泵模式运行。从高层次来看,关键区别在于 distcc 如何处理预处理的源代码。普通模式传输完整的源代码和编译器参数。预处理保留在客户端上。泵模式将预处理和编译都分发到 distcc 集群,这在许多情况下更有效率和速度更快。有关更多详细信息,请参阅 distcc(1)

志愿者

志愿者的配置存储在 /etc/conf.d/distccd 中。至少,添加 --allow-private 开关,它涵盖了许多 ipv4 私有网络范围,或者如果你的网络支持 ipv6,则使用 --allow 和你的 ipv6 CIDR。将日志记录到文件对于在需要时进行故障排除也很有用。

DISTCC_ARGS="--allow-private --log-file /tmp/distccd.log"

或者,如果你需要允许 ipv6 访问并且你的网络 CIDR 是 /64

DISTCC_ARGS="--allow-private --allow aaaa:bbbb:cccc:dddd:eeee:::/64 --log-file /tmp/distccd.log"

如果机器上存在多个接口,请考虑传递 --listen ADDRESS 选项。还可以定义其他选项。请参阅 distccd(1)

启动 每个参与的志愿者上的 distccd.service。要使 distccd.service 在启动时启动,请启用它。

客户端

与 makepkg 一起使用

在以下部分编辑 /etc/makepkg.conf

  1. BUILDENV 数组将需要取消 distcc 的禁用,即列出它时不要带感叹号。
  2. 取消注释 DISTCC_HOSTS 行,并添加志愿者的主机名或 IP 地址。可选地,在此之后添加一个正斜杠和它们要使用的最大线程数。后续节点应以空格分隔。此列表应从最强大到最弱(处理能力)排序。
  3. 调整 MAKEFLAGS 变量,使其大致对应于每台服务器最大线程数的两倍。在下面的示例中,这是 2x(9+5+5+3)=44。
注意: 可以使用主机名而不是 IP 地址,但如果目的是使用 devtools 构建脚本进行构建,则当前不支持构建根中的名称解析,因此在这种情况下请坚持使用 IP 地址。使用 IP 地址的另一个原因是,在运行 pi-hole 进行名称解析的网络上,pi-hole 的日志将记录来自 distcc 的每个请求,从而用数千行多余的日志来垃圾邮件日志。
警告: -march=native 标志不能在 CFLAGSCXXFLAGS 变量中使用,否则 distccd 将不会将工作分发到其他机器。

应该注意的是,没有真正的通用配置。尝试一种,测试它,将结果与其他设置进行比较。以下是一些常见的设置

普通模式示例
BUILDENV=(distcc fakeroot color !ccache check !sign)
MAKEFLAGS="-j44"
DISTCC_HOSTS="localhost/9 192.168.10.2/5 192.168.10.3/5 192.168.10.4/3"
泵模式示例
BUILDENV=(distcc fakeroot color !ccache check !sign)
MAKEFLAGS="-j70"
DISTCC_HOSTS="localhost/9 192.168.10.2,cpp,lzo 192.168.10.3,cpp,lzo 192.168.10.4,cpp,lzo"

这里有几件事需要指出

  • 泵模式通常比普通模式在较高的 MAKEFLAGS 值下表现更好。
  • 在泵模式下,IP 或主机名后缀为文字 ',cpp,lzo',这是泵模式所要求的。此外,此示例中的 localhost 没有后缀。这意味着 distcc 将加载 localhost 定义的 9 个作业,并更积极地将代码生成分发给志愿者。可能在较大的集群中,人们可能希望将 localhost 上的本地作业数限制为更少,以允许处理分发到集群。也可以对 localhost 使用 ,cpp,lzo 后缀。
  • 如上所述,没有一个单一的配置可以有效地与所有 distcc 集群一起工作/确定最佳设置是通过测试和基准测试凭经验得出的。

不与 makepkg 一起使用

普通模式示例

客户端上 distcc 的最小配置包括设置可用的志愿者和重新定义 PATH

$ export PATH="/usr/lib/distcc/bin:$PATH"
$ export DISTCC_HOSTS="localhost/9 192.168.10.2/5 192.168.10.3/5 192.168.10.4/3"
泵模式示例
$ export PATH="/usr/lib/distcc/bin:$PATH"
$ export DISTCC_HOSTS="localhost/9 192.168.10.2,cpp,lzo 192.168.10.3,cpp,lzo 192.168.10.4,cpp,lzo"

编译

使用 makepkg

普通模式示例

一旦配置了 /etc/makepkg.conf,就不需要特殊步骤。只需像往常一样调用 makepkg 即可。

泵模式示例

无论是否使用 makepkg 或在 shell 上,用户都必须在编译之前启动 pump。由于 pump 包括检查以确保正确配置了一组 DISTCC_HOSTS,我们需要首先定义一个虚假的 DISTCC_HOSTS 行。请记住,makepkg 将使用 /etc/makepkg.conf 中指定的值。

$ export DISTCC_HOSTS="localhost,cpp,lzo"
$ eval $(pump --startup)

现在像往常一样调用 makepkg

完成后,可以选择停止 pump

$ pump --shutdown
提示: 或者,使用 pump 调用 makepkg,它将在 makepkg 完成后自动关闭 include 服务器。

不使用 makepkg

普通模式示例

在导出 #不与 makepkg 一起使用 中描述的两个变量后,只需调用编译器

$ make -j44

某些程序可能需要定义 CC 和/或 CXX 变量才能正常工作

$ make -j44 CC=distcc CXX=distcc

泵模式示例

如上所示启动 pump。编译与普通模式没有什么不同。

使用 CMake

使用以下 CMake 选项来构建使用 distcc 的基于 CMake 的项目

$ cmake -DCMAKE_C_COMPILER_LAUNCHER=distcc -DCMAKE_CXX_COMPILER_LAUNCHER=distcc ...

监控进度

distcc 附带了一个 cli 监视器 distccmon-text,可以使用它来检查编译状态。

cli 监视器可以通过在命令后附加一个空格和整数来连续运行,该整数对应于等待重复查询的秒数

$ distccmon-text 3
29291 Preprocess  probe_64.c                                 192.168.10.2[0]
30954 Compile     apic_noop.c                                192.168.10.2[0]
30932 Preprocess  kfifo.c                                    192.168.10.2[0]
30919 Compile     blk-core.c                                 192.168.10.2[1]
30969 Compile     i915_gem_debug.c                           192.168.10.2[3]
30444 Compile     block_dev.c                                192.168.10.3[1]
30904 Compile     compat.c                                   192.168.10.3[2]
30891 Compile     hugetlb.c                                  192.168.10.3[3]
30458 Compile     catalog.c                                  192.168.10.4[0]
30496 Compile     ulpqueue.c                                 192.168.10.4[2]
30506 Compile     alloc.c                                    192.168.10.4[0]

使用 distcc 进行交叉编译

可以使用 distcc 来帮助交叉编译

  • 运行目标架构的机器必须用作客户端。
  • 非原生架构的志愿者将帮助编译,但他们需要安装相应的工具链,并且他们的 distccd 指向它。

Arch Linux ARM 作为客户端(x86_64 作为志愿者)

本节详细介绍了如何使用 Arch Linux (x86_64) 志愿者来帮助 Arch ARM 设备进行交叉编译。请参阅 这些测试,以了解仅使用一台 x86_64 机器帮助 ARM 设备编译即可实现 2-4 倍的速度提升的证据。

志愿者

Arch ARM 开发者强烈建议使用官方项目 工具链,该工具链应安装在 x86_64 志愿者上。与其手动管理这些工具链,不如使用 AUR 提供的两个工具链以及配置和 systemd 服务单元

包含 arm/arm64 工具链的志愿者上的设置与 #志愿者 相同,只是配置和 systemd 服务文件的名称与相应软件包的名称匹配。例如,对于 armv7h,配置文件是 /etc/conf.d/distccd-armv7h,systemd 服务单元是 distccd-armv7h.service

请注意,每个工具链都在唯一的端口上运行,因此如果需要,所有四个工具链都可以共存于志愿者上。请确保允许流量流向 distcc 运行的端口,请参阅 Category:Firewallsdistcc(1)

目标架构 Distcc 端口
armv7h 3635
armv8h/aarch64 3636

客户端

设置 Arch ARM 客户端的最简单方法是使用 distccd-arch-armAUR。它提供了涵盖所有四种 Arch ARM 风格的所有四个配置和 systemd 服务单元。例如,如果 Arch ARM 客户端正在运行 armv7h 镜像,可以选择编辑 /etc/conf.d/distccd-armv7h 并更改其中的默认值。准备构建时,启用 distccd-armv7h.service 并编译。

有关更详细的教程,请参阅 usage-examples

如果有人宁愿在不使用上面提到的 AUR 软件包的情况下设置客户端,则客户端的手动设置与 #客户端 相同,只是需要修改以下两个文件以定义志愿者预期使用的现在非标准端口。如果使用 AUR 软件包,请参阅上表。

  1. /etc/conf.d/distccd:armv7h 机器上的示例:DISTCC_ARGS="--allow-private --log-level info --log-file /tmp/distccd-armv7h.log --port 3635"
  2. /etc/makepkg.conf:armv7h 机器上的示例:DISTCC_HOSTS="192.168.10.2/5:3635 192.168.10.3/5:3635"
关于 ARM 客户端上的 localhost 的注意事项

当在使用 x86_64 志愿者构建 Arch ARM 设备时,强烈建议从 DISTCC_HOSTS 中排除 localhost 指令,因为许多 ARM 设备没有所需的处理能力。

为了说明这种效果,请考虑以下编译 linux 内核 5.10.44 版本的 Image 目标的示例。客户端是 RPi4B (aarch64),志愿者 (192.168.1.288) 是一台四核/超线程 Intel 机器。每个编译任务只运行一次,并且在它们之间运行了 make clean

运行 make -j15 Image CC=distcc CXX=distcc
DISTCC_HOSTS= 时间 (mm:ss) 速度降低倍数
"192.168.1.288:3636/9" 6:50 -
"localhost/5 192.168.1.288:3636/9" 10:34 2.8倍
"192.168.1.288:3636/9 localhost/5" 10:13 2.7倍

Arch Linux (x86_64) 作为客户端 (Arch ARM 作为志愿者)

本节详细介绍如何使用 Arch ARM 志愿者来帮助 x86_64 客户端进行交叉编译。请参阅这些测试,以证明即使使用 1 个 Arch ARM 志愿者也可以显着加快编译时间,而最多 2 个志愿者可以使收益翻倍。

客户端

客户端的设置与 #客户端 相同,distcc 在标准端口 3632 上运行。

志愿者

distccd-x86_64AUR 将提供一个工具链,安装在 Arch ARM 设备上以启用交叉编译。

其他工具链

  • EmbToolkit: 用于创建交叉编译工具链的工具;支持 ARM 和 MIPS 架构;支持构建基于 LLVM 的工具链
  • crosstool-ng: 类似于 EmbToolkit;支持更多架构(请参阅网站以获取更多信息)
  • Linaro: 提供用于 ARM 开发的工具链

EmbToolkit 提供了一个友好的图形配置菜单 (make xconfig) 用于配置工具链。

故障排除

编译 Arch Linux 内核软件包的怪癖

如果从官方 PKGBUILD (或许多来自 AUR 的 PKGBUILD) 构建内核,由于内核被硬编码为使用 GCC 插件,而由于技术原因 distccd 无法支持这些插件,因此 distcc 将无法工作。

一种解决方法是编辑内核源代码,删除对 GCC 插件的硬编码要求。这可以通过在 PKGBUILD 本身中插入 make 步骤之前的 sed 单行命令来完成

sed -i '/HAVE_GCC_PLUGINS/d' arch/x86/Kconfig

未能这样做将导致 distcc 在构建期间无法工作。请参阅 FS#64275

另一种选择是将 CC=distcc 和 CXX=distcc 变量作为构建命令的一部分传递

make all CC=distcc CXX=distcc

编译 chromium 的怪癖

编译使用 clang 的 chromium 目前受到 issue#386 的影响。为了规避此错误,请将以下内容添加到 PKGBUILD 中的 _flags 数组中

'is_cfi=false'
'use_gold=false'
'clang_use_default_sample_profile=false'
'chrome_pgo_phase=0'

Journalctl

使用 journalctl 查找出错的原因

# journalctl $(which distccd) -e --since "5 min ago"

调整日志级别

默认情况下,distcc 将日志记录到 /var/log/messages.log。一个技巧(实际上在 distccd 手册页中推荐)是直接记录到备用文件。同样,可以在 RAM 中通过 /tmp 找到它。另一个技巧是降低要包含在日志文件中的最低严重性错误级别。LEVEL 可以是任何标准 syslog 级别,特别是 critical、error、warning、notice、info 或 debug。

在客户端上使用此处提到的参数调用 distcc,或将其附加到志愿者上的 /etc/conf.d/distccd 中的 DISTCC_ARGS

DISTCC_ARGS="--allow 192.168.10.0/24 --log-level error --log-file /tmp/distccd.log"

通过重定位 $HOME/.distcc 限制 HDD/SSD 使用量

默认情况下,distcc 创建 $HOME/.distcc,它存储瞬态相关信息,因为它为节点提供编译工作。这将避免不必要的 HDD 读/写,对于 SSD 尤其重要。

$ export DISTCC_DIR=/tmp/distcc

对于 distccd-alarm

没有那个文件或目录

类似于以下的错误表明用户错误地运行了 distcc 提供的 distccd 服务,而不是 distccd-alarm 软件包提供的服务(即 distccd-alarm-armv7hAUR,或 distccd-alarm-armv8AUR。)

请务必为目标架构启动正确的服务。

distcc[25479] (dcc_execvp) ERROR: failed to exec armv7l-unknown-linux-gnueabihf-g++: No such file or directory

Avahi-daemon 在 distccd.service 启动时停止发布

本文或本节的 фактическая точность находится под сомнением。

原因: 如果用户稍后重启服务,这并不能解决任何问题。(在 Talk:Distcc 中讨论)

distccd.service 作为服务启动可能会导致 avahi-daemon 停止工作。可以通过确保 avahi-daemon.servicedistccd.service 之后启动来缓解这种情况,方法是编辑 avahi-daemon.service 的单元文件(请参阅 Systemd#编辑提供的单元文件)并在 [Unit] 部分的末尾添加 After=distccd.service

/etc/systemd/system/avahi-daemon.service
...
[Unit]
Description=Avahi mDNS/DNS-SD Stack
Requires=avahi-daemon.socket
After=distccd.service
...

另请参阅