Configuring a Toolchain - retrofw/retrofw.github.io GitHub Wiki
A step-by-step guide
Download and install VirtualBox from https://www.virtualbox.org/
Create a new virtual machine of type “Ubuntu”. The machine doesn’t need much memory or CPU, so feel free to leave the defaults for those.
Add a new hard drive that’s dynamically allocated of 32GB or larger. Dynamic allocation ensures that the drive will only take what it’s actually using.
Download mini.iso from http://archive.ubuntu.com/ubuntu/dists/cosmic-updates/main/installer-amd64/current/images/netboot/
Set mini.iso as the CD-ROM drive for your virtual machine and then boot the machine.
Select Install from the options once booted.
Select your language and location. (e.g. “English” and “United States”) Keyboard detection can then setup your keyboard. (e.g. Mine detects as “us:intl”)
The hostname can be left as “ubuntu”. Then select the nearest country to pull the OS components from. The close the geographical location, the faster the download will go. Most people won’t need to enter proxy information, so skip that step if you don’t know what it is.
Create a user that you will log in with. For example, I created a user with the name “Dev User”, a username of “devuser”, and a password of “password.
At this point the system will be installed to the Virtual Machine’s hard drive. This will take a few minutes, so go grab a cup of coffee/tea.
The installer will then prompt you if you want automatic updates. I personally disable this, but it’s up to you what you choose.
You will then be offered the opportunity to install a desktop and other software features. We’re not going to need a GUI on the VM, so leave all the options blank and continue.
Install the GRUB bootloader to the master boot record. (i.e. select “Yes”) At this point the installer is complete and ready to reboot the system. Eject the mini.iso image from the Optical Drive and continue.
Guest additions
sudo apt-get install virtualbox-guest-additions-iso
sudo mount /usr/share/virtualbox/VBoxGuestAdditions.iso /media
cd /media
sudo sh ./VBoxLinuxAdditions.run
Shared folders
Create a local development directory. For example, c:\Development
on Windows or /Users/<username>/Development
on Mac.
sudo mkdir /opt/rs97apps
sudo mount -t vboxsf Development /opt/rs97apps
Install compiler dependencies
sudo apt-get install build-essential libncurses5 libncurses5-dev git python unzip bc squashfs-tools
BuildRoot is an easy to use solution designed to create Linux distributions and cross-compiler toolchains for embedded development. The RetroFW project uses buildroot as a common solution so that all developers can quickly and easily begin software development.
This step is a lot easier as root, so let’s start by using sudo.
sudo su -l
We will now enter the /opt directory to pull down the buildroot tools.
cd /opt
The following commands will download version 2018.02.9 of buildroot from https://buildroot.org/downloads/buildroot-2018.02.9.tar.gz and extract it into /opt
.
wget https://buildroot.org/downloads/buildroot-2018.02.9.tar.gz
tar -xzvf buildroot-2018.02.9.tar.gz
Tip: If you visit the buildroot website you may notice that there is a newer version available. Do not attempt to upgrade! Toolchains are very sensitive to the version used and should only be upgraded at the direction of the kernel developers.
We are now ready to configure buildroot for RS-97 development. Thankfully, a very nice interface is provided for this purpose.
cd buildroot-2018.02.9
make menuconfig
Set the following options in the buildroot interface.
Target Options
- Target Architecture - MIPS (little endian)
- Disable soft-float
Toolchain
- C library (uClibc-ng)
- Enable WCHAR support
- Enable C++ support
Target Packages
- Graphic libraries and applications (graphic/text)
- SDL
- SDL_gfx
- SDL_image
- SDL_mixer
- SDL_net
- SDL_sound
- SDL_TTF
Tip: You can always come back and adjust the buildtools configuration for new software. Only software affected by the change will be rebuilt.
Exit the menu system and save the configuration when prompted. We are now ready to run make
on the project.
export FORCE_UNSAFE_CONFIGURE=1
make
Tip: This step can take a very long time. Make sure your computer is plugged into the wall and not running on battery. Many developers leave this process to run overnight.
The following commands will install our new toolchain into a common location.
mkdir /opt/rs97tools
cp -R /opt/buildroot-2018.02.9/output/host/* /opt/rs97tools/
We now have a toolchain that we can use to develop RS-97 software. The final step is to add the tools to our path.
pico /etc/profile.d/rs97tools.sh
(Feel free to use vi
instead of pico
if you are more familiar with that editor.)
Type the following text into the editor and the hit CTRL+X to quit and save:
export PATH=/opt/rs97tools/mipsel-buildroot-linux-uclibc/sysroot/usr/bin:$PATH
export PATH=/opt/rs97tools/bin:$PATH
If you log out and log back in you should now be able to run the compiler to verify that the tools are successfully installed.
mipsel-linux-gcc
We should also be able to run sdl-config to verify that our SDL installation is functioning:
sdl-config --libs
Finally, we can test building an RS-97 project by pulling Dingux Commander and building it:
cd /opt/rs97apps/
git clone https://github.com/jbanes/rs97-commander
cd rs97-commander
make
If everything goes well, you should now have a commander.ipk
file in the bin
subdirectory that you can install and test on your RS-97.
Tip: Remember that the rs97-commander
directory is accessible on your Windows or Mac machine. This is because we used a shared folder that locates everything under /opt/rs97apps
in your host machine’s development directory.
When I sat down to begin developing code for the RS-97, I did some research on what the best solution was for a C/C++ IDE that can be used with Linux code. Surprisingly, the resounding answer was Netbeans, a Java IDE that I already used on a daily basis!
As it turns out, when Sun acquired Forte back in the day, Forte was working on an ideal IDE environment that could be repurposed for numerous languages and toolsets. This environment was initially used for their Forte4J product (what eventually became Netbeans) and was ultimately forked after the Sun acquisition to create the Sun Development Studio for C/C++.
Given the common codebase, Sun merged Development Studio and Netbeans back into a single interface that could do everything. Thus we’re left with a top-notch tool that can support our C/C++ development just as well as Java development.
To complete this installation we’ll need two pieces. A Java Development Kit / Runtime capable of running Netbeans and the Netbeans IDE itself.
Obtain a Java Development Kit (JDK) from https://www.oracle.com/technetwork/java/javase/downloads/index.html or https://adoptopenjdk.net/ in order to run Netbeans.
Visit https://netbeans.org/downloads/8.2/ and download either the C/C++ or All package.
Install the Java SDK and then install Netbeans. Once you launch Netbeans we can use our previous rs97-commander
project as an example for how to setup a project for editing.
Click on “File” and click “New Project”. Select the category C/C++ and the project type of C/C++ project with existing sources then click Next >. Select the rs97-commander
directory and click Finish. You should now be able to edit the code for the project.
Please note that while the IDE can help with code completion and error checking, it is not possible to build RS-97 software from the IDE in this configuration. You still need to switch to the Virtual Machine window and type make
to build the software.
TODO: This section can be improved by adding instructions for integrating builds into Netbeans via SSH.
So you just pulled down the code for an awesome RS-97 project that you want to help maintain. You excitedly type make
and wait for binaries to pop out the other end of your awesome toolchain. Commands start appearing on the screen and…
...the build breaks. Damn.
So you do what any good programmer would do. You open the Makefile
to see what the problem might be. Almost right away you see hard-coded toolchain paths, incorrect compiler references, random attempts to link libraries, etc. Looks like the last guy got as far as compiling the code on his machine, but not so far as to make it portable.
You have your work cut out for you. Luckily, I’m here to help you sort it all out.
The first thing to understand about a cross-compiling toolchain is that your tools will carry a prefix to prevent confusion with the host platform. So rather than using cc
, you’re going to want to use mipsel-linux-cc
instead. In fact, you’ll want to do this with all the tools. For example:
g++ -> mipsel-linux-g++
ld -> mipsel-linux-ld
as -> mipsel-linux-as
strip -> mipsel-linux-strip
ranlib -> mipsel-linux-ranlib
To support this you could modify all the references to these tools to the cross-compiler version. However, this would not be very portable or future-proof. Instead, you should change all the locations where you have a direct reference like this:
cc -o file.o file.c
...to use a variable like this:
$(CC) -o file.o file.c
You can then define that variable at the top of your Makefile
like this:
CC = mipsel-linux-cc
That works, but it still gets a bit unwieldy when we define multiple tools. For example:
AS = mipsel-linux-as
CC = mipsel-linux-cc
CXX = mipsel-linux-g++
LD = mipsel-linux-ld
This can be further improved by separating the architecture prefix into a single variable:
ARCH = mipsel-linux-
AS = $(ARCH)as
CC = $(ARCH)cc
CXX = $(ARCH)g++
LD = $(ARCH)ld
Now if the architecture changes only a single reference needs to be updated.
When it comes to where a build toolchain is located on a system, there is unfortunately no standard directory structure that can be used. This can make it difficult to create a portable Makefile
that knows where to look for includes and libraries.
One solution is to simply hardcode the paths into the Makefile
. While quick and generally easy to accomplish, this solution renders the Makefile
forever tied to the machine of the person who made it. Collaborative efforts may even be stunted or outright stopped as programmers fight over the paths used in the Makefile.
Another option is to use environment variables to expose the desired path. This is more portable. However, it leaves programmers new to the project at a disadvantage. If they don’t have the correct variables set they’ll be unable to compile your project and have very little clue as to why. More often than not this solution degenerates back to hardcoded paths.
A simpler method is to add the following two lines to your Makefile
:
SDL_CFLAGS := $(shell sdl-config --cflags)
SDL_LIBS := $(shell sdl-config --libs)
These lines will ask the sdl-config tool information about where the include and library directories are on the system. And since nearly everything is co-located in the buildroot installation, this will provide the necessary options to find all of the system libraries.
To use these variables, just add these options to your CFLAGS
or LDFLAGS
variables. If your project lacks variables for compiler and linker options, you can add them directly to the command like this:
$(CC) $(SDL_CFLAGS) $(SDL_LIBS) -o file.o file.c
If you already have options like -L
, -I
, and -lSDL
in your project, you can safely remove them at this point.
One of the challenges of embedded systems is that libraries often can’t be installed at “common” locations like /usr/lib
. While the directory may exist, the root filesystem tends to be read-only. The typical solution is to place the .so
file alongside the executable binary. While this may work in some systems, you may find it to be problematic in RetroFW.
The good news is that there is a simple solution. Linux generally records fixed locations at which to look for libraries. We can set our install location at compile / linking time so that RetroFW can find it.
-Wl,-rpath,"/home/retrofw/apps/myapp/"
Just change the path to match your install directory and include this option in your CFLAGS
.
Tip: Creating an IPK package to distribute your application will ensure that the user installs the library to the correct location. See the IPK support section for more details.
Cmake looks confusing at first glance, but it is truly one of the easiest tools to start cross compiling after the initial headaches and learning curve.
Cmake has a file called CMakeLists.txt
which contains all the important dependencies for that application and handles all the complexity of cross compiling for you. There is also another very important file located at /opt/rs97tools/usr/share/buildroot/toolchainfile.cmake
which tells your computer that you are planning on cross compiling this application. This handles all the hard stuff for you.
To compile with CMAKE we will need to:
- Download the source of the application you want to compile and go to the location where
CMakeLists.txt
is located. For example lets say the source of the application and theCMakeLists.txt
file is located in/home/homePC/game
.
Please note: Location and cmake build instructions can vary among different applications/games during compiling, but building the source in a separate folder is always considered good practice.
-
Type
mkdir build
. This will create abuild
directory, which will be used during compiling. -
Type
cd build
to go to that directory. -
In the
build
directory type
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=/opt/rs97tools/usr/share/buildroot/toolchainfile.cmake -DCMAKE_C_FLAGS_RELEASE="-O3 -DDINGOO -DNDEBUG -I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" -DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG -DDINGOO -I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" -DBUILD_TARGET=DINGOO -DTREMOR_INCLUDE_DIR="-I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" -DCMAKE_INSTALL_PREFIX=/mnt
If you have a graphical interface installed you can even open up a cmake cross compiling GUI. This has all the flags available for you to tick and edit in a GUI for that application/game. If you do not have a graphical interface installed, you will have to refer to each of the games/applications installation guides to find all the available options, as all of them are slightly different.
The command to open up the gui would be
cmake-gui .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=/opt/rs97tools/usr/share/buildroot/toolchainfile.cmake -DCMAKE_C_FLAGS_RELEASE="-O3 -DDINGOO -DNDEBUG -I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" -DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG -DDINGOO -I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" -DBUILD_TARGET=DINGOO -DTREMOR_INCLUDE_DIR="-I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/ -DCMAKE_INSTALL_PREFIX=/mnt"
The command above will specify that you want to create a Release
and will try not include any debugging information with the -DNDEBUG
flag. It will also specify that you are building for the DINGOO platform (Opendingux and retrofw) and will try to install the game in /mnt
.
Please note: I have included an optional TREMOR flag, because I have had one occasion where a bug in the CMakeLists.txt
caused some of the audio to be Vorbis
and some with tremor
causing some really weird problems. This is unlikely to be needed.
-
Now that you have used the GUI or edited the flags and are happy, observe that a
make
file was made in the/home/homePC/game/build
folder as well asCMakeCache.txt
. TheCMakeCache.txt
can be edited withcmake-gui
orcmake
to create new variations of themake
file with different build flags, making it very versatile. -
Type
make
and pray.
7)If the make succeeds type make install
. This will install the application in /mnt
as that is the folder we specified with with -DCMAKE_INSTALL_PREFIX=/mnt
Please note that some games do not use the -DCMAKE_INSTALL_PREFIX=/mnt
flag, so they might not install there.
We are assuming that you have installed the toolchain in /opt/rs97tools
in the scenario below.
Configure has a couple of important flags mianly
-
Specifying the host machine. As we are using linux above this would be
--host=i686-pc-linux-gnu
-
Specifying the target. In this case this would be
mipsel-linux
-
Specifying the
--prefix
. This will specify where themake install
will write the required files. For cross compiling games, this is usually a good idea, as otherwise the files will be installed to/usr/local
or somewhere where you will find it difficult to find. -
Specifying the
CFLAGS
andLDFLAGS
location. Using the example above this will make itCFLAGS="-I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" LDFLAGS="-L/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/lib/"
-
Specifying the right compilers/linkers.
If we use the case above, our toolchain is located in /opt/rs97tools
- So location will be as follows
AS = /opt/rs97tools/bin/mipsel-linux-as
CC = /opt/rs97tools/bin/mipsel-linux-cc
CXX = /opt/rs97tools/bin/mipsel-linux-g++
LD = /opt/rs97tools/bin/mipsel-linux-ld
This makes our configure script quite long, but hopefully verbose and will cover most cases. Please note optional flags such as --disable-shared --enable-static
are also available, but they depend on the library/application being compiled.
Go to the aplication where the configure
file is located and type the following command to prepare files for installation in location /mnt
AS=/opt/rs97tools/bin/mipsel-linux-as CC=/opt/rs97tools/bin/mipsel-linux-cc CXX=/opt/rs97tools/bin/mipsel-linux-g++ LD=/opt/rs97tools/bin/mipsel-linux-ld ./configure --prefix=/mnt --host=i686-pc-linux-gnu --target=mipsel-linux CFLAGS="-I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" LDFLAGS="-L/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/lib/"
- Type
make
and pray. - If the make succeeds type
make install
. This will install the application in/mnt
as that is the--prefix
we used in the command above.
Please note that some games do not use the --prefix
flag, so they might not install there.