May 2022 update
This whole project has now moved to GitHub, complete with automated scripts. You can find it here:
This page will remain for archival purposes, but is considered out of date for Raspberry Pi OS Bullseye (based on Debian 11 Bullseye), released February 2022.
Cross Compile MAME for Raspberry Pi
Last updated May 2021
Compiling MAME on Raspberry Pi is quite slow. Particularly for 64bit (aarch64) builds with GCC10, the RAM requirements extend past the most recent RPi4's 8GB when compiling on multiple processes using the -j4 flag (around 2-3GB per process, so 12-15GB required for make -j4
), and compiling with just -j1
is VERY slow.
Cross-compiling allows compilation on a faster x86_64 machine, which will build binaries for 32bit RPi 2/3/4 (armhf) or 64bit RPi3/4 (aarch64).
These steps are lengthy, complex, and will very likely break in mere months after I've written this guide. But here's what worked for me around May 2021.
My Intel Core i5 4690, Turbo Boost to 3.7GHz, 32GB RAM, compiles MAME 0.230 for 32bit armhf via GCC8 in 67 minutes.
Assumptions, software, caveats
- My x86_64 build machine is a 4 core i5 running Ubuntu Linux 20.04 LTS "focal".
- My Raspberry Pis vary. I tend to test a model 3B+ and a model 4B, and use either Raspbian 10 Buster or Raspbian 11 Bullseye, and jump between 32bit and 64bit.
- This guide currently targets Raspberry Pi 3 and 4, 32bit, running Raspbian Buster (based on Debian 10 Buster). This is the same software running on the current RetroPie build for RPi model 2 and up.
- For GCC8 32bit, aim for about 2GB RAM per make job, peak.
- For GCC10 64bit, aim for about 2.5GB RAM per make job, peak.
- You can potentially adapt this guide to numerous other platforms, but I'm not going to cover them here.
Host machine pre-requisites
- Even while cross-compiling, the build system appears to want to have all the pre-requisites for building MAME locally.
- On Ubuntu this is nice and easy to configure:
- Edit your
/etc/apt/sources.list
file - For each line that starts with
deb
, copy that line and make the word at the beginningdeb-src
- Save your new
sources.list
- Run
sudo apt-get update sudo apt-get build-dep -y mame
- Edit your
- This will install everything necessary to build the version of MAME bundled with Ubuntu 20.04LTS. While that's quite a bit older than what we're building in this guide, the compilers, headers and libraries are all still relevant.
- If you're absolutely stuck, here's a minimal example
/etc/apt/sources.list
for Ubuntu 20.04 LTS "Focal Fossa". I recommend using mirrors closer to your physical location (substitearchive.ubuntu.com
forus.archive.ubuntu.com
,au.archive.ubuntu.com
,uk.archive.ubuntu.com
,de.archive.ubuntu.com
, etc).# Binary packages deb http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu focal-security main restricted universe multiverse # Source packages deb-src http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu focal-security main restricted universe multiverse
crosstool-ng
-
crosstool-ng builds cross compilation environments. So we have to build the tool that builds the tools that builds MAME. Fun times ahead.
-
Grab a copy, build and install it
mkdir ~/src cd ~/src git clone https://github.com/crosstool-ng/crosstool-ng cd crosstool-ng ./bootstrap ./configure --prefix=${HOME}/ct-ng make -j4 make install
-
The tool is now installed. Time to build an environment. First, set up your path
export PATH=${HOME}/ct-ng/bin:$PATH
-
Choose an environment to build
mkdir ~/ctng_rpi_armhf cd ~/ctng_rpi_armhf ct-ng list-samples
-
You'll see a whole bunch of environments to choose from. When I wrote this guide, for a RPi with a 32bit OS, the best match was
armv8-rpi3-linux-gnueabihf
. You can see what that will install with the following:ct-ng show-armv8-rpi3-linux-gnueabihf
-
For RPi with a 64bit OS,
aarch64-rpi3-linux-gnu
is environment needed. This guide will continue on with the 32bit however, so just substitute as needed for your environment. -
Now let's copy that 32bit profile and prepare it.
ct-ng armv8-rpi3-linux-gnueabihf
-
This will create a file named
.config
(note the period in front, you'll need tols -la
to see this). You can see the versions of things it will install like so:$ ct-ng show-config [l...] armv8-rpi3-linux-gnueabihf Languages : C,C++ OS : linux-5.11.6 Binutils : binutils-2.36.1 Compiler : gcc-10.3.0 C library : glibc-2.33 Debug tools : gdb-9.2 Companion libs : expat-2.3.0 gettext-0.20.1 gmp-6.2.1 isl-0.22 libiconv-1.16 mpc-1.2.0 mpfr-4.1.0 ncurses-6.2 zlib-1.2.11 Companion tools :
-
If you want to change these versions, edit them in
.config
. For example, I changed theOS
tolinux-5.10.17
to match my RPi (rununame -r
on the RPi to find out your running kernel). Likewise I changedgcc
to8.3.0
,glibc
to2.28
,binutils
to2.31.1
. This is accurate to the date at the top of this page. You can find versions of what you need by looking at package lists online, for example -
Debian Buster
- gcc 8.3.0: https://packages.debian.org/buster/gcc
- libc6 2.28: https://packages.debian.org/buster/libc6
- binutils 2.31.1: https://packages.debian.org/buster/binutils
-
Debian Bullseye
- gcc 10.2.1: https://packages.debian.org/bullseye/gcc
- libc6 2.31: https://packages.debian.org/bullseye/libc6
- binutils 2.35.2: https://packages.debian.org/bullseye/binutils
-
Inside the
.config
file, change the following variables to the versions required. For example:CT_LINUX_VERSION="5.10.17" CT_GLIBC_MIN_KERNEL="5.10.17" CT_BINUTILS_VERSION="2.31.1" CT_GCC_VERSION="8.3.0" CT_GLIBC_VERSION="2.28"
-
Once modified, re-check:
$ ct-ng show-config [l...] armv8-rpi3-linux-gnueabihf Languages : C,C++ OS : linux-5.10.17 Binutils : binutils-2.31.1 Compiler : gcc-8.3.0 C library : glibc-2.28 Debug tools : gdb-9.2 Companion libs : expat-2.3.0 gettext-0.20.1 gmp-6.2.1 isl-0.22 libiconv-1.16 mpc-1.2.0 mpfr-4.1.0 ncurses-6.2 zlib-1.2.11 Companion tools :
-
Now build the lot.
ct-ng build
-
You'll see a considerable amount of output as ct-ng builds the entire cross-compile environment. With any luck, you'll end up with these tools installed to
~/x-tools/armv8-rpi3-linux-gnueabihf
, which we'll use to build MAME next. -
Download the config files (you'll need to rename them to
.config
):
ARM-Linux Header files and libraries
- The build process will need a number of header files and libraries, and the easiest way is to grab these directly from your Raspberry Pi itself. Bring up a shell on the Pi, and run
sudo apt-get clean sudo apt-get update mkdir -p ~/armhf/debs cd ~/armhf/debs apt-get download -y \ apulse libasound2 libasound2-dev libasyncns0 libbrotli1 libbsd0 libcap2 \ libcom-err2 libdbus-1-3 libdouble-conversion1 libdrm2 libexpat1 libffi6 \ libflac8 libfontconfig1 libfontconfig1-dev libfreetype6 libfreetype6-dev \ libgbm1 libgbm-dev libgcrypt20 libgl1 libgl-dev libgles1 libgles2 libgles2-mesa \ libglew2.1 libglib2.0-0 libglib2.0-dev libglu1-mesa libglvnd0 libglx0 \ libglx-dev libglx-mesa0 libgpg-error0 libgraphite2-3 libgssapi-krb5-2 \ libharfbuzz0b libice6 libicu63 libk5crypto3 libkeyutils1 libkrb5-3 \ libkrb5support0 liblz4-1 liblzma5 libmd0 libogg0 libopus0 libpcre2-16-0 \ libpcre3 libpng16-16 libpulse0 libqt5core5a libqt5gui5 libqt5widgets5 \ libraspberrypi0 libraspberrypi-bin libraspberrypi-dev libsdl1.2debian \ libsdl2-2.0-0 libsdl2-dev libsdl2-ttf-2.0-0 libsdl2-ttf-dev libsdl-image1.2 \ libsdl-mixer1.2 libsdl-ttf2.0-0 libsm6 libsndfile1 libsndio7.0 libsystemd0 \ libtirpc3 libuuid1 libvorbis0a libvorbisenc2 libwayland-client0 \ libwayland-cursor0 libwayland-egl1 libwayland-server0 libwrap0 libx11-6 \ libx11-data libx11-dev libx11-xcb1 libx264-155 libx265-165 libxau6 libxau-dev \ libxaw7 libxcb1 libxcb1-dev libxcb-composite0 libxcb-dri2-0 libxcb-dri3-0 \ libxcb-glx0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-present0 \ libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 \ libxcb-sync1 libxcb-util0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xinput0 \ libxcb-xkb1 libxcb-xv0 libxcomposite1 libxcursor1 libxcursor-dev libxdamage1 \ libxdmcp6 libxdmcp-dev libxext6 libxext-dev libxfconf-0-2 libxfixes3 \ libxfixes-dev libxfont2 libxft2 libxi6 libxi-dev libxinerama1 libxinerama-dev \ libxkbcommon0 libxkbcommon-dev libxkbcommon-x11-0 libxkbfile1 libxklavier16 \ libxml2 libxmu6 libxmuu1 libxpm4 libxrandr2 libxrandr-dev libxrender1 \ libxrender-dev libxres1 libxshmfence1 libxslt1.1 libxss1 libxss-dev libxt6 \ libxtables12 libxt-dev libxtst6 libxv1 libxv-dev libxvidcore4 libxxf86dga1 \ libxxf86vm1 libxxf86vm-dev libxxhash0 libzadc4 libzstd1 qtbase5-dev \ x11proto-dev zlib1g libpulse-dev
- This will download a number of
deb
files (Debian package files), most of which will havearmhf
oraarch64
in the name, depending on whether you're building 32bit or 64bit. - Download these scripts (
chmmod +x
to make executable and run on your Pi): - Copy these files to your x86_64 machine. If you have SSH set up,
rsync
is convenient. Run from the Pi (substituteusername
andbuildmachine
for your details):rsync -avrhP ~/armhf username@buildmachine:~/
- On the x86_64 build machine, extract the deb files:
cd ~/armhf ls -1d debs/*.deb | while read DEB ; do echo "${DEB}" ; dpkg -x "${DEB}" . ; done
- This will extract the contents of each deb file into the
~/armhf
directory, and create a folder structure we can use later during the compile.
MAME compile
-
Grab the MAME source code. I'm going to build
0.230
here (since writing this, I've compiled backwards from 0.230 to 0.209, and only 2 builds have failed out of the 21, where it appeared the "NOASM=1" flag was ignored in both cases). Whether this guide works on much older versions, or much newer versions, I have no idea.mkdir ~/src cd ~/src git clone https://github.com/mamedev/mame.git cd mame git checkout mame0230
-
Next, let's make a script to do the compile. Let's create a file named
~/mame_compile_armhf.sh
(keep it out of your~/src/mame
directory), and it'll look like this:
#!/bin/bash
# force qt5
export QT_SELECT=5
# set paths
export MXTARCH=armv8-rpi3-linux-gnueabihf
export MXTOOLS=${HOME}/x-tools/${MXTARCH}
export MARCHDIR=${HOME}/armhf
export RPIARCH=arm-linux-gnueabihf
cd ~/src/mame
#make clean
make \
CROSS_BUILD=1 \
NOWERROR=1 \
PLATFORM=arm \
CFLAGS+="-I ${MARCHDIR}/usr/include" \
CFLAGS+="-I ${MARCHDIR}/usr/include/${RPIARCH}" \
CFLAGS+="-L ${MARCHDIR}/usr/lib" \
CFLAGS+="-L ${MARCHDIR}/usr/lib/${RPIARCH}" \
CPPFLAGS+="-I ${MARCHDIR}/usr/include" \
CPPFLAGS+="-I ${MARCHDIR}/usr/include/${RPIARCH}" \
CPPFLAGS+="-L ${MARCHDIR}/usr/lib" \
CPPFLAGS+="-L ${MARCHDIR}/usr/lib/${RPIARCH}" \
LDFLAGS+="-L ${MARCHDIR}/usr/lib" \
LDFLAGS+="-L ${MARCHDIR}/usr/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/usr/lib" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/usr/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/lib" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/usr/lib/${RPIARCH}/pulseaudio" \
ARCHOPTS+="-Wl,-R,${MARCHDIR}/opt/vc/lib" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/usr/lib" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/usr/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/lib" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/usr/lib/${RPIARCH}/pulseaudio" \
ARCHOPTS+="-Wl,-rpath,${MARCHDIR}/opt/vc/lib" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/usr/lib" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/usr/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/lib" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/lib/${RPIARCH}" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/usr/lib/${RPIARCH}/pulseaudio" \
ARCHOPTS+="-Wl,-rpath-link,${MARCHDIR}/opt/vc/lib" \
TARGETOS=linux \
NOASM=1 \
OVERRIDE_CC="${MXTOOLS}/bin/${MXTARCH}-gcc" \
OVERRIDE_LD="${MXTOOLS}/bin/${MXTARCH}-ld" \
OVERRIDE_CXX="${MXTOOLS}/bin/${MXTARCH}-c++" \
-j5
- If you're building for 64bit,
PLATFORM=arm
becomesPLATFORM=arm64
, and obviously all of the paths will point to different tools. - Set the
-j
(number of make jobs) flag to a sensible number.- Compiling via GCC8 for 32bit armhf takes a peak of 2GB RAM per make job
- Compiling via GCC10 for 64bit aarch64 takes a peak of 2.5GB RAM per make job
- If you have enough RAM, a sensible number is how many cores you have plus one.
chmod a+x
the script.- Run the script with
~/mame_compile_armhf.sh
- Download the compile script:
- After some time, you should see a binary
mame
file appear in~/src/mame
- Verify the file is for the right architecture, for example 32bit RPi armhf:
$ file mame mame: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 5.10.17, with debug_info, not stripped
- Or 64bit RPi aarch64:
$ file mame mame: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 5.10.17, with debug_info, not stripped
- Copy it to your RPi and run it.
- Good luck!