Up to Main Index                                       Up to Annexed Works

                  BUILDING GO FROM SOURCE ON A RASPBERRY PI

Building Go from source on a Raspberry Pi, especially a Raspberry Pi Zero, can
be a challenge. These are notes are from my experience building Go from source
on machines with limited resources. These notes are for building a Go source
distribution downloaded from https://golang.org and are not about compiling
normal user Go code…

The original Raspberry Pi Zero, and Zero W, are cheap and tiny computers with
limited resources that are capable of running a full Linux distribution. They
have a single core BCM2835 32-bit processor running at 1GHz with 512Mb RAM.
The storage is usually limited to an SD card, although an SSD or thumb-drive
can be attached via the single micro USB port using an OTG adapter.

If Go can be built on these diminutive systems, then we should be able to
build Go on any model of Raspberry Pi or similar single-board computer.

For these notes a Raspberry Pi Zero W was used. The operating system used was
an official Raspberry Pi OS image. The version of Go built was Go 1.20.4. The
SDCard used was a 16Gb Class 10 A1 SanDisk.

A previous installation of Go 1.17.13 was used to build Go 1.20.4. If there
is no previous version of Go installed then download and build the latest Go
1.4 release first, which does not require Go to build. Then build Go 1.20.4.

Additional information on building Go from source can be found on the official
Go website: https://go.dev/doc/install/source

Feel free to take and adapt these notes as you see fit. Feedback in the form
of comments, improvements or errata is always welcome: diddymus@wolfmud.org

                                    ∞ ∞ ∞

The first step should be to setup the RAM reserved for graphics to be as small
as possible. This frees up more memory for the operating system and Go build
to use. In the /boot/config.txt file, the gpu_mem setting should be set to 16,
take note of its current value so it can be restored after the build:


    gpu_mem=16


A reboot will be required for the change to take effect. Once rebooted, as
many background services as possible should be stopped to free up additional
memory. Which services are running on your Raspberry Pi will depend on what
software has been installed. Logging in to the Raspberry Pi from the console
or over SSH, instead of running a graphical interface, is highly recommended.
This will free up more memory for the build. The more free memory the quicker
the build will be.

A small swap space will be provided by a memory backed zram device that
compresses and decompresses its content automatically. Setup the swap space,
as root, using:


    >swapoff -a
    >zramctl --find -a lz4 -s 32M
    /dev/zram0
    >mkswap /dev/zram0
    Setting up swapspace version 1, size = 32 MiB (33550336 bytes)
    no label, UUID=d43824c2-feab-4b48-b162-049d129dbac3
    >swapon --priority 100 /dev/zram0
    >


Note that if the size of the zram device is too large, the build will spend
more time swapping to the SDCard as less of the build can be kept in memory.

Building Go will require more swap space than is available on the zram device
alone. For additional swap space a temporary 1Gb swap file will be used. To
create and mount a temporary swap file, as root, use:


    >dd if=/dev/zero of=/tmpswap bs=1M count=1024
    2097152+0 records in
    2097152+0 records out
    1073741824 bytes (1.1 GB, 1.0 GiB) copied, 558.15 s, 1.9 MB/s
    >mkswap /tmpswap
    Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
    no label, UUID=45122e1d-9bcd-4bf4-81cb-0a5b3aec51ec
    >swapon /tmpswap
    >


Check the zram and swap file are now being used:


    >swapon -s
    Filename      Type            Size            Used            Priority
    /dev/zram0    partition       32764           0               100
    /tmpswap      file            1048572         0               -2
    >


This allows frequent small swaps to memory with only the overflow going to the
SDCard. This should speed up the build and help preserve the SDCard.

A temporary directory will be needed for the build. As /tmp is normally mapped
to tmpfs and memory backed, /tmp is unsuitable. Instead a temporary directory
in the user’s home directory is used. As a normal user:


    >mkdir ~/tmp
    >


The Go source code should be downloaded and unpacked into a directory named as
per the release being built. In this case go1.20.4:


    >cd ~
    >mkdir go1.20.4
    >cd go1.20.4
    >tar --strip-components=1 -zxf ~/downloads/go1.20.4.src.tar.gz
    >


Note the first release of a Go version is named without a minor number, for
example, go1.20.src.tar.gz, in which case the directory used would be go1.20.0
See the later discussion on symlinking for the reasoning for this.

By default stacks are created with a size of 8Mb, however the limit can be
lowered saving memory during the build. A setting of 1Mb seems to work well.
To set, and then check, the initial stack limit, as a normal user:


    >ulimit -s 1024
    >ulimit -s
    1024
    >


To build Go, using the temporary ~/tmp directory, as a normal user:


    >cd ~/go1.20.4/src
    >TMPDIR=~/tmp ./all.bash
    :
    wait for 9½ hours…
    :
    >


Once the build has completed, revert the change to /boot/config.txt and set
gpu_mem back to its original value and reboot. The changes to swap will be
lost and ulimit settings returned to normal. As root the /tmpswap file can
also be removed.

For myself, ~/golang is a symlink to a major Go version, in this case Go 1.20,
and this is a symlink to the latest minor Go version, in this case Go 1.20.4:


    lrwxrwxrwx  1 me me    9 Nov 10 14:58 go1.17 -> go1.17.13
    drwxr-xr-x 10 me me 4096 Dec  1 16:39 go1.17.13
    lrwxrwxrwx  1 me me    6 Feb  7 14:14 /home/me/golang -> go1.20
    lrwxrwxrwx  1 me me    8 May  2 20:34 go1.20 -> go1.20.4
    drwxr-xr-x 10 me me 4096 May  2 20:11 go1.20.4


~/golang/bin is added to the PATH environment variable in ~/.bashrc and uses
the Go version ~/golang currently points to:


    export PATH=~/bin:$PATH:~/golang/bin


This allows switching between Go major/minor versions easily by changing the
symlinks. For example to switch switch from Go 1.20.4 to Go 1.17.13:


    >go version
    go version go1.20.4 linux/arm
    >rm golang
    >ln -s go1.17 golang
    >go version
    go version go1.17.13 linux/arm
    >

For each new Go release the build is put into a go1.x.x and the go1.x symlink
added or updated. The golang symlink is only updated for new go1.x releases,
or if I need a specific version of Go for testing.

These are the build times, including running all the tests, on different
Raspberry Pi models and a desktop machine:


            System/OS/Memory    Time      Configuration
            ------------------- --------  ------------------------
            RPi0 32-bit  512Mb: 09:23:28  tmpdir, ulimit, swapfile
            RPI3 32-bit    1Gb:    51:49  tmpdir, ulimit, swapfile
            RPi4 32-bit    4Gb:    24:03  tmpdir
            RPi4 64-bit    8Gb:    26:31  tmpdir
            Desktop 64-bit 8Gb:    11:29


The desktop used was an Intel i5-2400 quad core @3.1Ghz with 8Gb RAM.

--
Diddymus


  Up to Main Index                                       Up to Annexed Works