Skip to content

Configuring a Toolchain

Poligraf edited this page Nov 13, 2020 · 15 revisions

Configuring a Toolchain for RetroFW Development

A step-by-step guide

Create a Virtual Machine

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.

alt_text

alt_text

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.

alt_text

alt_text

alt_text

Install Ubuntu

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.

alt_text

Select Install from the options once booted.

alt_text

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.

alt_text

alt_text

alt_text

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.

alt_text

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.

alt_text

Configure Ubuntu for Development

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

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

alt_text

We should also be able to run sdl-config to verify that our SDL installation is functioning:

sdl-config --libs

alt_text

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.

alt_text

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.

Netbeans

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.

alt_text

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.

Makefile suggestions

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.

Setting the right compiler / linker

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.

Detecting Include and Library paths

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.

Linking custom shared objects

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

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:

  1. 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 the CMakeLists.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.

  1. Type mkdir build. This will create a build directory, which will be used during compiling.

  2. Type cd build to go to that directory.

  3. 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.

  1. 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 as CMakeCache.txt. The CMakeCache.txt can be edited with cmake-gui or cmake to create new variations of the make file with different build flags, making it very versatile.

  2. 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.

Config file suggestions

We are assuming that you have installed the toolchain in /opt/rs97tools in the scenario below.

Configure has a couple of important flags mianly

  1. Specifying the host machine. As we are using linux above this would be --host=i686-pc-linux-gnu

  2. Specifying the target. In this case this would be mipsel-linux

  3. Specifying the --prefix. This will specify where the make 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.

  4. Specifying the CFLAGS and LDFLAGS location. Using the example above this will make it CFLAGS="-I/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/include/" LDFLAGS="-L/opt/rs97tools/mipsel-RetroFW-linux-uclibc/sysroot/usr/lib/"

  5. 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/"
  1. Type make and pray.
  2. 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.

If you finished reading this. Just remember that someone spend their Friday/Saturday nights writing this for your education, instead of going out for beers.

Clone this wiki locally