Build Environment

Over the years, I’ve assembled a build environment for RISC OS software using the Linux-based GCCSDK and a range of other, more bespoke, tools. The aim of this guide is to explain the process needed to get a working setup, based on an Ubuntu 20.04 system. The steps are likely to be similar on other systems, too – although I can’t advise on the details.

Setting up the environment

To be able to build software for RISC OS on Linux, we need a working GCCSDK cross-compiler setup. Achieving this is fairly straightforward on a modern Linux system, at least if it’s Debian-based.

Conventionally, the GCCSDK tools live in a folder called gccsdk in your home directory: that is, ~/gccsdk. A collection of system variables are used to locate the cross-compiler, and these must be set up in advance of using the system. How you choose to do this is up to you, and will depend on your system setup; adding the following commands to the end of your ~/.bashrc file is one solution.

The GCCSDK requires two variables to be configured: GCCSDK_INSTALL_CROSSBIN points to the binary files used by the cross-compiler and its resources, while GCCSDK_INSTALL_ENV points to the cross-compilation environment where libraries and the like are installed.

In addition to these, if you’re planning to build the DeskLib library, which uses the <DeskLib$Path> system variable extensively, you’ll need to set up the DESKLIB_PATH to point to the location in which it installs itself on GCCSDK_INSTALL_ENV. The purpose of this variable is in fact exactly the same as its RISC OS counterpart. If you’re planning to build WinEd, then it is also useful to set up a similar FLEXLIB_PATH variable at the same time.

Finally, if you’re intending to use my source code (which is where this tutorial ends up), then you will need another folder and three more variables. You can create a folder in your home directory alongside the gccsdk folder (ie. ~/sftools), or within the gccsdk folder itself (ie. ~/gccsdk/sftools). Whichever you decide to do, the SFTOOLS_BIN, SFTOOLS_BASIC and SFTOOLS_MAKE variables should point to three sub-folders within it.

For example, with ~/gccsdk and ~/sfbin folders in the home directory, for a user called “steve”, the following commands could be used in ~/.bashrc or similar:

# Add some variables to allow GCCSDK to function

export GCCSDK_INSTALL_CROSSBIN=/home/steve/gccsdk/cross/bin
export GCCSDK_INSTALL_ENV=/home/steve/gccsdk/env

# Allow SF Tools to function

export SFTOOLS_BIN=/home/steve/sftools/bin
export SFTOOLS_BASIC=/home/steve/sftools/basic
export SFTOOLS_MAKE=/home/steve/sftools/make

# Needed for building DeskLib and WinEd

export DESKLIB_PATH=$GCCSDK_INSTALL_ENV/include/DeskLib
export FLEXLIB_PATH=$GCCSDK_INSTALL_ENV/include

We can now create the folders that we need. Following the example above, we could simply create the gccsdk and sftools folders in the home directory:

:~$ mkdir gccsdk
:~$ mkdir sftools
:~$

The sftools directory is only required if you plan to set up the build environment for my software.

Preparing GCC4

The next step is to download and build the GCCSDK tools. We will need Subversion to check out the files, so make sure that’s present; if not, it can be installed with apt:

:~$ sudo apt install subversion

<--- time passes --->

:~$

Descend into the gccsdk folder, and use Subversion to check out (download) the Autobuilder and GCC4 files from the Subversion repositories at www.riscos.info, into two new sub-folders called autobuilder and gcc4:

:~$ cd gccsdk
:~/gccsdk$ svn co svn://svn.riscos.info/gccsdk/trunk/autobuilder/ autobuilder

<--- time passes --->

Checked out revision 7427.
:~/gccsdk$ svn co svn://svn.riscos.info/gccsdk/trunk/gcc4/ gcc4

<--- time passes --->

Checked out revision 7427.
:~/gccsdk$

The reported revision numbers will vary as new changes are checked in to the Subversion repository by the GCCSDK developers. Descend into the gcc4 folder and run the buildworld script:

:~/gccsdk$ cd gcc4
:~/gccsdk/gcc4$ ./buildworld
Please install the following tools for GCCSDK: m4 bison flex autogen gperf makeinfo
:~/gccsdk/gcc4$

If the script responds with a request to install additional tools, then it will be necessary to install these packages before trying again (note that in Debian, makeinfo is found in the texinfo package):

:~/gccsdk/gcc4$ sudo apt install m4 bison flex autogen gperf texinfo

<--- time passes --->

