Distributed compiling with Gentoo Linux and WSL2
The year is 2021 and I've decided to embark on a journey to update an old laptop running an installation of Gentoo Linux that hasn't been updated since 2015. I've since used macOS and I've recently settled on a Windows 11 desktop running WSL2 as my main development environment. There's a lot of CPU power on this desktop sitting idle a lot of the time so I decided to share it with the old Gentoo laptop and have things running a little faster by giving distcc a go. In this article, I'll lay out the steps required to have it work. YMMV.
Distcc
Distcc is a program designed to distribute compiling tasks across a network to participating hosts. It comprises a server, distccd, and a client program, distcc. Distcc can work transparently with ccache, Portage, and Automake with a small amount of setup.
-- https://wiki.gentoo.org/wiki/Distcc
Please note that one of the requirements for using distcc
is that all of the computers on the network need to have the same GCC version.
Installing Gentoo on WSL
WSL 2 is a new version of the Windows Subsystem for Linux, a compatibility layer for running ELF64 Linux binaries natively on Windows 10, Windows 11, and Windows Server 2019. WSL 2 was announced in 2019, introducing changes such as a real Linux kernel, through a subset of Hyper-V features. This articles assumes that WSL 2 is already up and running on your Windows OS.
As mentioned in the preamble, I'm running WSL2 on a Windows 11 desktop. So, first things first, let's get Gentoo Linux installed in WSL. To install it I roughly followed the Gentoo in WSL guide.
A stage 3 tarball is required so grab one from your favourite Gentoo source mirror or the Gentoo downloads website. I've gone for an openrc
build (instead of systemd
). You will then need to extract it to a tar file. Note that at the time of writing the Gentoo in WSL guide shows a code example importing a *.tar.xz
file but that is incorrect. You want a *.tar
file instead.
I'll perform the next steps in a Windows PowerShell.
PS C:\Users\vitor\Downloads\Gentoo> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 29/11/2021 22:15 1009377280 stage3-amd64-openrc-20211128T170532Z.tar
After extracting the downloaded stage3 file import it into WSL using the command shown below. I've selected D:\wsl\Gentoo
as the installation directory.
wsl.exe --import Gentoo D:\wsl\Gentoo\ .\stage3-amd64-openrc-20211128T170532Z.tar.xz --version 2
This will create a ext4.vhdx
file in your desired location.
PS D:\wsl\Gentoo\> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 07/12/2021 16:24 14460911616 ext4.vhdx
Using the Gentoo distribution
You can now run your newly imported Gentoo Linux distribution:
PS C:\Users\vitor> wsl -d Gentoo
vitor-windows /mnt/c/Users/vitor #
Next we will configure Portage by setting some initial settings in /etc/portage/make.conf
. I'm using settings as recommended in this excellent article about setting up Gentoo in WSL. Note that we can't use -march=native
or -mtune=native
in the CFLAGS
or CXXFLAGS
variables of make.conf
when compiling with distcc
so I've set the CPU architecture explicitly (znver3
).
vitor-windows ~ # cat /etc/portage/make.conf
COMMON_FLAGS="-O2 -pipe"
CFLAGS="${COMMON_FLAGS} -march=znver3"
CXXFLAGS="${CFLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
# NOTE: This stage was built with the bindist Use flag enabled
PORTDIR="/var/db/repos/gentoo"
DISTDIR="/var/cache/distfiles"
PKGDIR="/var/cache/binpkgs"
# This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
LC_MESSAGES=C
USE="-X -gtk -nls"
# No hardware videocard support
VIDEO_CARDS="dummy"
ACCEPT_LICENSE=""
# Disable non-functional sandboxing features
FEATURES="-ipc-sandbox -pid-sandbox -mount-sandbox -network-sandbox"
# Define targets for QEMU
QEMU_SOFTMMU_TARGETS="aarch64 arm i386 riscv32 riscv64 x86_64"
QEMU_USER_TARGETS="aarch64 arm i386 riscv32 riscv64 x86_64"
Next we need to sync the portage tree:
vitor-windows ~ # emerge --sync
We are now ready to look at how to install and setup distcc.
Install and setup distcc
Install distcc
with emerge
:
vitor-windows ~ # emerge --ask sys-devel/distcc
Next, we need to configure Portage to use distcc. We will need to do this on both the WSL and the laptop (native) Gentoo installations. I'll configure the MAKEOPTS
and FEATURES
variables in make.conf
using the strategy presented in the Gentoo Distcc guide.
set the value of N to twice the number of total (local + remote) CPU cores + 1, and
set the value of M to the number of local CPU cores
We will also configure /etc/conf.d/distcc
in the laptop to allow both distcc clients to communicate with each other. It's important to note that both machines are in a local network in a 192.168.0.0/24
subnet.
WSL configuration
The WSL host has an AMD Ryzen 9 5900X processor with 12 cores and 24 thread processors as displayed in the /proc/cpuinfo
output.
vitor-windows ~ # uname -a
Linux vitor-windows 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 AMD Ryzen 9 5900X 12-Core Processor AuthenticAMD GNU/Linux
vitor-windows ~ # cat /proc/cpuinfo | grep processor | wc -l
24
Therefore:
vitor-windows ~ # cat /etc/portage/make.conf
...
MAKEOPTS="-j57 -l24"
FEATURES="${FEATURES} distcc"
Running either openrc
or systemd
in WSL2 is currently "problematic" so we will launch distccd
explicitly on the command-line and won't need to configure /etc/conf.d/distcc
here.
Laptop (ultrabook
) configuration
The old laptop is far less powerful than their new WSL companion:
vitor-ultrabook ~ # uname -a
Linux vitor-ultrabook 5.10.76-gentoo-r1 #1 SMP PREEMPT Sun Dec 5 15:27:52 WET 2021 x86_64 Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz GenuineIntel GNU/Linux
vitor-ultrabook ~ # cat /proc/cpuinfo | grep processor | wc -l
4
Portage configuration:
vitor-ultrabook ~ # cat /etc/portage/make.conf
...
MAKEOPTS="-j57 -l4"
FEATURES="${FEATURES} distcc"
Distcc configuration (launched by openrc
):
vitor-ultrabook ~ # cat /etc/conf.d/distccd
# /etc/conf.d/distccd: config file for /etc/init.d/distccd
DISTCCD_EXEC="/usr/bin/distccd"
DISTCCD_OPTS="${DISTCCD_OPTS} --port 3632"
# Logging
DISTCCD_OPTS="${DISTCCD_OPTS} --log-level debug --log-file /var/log/distcc/distccd.log"
# --allow is now mandatory as of distcc-2.18.
DISTCCD_OPTS="${DISTCCD_OPTS} --allow 192.168.0.0/24"
Windows and WSL networking
The last configuration piece is to expose the WSL internal IP address via port-forwarding so it's reachable by the latpop .
Here we see that the Windows host has a 192.168.0.32
assigned to it whereas the WSL2 adapter has a 172.24.80.1
IP address.
Windows IP Configuration
Wireless LAN adapter Wi-Fi:
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::a4c3:9fc2:486:7a2a%8
IPv4 Address. . . . . . . . . . . : 192.168.0.32
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.0.1
Ethernet adapter vEthernet (WSL):
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::e029:d3cf:4809:b22e%30
IPv4 Address. . . . . . . . . . . : 172.24.80.1
Subnet Mask . . . . . . . . . . . : 255.255.240.0
Default Gateway . . . . . . . . . :
And the IP address assigned to the Gentoo instance is 172.24.83.194
:
vitor-windows ~ # ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.24.83.194 netmask 255.255.240.0 broadcast 172.24.95.255
inet6 fe80::215:5dff:fe4c:3e71 prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:4c:3e:71 txqueuelen 1000 (Ethernet)
RX packets 1433177 bytes 1781034319 (1.6 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 388279 bytes 35333250 (33.6 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
In a PowerShell session with Administrator privileges setup port-forwarding with the netsh
command:
PS C:\Users\vitor> netsh interface portproxy add v4tov4 listenport=3632 listenaddress=0.0.0.0 connectport=3632 con
nectaddress=172.24.83.194
Once distcc has been started in WSL you can test the connection with Test-NetConnection
PS C:\Users\vitor> Test-NetConnection 172.24.83.194 -Port 3632
ComputerName : 172.24.83.194
RemoteAddress : 172.24.83.194
RemotePort : 3632
InterfaceAlias : vEthernet (WSL)
SourceAddress : 172.24.80.1
TcpTestSucceeded : True
You will also need to open and exception in your firewall to allow connections to the 3632
TCP port used by distccd
. If using the native Windows Defender Firewall you can run a couple of New-NetFirewallRule
commands to allow traffic to and from that port:
PS C:\Users\vitor> New-NetFirewallRule -DisplayName "Gentoo Distccd" -Direction Inbound -LocalPort 3632 -Action Allow -Protocol TCP
Name : {a5e66c0a-79c6-42d6-a6e6-29808b4b11b3}
DisplayName : Gentoo Distccd
Description :
DisplayGroup :
Group :
Enabled : True
Profile : Any
Platform : {}
Direction : Inbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : The rule was parsed successfully from the store. (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses : {}
PS C:\Users\vitor> New-NetFirewallRule -DisplayName "Gentoo Distccd" -Direction Outbound -LocalPort 3632 -Action Allow -Protocol TCP
Name : {4be6aa6a-ae65-44e0-b66a-5427dfaf32ea}
DisplayName : Gentoo Distccd
Description :
DisplayGroup :
Group :
Enabled : True
Profile : Any
Platform : {}
Direction : Outbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : The rule was parsed successfully from the store. (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses : {}
Distributed compiling with distccd
We are finally ready to launch distccd
on both machines and reap the benefits of all the setup work done so far.
Launch distcc in WSL
As previously mentioned we can't rely on openrc
in WSL to launch distccd
so we will launch it from the command-line as follows:
vitor-windows ~ # distccd --port 3632 --log-level debug --allow 192.168.0.0/16 --allow 172.24.0.0/16 --daemon --no-detach --user distcc
distccd[194] (dcc_discard_root) successfully set no_new_privs
distccd[194] (dcc_discard_root) discarded root privileges, changed to uid=240 gid=240
Launch distcc in the laptop
In the laptop, using openrc
, launching distccd
is as simple as:
vitor-ultrabook ~ # rc-service distccd start
Check that the WSL client is reachable using netcat
:
vitor-ultrabook ~ # nc -zv 192.168.0.32 3632
192.168.0.32: inverse host lookup failed:
(UNKNOWN) [192.168.0.32] 3632 (distcc) open
Start emerge'ing
Everything is now in place to emerge packages in the laptop, using distccd
to distribute code compilation with the Windows machine running WSL.
Just for the sake of showing what's happening under the hood I'll enable extra
distcc
debugging by setting the DISTCC_VERBOSE
environment variable in /etc/portage/bashrc
:
vitor-ultrabook ~ # cat /etc/portage/bashrc
export DISTCC_VERBOSE=1
Now when installing a package via Portage one can see pieces of source and compiled code being transferred between the distcc
clients. This should hopefully result in much faster builds than if it was solely built on a single machine unless you have a very slow network.
>>> Emerging (1 of 1) net-analyzer/nmap-7.92::gentoo
* nmap-7.92.tar.bz2 BLAKE2B SHA512 size ;-) ...
>>> Compiling source in /var/tmp/portage/net-analyzer/nmap-7.92/work/nmap-7.92 ...
make -j57 -l4 -C . makefile.dep
make: Entering directory '/var/tmp/portage/net-analyzer/nmap-7.92/work/nmap-7.92'
distcc[33] (dcc_trace_version) distcc 3.4 x86_64-pc-linux-gnu; built Nov 30 2021 09:08:35
distcc[33] (dcc_recursion_safeguard) safeguard level=0
...
distcc[442] (dcc_r_file) received 24560 bytes to file payload.o
distcc[442] (dcc_r_file_timed) 24560 bytes received in 0.006844s, rate 3504kB/s
distcc[442] 1136553 bytes from payload.cc compiled on 192.168.0.32 in 0.9942s, rate 1116kB/s
distcc[442] (dcc_unlock) release lock fd3
distcc[442] compile payload.cc on 192.168.0.32 completed ok
distcc[442] elapsed compilation time 0.995464s
distcc[442] (dcc_exit) exit: code 0; self: 0.000000 user 0.006772 sys; children: 0.094792 user 0.026804 sys
distcc[442] (dcc_cleanup_tempfiles_inner) deleted 2 temporary files
...
distcc[84] compile (null) on localhost completed ok
distcc[84] (dcc_unlock) release lock fd3
distcc[84] elapsed compilation time 0.250713s
distcc[84] (dcc_exit) exit: code 0; self: 0.002075 user 0.000000 sys; children: 0.184401 user 0.065695 sys
distcc[84] (dcc_cleanup_tempfiles_inner) deleted 0 temporary files
...
NMAP SUCCESSFULLY INSTALLED
>>> Completed installing net-analyzer/nmap-7.92 into /var/tmp/portage/net-analyzer/nmap-7.92/image
...
You can also verify that distcc
is doing its thing in the WSL host by keeping an eye on htop
while the package is being installed in the other machine. Pretty neat!
And that's all for today. Thanks for reading and good luck!
Credits: Header photo by Claudio Schwarz on Unsplash