Bisecting Mesa on Ubuntu isn’t as straight-forward as my previous post might suggest. Most modern workloads use 64-bit binaries, but Steam still requires 32-bit libraries and game managers like Uplay frequently carry similar requirements. Deploying different builds of Mesa for 64- and 32-bit often leads to workload instability.

Cross-compilng Mesa on Ubuntu is a bit fraught, however. Until recently, one needed to install the 32-bit valgrind package to build 32-bit Mesa, which forced the 64-bit valgrind package to be uninstalled. As of this writing, Mesa requires LLVM 15, and installing the 32bit version of LLVM 15 on Ubuntu 22.04 will remove key packages like ubuntu-desktop and xorg. If in ignorance one uses apt’s -y option, sadness ensues.

These considerations render Mesa’s standard cross-compilation method ill-suited to Ubuntu. There is, however, an alternative approach that utilizes schroot, which enables us to provision build environments inside chroots, much like a Python venv. The following script illustrates the technique.


set -e # terminate on errors
set -x # echo commands


CODENAME=$(lsb_release --codename --short)
# make sure source is available
if [ ! -d "$SRC_DIR" ]; then
    mkdir -p $SRC_DIR

set +e # allow errors temporarily
git -C $SRC_DIR rev-parse 2>/dev/null
exit_code=$(echo $?)
set -e
if [ "$exit_code" -ne 0 ] ; then
    echo "Cloning source..."
    # checkout source
    git clone $SRC_DIR
    echo "Source already cloned."

# configure execution-wide state
BUILD_ID=`git -C $SRC_DIR describe --always --tags`

build_mesa() {
        # $1: The schroot architecure
        # $2: The name of the schroot environment
        # $3: The schroot personality
        # ref:
        sudo apt -y install schroot debootstrap
        sudo mkdir -p $SCHROOT_PATH

        echo "Bootstrapping environment..."
        set +e # debootstrap will return non-zero if the environment has been previously provisioned
        sudo debootstrap --arch $1 $CODENAME $SCHROOT_PATH
        set -e

        echo "Configuring apt..."
        # create minimum viable apt sources
        # ref:
        sudo sh -c "cat > $SCHROOT_PATH/etc/apt/sources.list" << EOF
deb $CODENAME universe restricted main multiverse
deb ${CODENAME}-updates universe restricted main multiverse
deb ${CODENAME}-backports universe restricted main multiverse
deb ${CODENAME}-security universe restricted main multiverse
deb-src $CODENAME universe restricted main multiverse
deb-src ${CODENAME}-updates universe restricted main multiverse
deb-src ${CODENAME}-backports universe restricted main multiverse
deb-src ${CODENAME}-security universe restricted main multiverse

        echo "Configuring chroot..."
        sudo sh -c "cat > /etc/schroot/chroot.d/$2" << EOF
description=64b Mesa Build Env

        sudo schroot -c $2 apt update
        # "-- sh -c" required to pass arguments to chroot correctly
        # ref:
        sudo schroot -c $2 -- sh -c "apt -y --fix-broken install" # sometimes required for initial setup
        sudo schroot -c $2 -- sh -c "apt -y upgrade"
        sudo schroot -c $2 -- sh -c "apt -y build-dep mesa"
        sudo schroot -c $2 -- sh -c "apt -y install git llvm llvm-15"

        # Contemporary Mesa requires LLVM 15. Make sure it's available
        sudo schroot -c $2 -- sh -c "update-alternatives --install /usr/bin/llvm-config llvm-config /usr/lib/llvm-15/bin/llvm-config 200"

        # do the build
        cd $SRC_DIR
        mkdir -p $BUILD_DIR
        sudo schroot -c $2 -- sh -c "meson setup $BUILD_DIR $BUILD_OPTS --prefix=$INSTALL_DIR"
        sudo schroot -c $2 -- sh -c "ninja -C $BUILD_DIR"
        sudo schroot -c $2 -- sh -c "ninja -C $BUILD_DIR install"

        # deploy
        sudo cp -Tvr "${SCHROOT_PATH}${INSTALL_DIR}" "$INSTALL_DIR"

build_mesa "amd64" "${CODENAME}64" "linux"
build_mesa "i386" "${CODENAME}32" "linux32"

As before, environment overrides are required for system components to use the new build. Both 64-bit and 32-bit overrides are necessary.

# Force use of the local Mesa build for GL workloads

# Here I force the use of local the local build for Intel GPUs--revise as needed
# Newer versions of the Vulkan Loader should make this simpler;
# see

Also as before, the new build must be deployed to /usr/local (in a shell that sources the new environment):

sudo service gdm3 stop
sudo ln -sfn /usr/local-`git describe --always --tags` /usr/local
sudo ldconfig # update linker cache
sudo service gdm3 start

I expect this script to evolve over time, so I’ve committed it to GitHub.