:~/gccsdk/gcc4$ ./buildworld
It looks like this is the first time you're running the GCCSDK build.
Some default locations have been set for you in the file 'gccsdk-params'.
These locations need to be writable. Check that they're ok, or change them as you need, then
Run this command again. Here is the contents of the 'gccsdk-params' file that was created for you:

# -- Following section is user configurable:
# This is where the cross compiler will be installed. It needs to end in 'bin'.
export GCCSDK_INSTALL_CROSSBIN=/home/steve/gccsdk/cross/bin
# This is where the porting tools and any libraries you build will be installed.
export GCCSDK_INSTALL_ENV=/home/steve/gccsdk/env
:~/gccsdk/gcc4$

The installer has set the GCCSDK_INSTALL_CROSSBIN and GCCSDK_INSTALL_ENV variables to match the location that we’re running the script from. If these don’t match the values that we set above, it would suggest that we’re not installing things where we intended. Since they do, we can run the build script for what we hope will be the last time. It should go off and build the compiler... which will take some time, so leave it to do its stuff!

:~/gccsdk/gcc4$ ./buildworld

<--- much time passes --->

Built and installed GCCSDK cross compiler ok. Check out the README for examples of how to use the cross compiler or Autobuilder.
:~/gccsdk/gcc4$

If the script eventually reports success (depending on the speed of your machine, it could take several hours), then this should be the GCC cross-compiler built and ready to go!

Preparing the Autobuilder

The next thing to configure is the Autobuilder, which uses the GCC4 cross-compiler to automatically fetch the source for, build and install useful software. We can return to ~/gccsdk, and create ourselves a build folder in which the Autobuilder can do its stuff.

:~/gccsdk/gcc4$ cd ..
:~/gccsdk$ mkdir build
:~/gccsdk$ cd build
:~/gccsdk/build$

The Autobuilder requires a collection of addtional dependencies. On Ubuntu 20.04, installing the following appears to be sufficient to allow it to function:

:~/gccsdk/build$ sudo apt install libtool patch wget help2man gcc g++ subversion sed make build-essential autoconf2.13 automake automake1.11 cvs doxygen dpkg-dev gettext intltool libglib2.0-dev libpopt-dev pkg-config meson

<--- time passes --->

:~/gccsdk/build$

The automake1.9, liborbit2-dev and realpath packages are also mentioned in the GCCSDK documentation, but don’t appear to be required in practice. This is likely to depend on what Autobuilder packages you try to build, so your mileage may vary!

We can now use the Autobuilder to download and build some of the libraries that we will require.

Building OSLib and DeskLib

If you’re planning to compile any of my C-based software, you’ll need OSLib; if you’re planning to use the environment to build WinEd, then you'll need DeskLib as well. These can both be built – at least in part – using the Autobuilder.

Remaining in the build folder that we created in the last step, we can request that the Autobuilder download and build OSLib:

:~/gccsdk/build$ ../autobuilder/build -v oslib

<--- time passes --->

Package oslib: Success

:~/gccsdk/build$

Autobuilder packages contain details of how to find and download, usually from version control systems, the latest source code for a given item. If it completes successfully, we should now have the current version of OSLib built and installed for us in the appropriate place on GCCSDK_INSTALL_ENV. If we run the run the same command again in six months’ time, we would get a new build containing any updates.

Now we can build Desklib in a similar way. This time we get a choice of versions, so we will choose the one which is suitable for linking with the Shared C Library:

:~/gccsdk/build$ ../autobuilder/build -v desklib-scl

<--- time passes --->

Package desklib-scl: Success

:~/gccsdk/build$

Note that this time, we were able to specify that we wished DeskLib to be compatible with building software to link with the Shared C Library. That wasn’t an option with OSLib – which is a problem, because we’ll need them both to be. By default, both OSLib and DeskLib, along with most other libraries in the Autobuilder, produce versions suitable for using with UnixLib.

To get around this, we will now download the OSLib source ourselves and build a version suitable for our purposes. The step above with the Autobuilder wasn’t wasted, as it has installed all of the OSLib header files in the correct places without us having to think about it, and there’s a UnixLib version of the compiled code ready for any work which might require it, too.

The first thing to do is to check out the OSLib sources in whatever location you choose to do your development. On my system, that's in a folder called ~/Development, but from now on you can check things out wherever you find most convenient. Once again, we use Subversion for the job:

:~/gccsdk/build$ cd ~/Development
:~/Development$ svn co https://svn.code.sf.net/p/ro-oslib/code/trunk/\!OSLib OSLib

<--- time passes --->

Checked out revision 458.
:~/Development$

We can now drop into the newly created working copy of OSLib, and build it using the appropriate options for “hard float”.

