Building 32-bit Second Life on 64-bit Arch Linux w/ debootstrap

One of the first things I learned about working with the Second Life viewer’s codebase is that the build environment is very particular. Once I found out that Linden themselves use Debian to build their Linux binaries, I figured that was probably the best approach for me to take. Instead of firing up VirtualBox & deploying a new Debian VM, I discovered a wonderful tool call debootstrap which “which will install a Debian base system into a subdirectory of another, already installed system” & allows me to build a 32-bit viewer using 32-bit Debian on my 64-bit Arch Linux work machine.

I recently set up the same arrangement on my home machine & thought I would document it, both as a reference for myself & to others who may be in a similar situation.

Debootstrap

First install debootstrap from the AUR using your preferred AUR helper.

[cj@yuzuki ~]$ yaourt -S debootstrap

Then create a directory for the Debian system to be installed into.

[root@yuzuki ~]# mkdir /opt/debootstrap32

Then debootstrap like this.

[root@yuzuki ~]# debootstrap --arch i386 squeeze ./debootstrap32 http://ftp.uk.debian.org/debian/

Obviously you can substitute i386 for other architectures, squeeze for other Debian releases & the mirror to one geographically closer to you.

To use the Debian install we chroot into it, using linux32 to report the correct architecture.

[root@yuzuki ~]# linux32 chroot /opt/debootstrap32

I wanted the Debian chroot to be able to access files on the host system (namely the viewer code itself) so what I did next was create a user in the chroot with the same name as my user on the host machine. Note that the prompt has changed now that we’re chroot’ed into Debian.

root@yuzuki:/# adduser cj

And made a mountpoint in the Debian directory tree for the host machines RAID.

root@yuzuki:/# mkdir /array_x

Then the following bash script added to /etc/rc.d/ provides a way to conveniently mount/unmount /dev, /dev/pts, /dev/shm, /tmp, /home & the RAID from the host machine into the debian chroot.

Update – should you ever decide to delete the debootstrap, don’t forget if you have mounted parts of the host filesystem within it (as I very nearly discovered the hard way!).

#!/bin/bash

. /etc/rc.conf
. /etc/rc.d/functions

dirs=(/dev /dev/pts /dev/shm /tmp /home)
case $1 in
    start)
        stat_busy "Starting debootstrap32 chroot"
        for d in "${dirs[@]}"; do
         mount -o bind $d /opt/debootstrap32$d
        done
        mount -o bind /array_x /opt/debootstrap32/array_x
        mount -t proc none /opt/debootstrap32/proc
        mount -t sysfs none /opt/debootstrap32/sys
        add_daemon debootstrap32
        stat_done
        ;;
    stop)
        stat_busy "Stopping debootstrap32 chroot"
        for (( i = ${#dirs[@]} - 1; i >= 0; i-- )); do
         umount "/opt/debootstrap32${dirs[i]}"
        done
        umount /opt/debootstrap32/array_x
        umount /opt/debootstrap32/{proc,sys}
        rm_daemon debootstrap32
        stat_done
        ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo "usage: $0 {start|stop|restart}"
esac
exit 0

This can be added to the daemons array in /etc/rc.conf as usual & manually invoked in the usual manner.

[root@yuzuki rc.d]# /etc/rc.d/debootstrap32 foo
usage: /etc/rc.d/debootstrap32 {start|stop|restart}

Second Life dependencies & using ccache

Install the dependencies (including ccache to speed up subsequent compilations of the viewer) listed by the Second Life wiki for building the viewer on Linux.

apt-get install cmake bison flex python g++ make bzip2 ccache libc6-dev libstdc++6 libx11-dev libgl1-mesa-dev libxrender-dev libglu1-mesa-dev zlib1g-dev libssl-dev libogg-dev libpng12-dev libdbus-glib-1-dev libgtk2.0-dev

Update – you may also need to install libxml2 which isn’t listed by the wiki, if the build fails saying it can’t find lxml2.

The easiest way to use ccache is to allow it to ‘masquerade’ as the other compiler commands, so that when you call (for example) gcc it actually calls the ccache version. To do this, you just need to export the ccache bin directory to the beginning of your path. The path is different for Arch & Debian so be careful if you add the export to your .bashrc so that it works every time you login.

// Arch version
export PATH="/usr/lib/ccache/bin:$PATH"

// Debian version
export PATH="/usr/lib/ccache:$PATH"

Now when you do which on gcc or g++ it should return the ccache versions.

[cj@yuzuki ~]$ which g++
/usr/lib/ccache/bin/g++
[cj@yuzuki ~]$ which gcc
/usr/lib/ccache/bin/gcc

Note that the default location that ccache uses to store the actual cache is ~/.ccache which you might want to change (eg to a SSD to increase speed, or away from a SSD to reduce wear). IIRC the default maximum size for the cache is 1GB. You can check the status of the cache via the -s flag.

[cj@yuzuki ~]$ ccache -s
cache directory                     /home/cj/.ccache
cache hit (direct)                     0
cache hit (preprocessed)               0
cache miss                             0
files in cache                         0
cache size                             0 Kbytes
max cache size                       1.0 Gbytes

The viewer is built with autobuild so grab that as per the wiki’s instructions.

hg clone http://hg.secondlife.com/autobuild

And add it to your path, which I’ve done in my .bashrc.

export PATH="/array_x/builds/autobuild/bin:$PATH"

With any luck we can now use our Debian chroot to actually build the viewer using autobuild. I made myself a little bash script to automate the process & to cancel the build if it detects that it isn’t being run in the Debian chroot.

#!/bin/bash

if [ -e /CHROOTdebootstrap32 ] ; then
	echo "Running autobuild configure..."
	autobuild configure -c RelWithDebInfoOS --debug -- -DLL_TESTS:BOOL=OFF -DGCC_DISABLE_FATAL_WARNINGS:BOOL=TRUE
	echo "Done configuring."

	echo "Running autobuild build..."
	autobuild build -c RelWithDebInfoOS --debug
	echo "Done building, have a nice day."
else
	echo "Not running in 32-bit debootstrap, exiting."
fi

This is the chroot… right? Oshi-

I like to make it really obvious when I’m in a chroot so I don’t inadvertently perform some sort of destructive maintenance on what I think is a chroot, only to discover that it is actually the host system & I have to go scrambling for the backups. A simple solution I thought of is simply to create a file in the root of the chroot & check for its existence at / in .bashrc. This check also allows me to use the right path for the export for ccache.

if [ -e /CHROOTdebootstrap32 ] ; then
        PS1="[\u@\h \[\e[1;31m\]CHROOTdebootstrap32$\[\e[0m\] \W]$ "
        export PATH="/usr/lib/ccache/bin:$PATH"
fi

This goes in the .bashrc of my regular user on the host system (because that is mounted to the chroot’s /home) but in the .bashrc of the chroot’s /root user (as the host system’s /root isn’t mounted into the chroot). The result is that the text ‘CHROOTdebootstrap32’ is appended in bright red to the prompt when in the chroot.