:~/Development$ cd OSLib
:~/Development/OSLib$ make ELFOBJECTTYPE=HARDFPU

<--- time passes --->

make[1]: Leaving directory '/home/steve/Development/OSLib/Source'
:~/Development/OSLib$

If this completes successfully, we can copy the resulting library file into the GCCSDK library folder; as noted earlier, the headers will have been installed correctly for us by the Autobuilder.

:~/Development/OSLib$ cp Build/libOSLib32.a $GCCSDK_INSTALL_ENV/lib/libOSLibH32.a
:~/Development/OSLib$

Note the additional ‘H’ in the destination filename, to save overwriting the “soft float” OSLib created by the AutoBuilder. All of my source code expects this naming convention.

There’s one final thing to do with OSLib, before we move on. As part of its own build process, OSLib creates a tool called BindHelp which can be used to assemble StrongHelp manuals. This is used by a lot of my software to create its documentation, so we’ll take a copy into the location that we have pointed SFTOOLS_BIN towards.

:~/Development/OSLib$ mkdir -p $SFTOOLS_BIN
:~/Development/OSLib$ cp Bin/bindhelp $SFTOOLS_BIN
:~/Development/OSLib$

And now, with OSLib and DeskLib installed, we can move on to the bespoke parts of the build system.

Shared tools

Having got the more standard components into place, we can move on to the specifics requirements for my software. Most of my projects use Shared Makefiles to build, which allow a lot of the more complex, common requirements to be written once and re-used many times. They live in the folder pointed to by the SFTOOLS_MAKE variable.

We will start by checking them out into the same development folder that we used for OSLib, so we can return up a level in the directory tree. From now on the location of these files does not matter, you can arrange your disc as you wish. So long as the environment variables point to the ~/gccsdk and ~/sftools folders (or the alternative locations that you chose) when you attempt to build software, everything should work.

My source code is stored using Git instead of Subversion, so a copy of this will need to be installed with apt if it isn’t already present:

:~/Development$ sudo apt install git

<--- time passes --->

:~/Development$

We can then use it to obtain a copy of the files:

:~/Development$ git clone https://github.com/steve-fryatt/makefiles.git makefiles
Cloning into 'makefiles'...
remote: Counting objects: 193, done.
remote: Compressing objects: 100% (112/112), done.
remote: Total 193 (delta 101), reused 134 (delta 73)
Receiving objects: 100% (193/193), 36.76 KiB | 9.19 MiB/s, done.
Resolving deltas: 100% (101/101), done.
:~/Development$

Whilst the exact numbers of objects, sizes and deltas will vary, the command should complete without an error. We can now drop in to the new project folder, and install the files into the correct places within ~/sftools (or your choice of folder):

:~/Development$ cd makefiles
:~/Development/makefiles$ make install
** Building with version c9a497b (c9a497b) on date 16 Sep 2020 ***
* CLEANING:
rm -rf build/ReadMe,fff
rm -rf build/Licence,fff
* TEXT MANUAL: build/ReadMe,fff
ManTools not available: skipping manual build.
* LICENCE: build/Licence,fff
* INSTALLING: /home/steve/sftools/make
:~/Development/makefiles$

If all goes to plan, this will install the Shared Makefiles in the $SFTOOLS_MAKE folder – which is /home/steve/sftools/make above. There’s a catch, though: it has reported “ManTools not available: skipping manual build”. This isn’t too surprisng, as ManTools is another part of my build tools, which we haven’t installed yet. To solve this, we will now need to install ManTools.

The process is very similar: return to the ~/Development folder, clone the ManTools repository, and install it:

:~/Development/makefiles$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/mantools.git mantools
Cloning into 'mantools'...
remote: Counting objects: 591, done.
remote: Compressing objects: 100% (306/306), done.
remote: Total 591 (delta 289), reused 545 (delta 266)
Receiving objects: 100% (591/591), 136.16 KiB | 6.19 MiB/s, done.
Resolving deltas: 100% (289/289), done.
:~/Development$ cd mantools
:~/Development/mantools$ make install
** Building with version aa2f3cb (aa2f3cb) on date 16 Sep 2020 ***
* CLEANING:
rm -rf build/ReadMe,fff
rm -rf build/Licence,fff
* TEXT MANUAL: build/ReadMe,fff
ManTools not available: skipping manual build.
* LICENCE: build/Licence,fff
* INSTALLING: /home/steve/sftools/bin
:~/Development/mantools$

This time the install copies the ManTools scripts into the $SFTOOLS_BIN folder, which is /home/steve/sftools/bin. Again it reports “ManTools not available: skipping manual build”, but we have solved that now! Run the installation again, and the full process should complete:

:~/Development/mantools$ make install
** Building with version aa2f3cb (aa2f3cb) on date 16 Sep 2020 ***
* CLEANING:
rm -rf build/ReadMe,fff
rm -rf build/Licence,fff
* TEXT MANUAL: build/ReadMe,fff
* LICENCE: build/Licence,fff
* INSTALLING: /home/steve/sftools/bin
:~/Development/mantools$

With ManTools installed, we can now return to the Makefiles project and complete its installation in the same way:

:~/Development/mantools$ cd ../makefiles
:~/Development/makefiles$ make install
** Building with version c9a497b (c9a497b) on date 16 Sep 2020 ***
* CLEANING:
rm -rf build/ReadMe,fff
rm -rf build/Licence,fff
* TEXT MANUAL: build/ReadMe,fff
* LICENCE: build/Licence,fff
* INSTALLING: /home/steve/sftools/make
:~/Development/makefiles$

The last of this group of simple scripts is PackTools, which performs some useful actions relating to creating RiscPkg packages. We can again return to the ~/Development folder, clone the repository and install its contents:

:~/Development/makefiles$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/packtools.git packtools
Cloning into 'packtools'...
remote: Counting objects: 172, done.
remote: Compressing objects: 100% (83/83), done.
remote: Total 172 (delta 51), reused 147 (delta 41)
Receiving objects: 100% (172/172), 41.42 KiB | 4.60 MiB/s, done.
Resolving deltas: 100% (51/51), done.
:~/Development$ cd packtools
:~/Development/packtools$ make install
** Building with version e57a6ab (e57a6ab) on date 16 Sep 2020 ***
* CLEANING:
rm -rf build/ReadMe,fff
rm -rf build/Licence,fff
* TEXT MANUAL: build/ReadMe,fff
* LICENCE: build/Licence,fff
* INSTALLING: /home/steve/sftools/bin
:~/Development/packtools$

Tokenizing BASIC

When building software to run on RISC OS, it’s useful to be able to include BBC BASIC programs. As BASIC doesn’t work well with source control due to its tokenised format, my projects store it as plain text and tokenise it on demand. In fact Acorn did this for the BASIC which is used within the RISC OS sources (such as for applications like Alarm), but they had the advantage that RISC OS is built on RISC OS, and so can use the built-in BASIC tokeniser.

That’s a less successful approach when working in a cross-compilation environment, so my build tools include Tokenize: a cross-platform tokeniser for ARM BASIC which is written in C. The process for installing Tokenize should come as no surprise by now: we will clone the repository into our ~/Development folder, then build and install it into the $SFTOOLS_BIN folder:

:~/Development/packtools$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/tokenize.git tokenize
Cloning into 'tokenize'...
remote: Counting objects: 974, done.
remote: Compressing objects: 100% (303/303), done.
remote: Total 974 (delta 664), reused 936 (delta 640)
Receiving objects: 100% (974/974), 201.43 KiB | 11.85 MiB/s, done.
Resolving deltas: 100% (664/664), done.
:~/Development$ cd tokenize
:~/Development/tokenize$ make install
** Building for with version 0c8a39c (0c8a39c) on date 16 Sep 2020 ***

<--- time passes --->

cp -r buildlinux/tokenize /home/steve/sftools/bin
:~/Development/tokenize$

There’s more verbiage in the output this time, since the Tokenize Makefile isn’t as refined as those for the previous projects, but the end result should be a report that the file has been copied into the /home/steve/sftools/bin folder.

Tokenize is a useful tool in its own right, and you can find both the Linux executable and a manual inside the tokenize/buildlinux folder. It can also be used on RISC OS if required: although tangential to this guide, the following commands will leave a RISC OS executable and ReadMe in the tokenize/buildro folder:

:~/Development/tokenize$ make TARGET=riscos
Building for riscos with version 0c8a39c (0c8a39c) on date 16 Sep 2020

<--- time passes --->

:~/Development/tokenize$

If the call to Make is changed further, to make release TARGET=riscos, then a Zip file containing the executable, ReadMe and Licence will be created in the parent ~/Development folder.

Building menus

Before we can start to build some actual code, there is one more tool that is required by many of my projects. MenuGen is a command-line utility to convert a text file of menu structures into a block of data that the RISC OS Wimp might almost understand. Yet again, installation is a case of cloning the repository into our ~/Development folder before building it in the usual way:

:~/Development/tokenize$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/menugen.git menugen
Cloning into 'menugen'...
remote: Counting objects: 414, done.
remote: Compressing objects: 100% (183/183), done.
remote: Total 414 (delta 234), reused 365 (delta 211)
Receiving objects: 100% (414/414), 188.18 KiB | 11.07 MiB/s, done.
Resolving deltas: 100% (234/234), done.
:~/Development$ cd menugen
:~/Development/menugen$ make install
** Building for with version 16d3286 (16d3286) on date 16 Sep 2020 ***

<--- time passes --->

cp -r buildlinux/menugen /home/steve/sftools/bin
:~/Development/menugen$

As with Tokenize, RISC OS executables can be created using the TARGET=riscos option when calling Make.

Install some libraries

With the build tools in place, we can now start to install some more libraries. We already have OSLib (and perhaps DeskLib) built and configured; now we will need to set up some libraries which are more specific to my projects.

We will start by installing WimpLib, which is a collection of BASIC routines used by many of the projects which are written in BASIC. The process is very similar to what we are by now familiar with:

:~/Development/swiheader$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/wimplib.git wimplib
Cloning into 'wimplib'...
remote: Counting objects: 337, done.
remote: Compressing objects: 100% (121/121), done.
remote: Total 337 (delta 216), reused 323 (delta 210)
Receiving objects: 100% (337/337), 71.84 KiB | 5.53 MiB/s, done.
Resolving deltas: 100% (216/216), done.
:~/Development$ cd wimplib
:~/Development/wimplib$ make install
Building with version eedc00d (eedc00d) on date 16 Sep 2020

<--- time passes --->

:~/Development/wimplib$

If the process completes without error, then text copies of the library files have been placed into the $SFTOOLS_BASIC folder, where they can be used for linking into BASIC applications.

Next we will install a SWI definition header file, which is used by projects containing assembly language sections built by AsAsm. Once again:

:~/Development/menugen$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/swiheader.git swiheader
Cloning into 'swiheader'...
remote: Counting objects: 53, done.
remote: Compressing objects: 100% (42/42), done.
remote: Total 53 (delta 18), reused 0 (delta 0)
Receiving objects: 100% (53/53), 25.29 KiB | 3.16 MiB/s, done.
Resolving deltas: 100% (18/18), done.
:~/Development$ cd swiheader
:~/Development/swiheader$ make install
Building with version 5749235 (5749235) on date 16 Sep 2020

<--- time passes --->

/home/steve/gccsdk/env/ro-install build/AsmSWINames /home/steve/gccsdk/env/include/AsmSWINames
:~/Development/swiheader$

If the final line appears without error, the AsmSWINames file has been copied into a place where the build can find it.

All of my C software requires my SFLib library, but before we can build that we need to install Acorn’s FlexLib. This comes as part of the RISC OS sources (it’s buried in ToolboxLib, for some reason), and is covered by the Apache Licence – so for simplicity, I have provided a ready-to-go copy of the source files along with a cross-compilation-friendly Makefile. We should know the drill by now:

:~/Development/wimplib$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/flexlib.git flexlib
Cloning into 'flexlib'...
remote: Counting objects: 50, done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 50 (delta 19), reused 0 (delta 0)
Receiving objects: 100% (50/50), 23.25 KiB | 4.65 MiB/s, done.
Resolving deltas: 100% (19/19), done.
:~/Development$ cd flexlib
:~/Development/flexlib$ make install
rm -rf build/libFlexLib32.a
mkdir -p build

<--- time passes --->

/home/steve/gccsdk/env/ro-install build/libFlexLib32.a /home/steve/gccsdk/env/lib
/home/steve/gccsdk/env/ro-install src/flex.h /home/steve/gccsdk/env/include
:~/Development/flexlib$

With that in place, we’re good to go for the final piece of the environment jigsaw: SFLib itself. I’m afraid that there are no surprises for the last library:

:~/Development/flexlib$ cd ..
:~/Development$ git clone https://github.com/steve-fryatt/sflib.git sflib
Cloning into 'sflib'...
remote: Counting objects: 1324, done.
remote: Compressing objects: 100% (416/416), done.
remote: Total 1324 (delta 917), reused 1299 (delta 903)
Receiving objects: 100% (1324/1324), 286.39 KiB | 7.74 MiB/s, done.
Resolving deltas: 100% (917/917), done.
:~/Development$ cd sflib
:~/Development/sflib$ make install
*** Building with version 72cc9af (72cc9af) on date 16 Sep 2020 ***

<--- time passes --->

* INSTALLING: /home/steve/gccsdk/env
:~/Development/sflib$

And that should be it: if everything went to plan, you should now have a working build environment!