Installation¶
The Termina toolchain can be obtained as a prebuilt Docker image or built from source, and the choice depends on the intended use. For readers who only wish to write Termina programs and reproduce the examples presented in this book, the recommended approach is the prebuilt Docker image, which provides a complete and ready-to-use development environment without requiring any installation on the host beyond Docker itself. For those who intend to work on the transpiler, or who operate on systems where Docker is not a suitable option, the transpiler can also be built from source and the supporting components configured manually. The remainder of this book assumes the Docker-based setup, although every example applies equally to a native installation.
Throughout the book, commands intended to be typed in a terminal are shown
preceded by a $ prompt. This character indicates the beginning of a command
and must not be typed by the reader.
Using the Docker image¶
The Termina project publishes a container image that bundles the transpiler,
the Operating System Abstraction Layer (OSAL) against which the generated code
is compiled, and the cross-toolchains of the rtems5-leon3sf-nexysa7,
rtems6-zynq7000-pynqz2 and freertos10-stm32l432-nucleol432kc platforms.
Because all of these elements
are distributed together within a single image, the user is relieved of the usual tasks of installing a
compiler, obtaining the appropriate toolchain for each target, and ensuring
that the different components are mutually compatible. For this reason, the
image is the recommended starting point.
The only prerequisite on the host system is Docker. On macOS and Windows, the
simplest option is to install Docker Desktop,
whereas on Linux, Docker Engine can be installed from the distribution's own
repositories or from the official packages.
The image is built for the linux/amd64 architecture. Consequently, on Apple
Silicon computers, Rosetta must be enabled in Docker Desktop, under Settings →
General → Use Rosetta for x86/amd64 emulation, so that the SPARC and ARM
cross-compilers can run transparently. On Windows, Docker Desktop should be
used with the WSL 2 backend.
Once Docker is available, the image is retrieved with the following command.
The latest tag tracks the most recent published release, while a specific
version such as v0.6.0 may be pinned instead in order to obtain a
reproducible environment, which is particularly convenient in a course where
all participants must work with an identical setup:
The following sections describe three ways of using the image, which can be combined according to the task at hand.
Working in a disposable container¶
The quickest way to obtain the toolchain, and the one with which a first project is created, is to mount the current directory into a disposable container and work from a shell within it. The container is removed on exit, together with any change made outside the mounted directory. The project resides on a bind mount of the host directory, and every file produced in it remains on the host.
The resulting shell provides the complete toolchain. A project is created with
termina new:
The application is then written under app/ and src/. Transpiling it to C and
compiling the result is done with:
Defining a termina alias¶
When editing is performed in a host application and the only service required
from the image is the transpiler itself, an alias may be defined that wraps
each invocation in a container. Once this alias is in place, the commands
termina new, termina build, and the rest behave as though the tool were
installed natively, while in reality each execution launches the image against
the current directory.
The alias covers transpilation, which is normally the only service needed from
the host. The compilation and execution of the generated code, however, rely on
make and the bundled toolchains, and therefore those steps still require a
shell inside the container, whether the disposable container shown above or the
Dev Container described next.
Working inside Visual Studio Code¶
For users of Visual Studio Code, the most convenient arrangement consists of letting the editor open a project inside the container. Under this configuration, the transpiler, its language server, and the Termina syntax extension run within the image, while the source files remain on the host, where they can be managed as usual.
The editor support is provided by the Termina extension, published on the Visual
Studio Code Marketplace as
termina-lang.termina.
It adds syntax highlighting for .fin files and connects to the transpiler's
language server for diagnostics and code navigation. Listing it under
extensions in the .devcontainer/devcontainer.json shown below installs it
automatically when the project is opened in the container.
This arrangement requires the
Dev Containers
extension to be installed in Visual Studio Code. In the root of a project,
created as shown above, a file named .devcontainer/devcontainer.json is added
with the following contents:
{
"name": "Termina dev container",
"image": "ghcr.io/termina-lang/docker-termina:latest",
"customizations": {
"vscode": {
"extensions": ["termina-lang.termina"]
}
},
"remoteUser": "vscode"
}
The project folder is then opened in Visual Studio Code, selecting the
Reopen in Container command when prompted, or invoking it from the command
palette. From that point on, the integrated terminal is a shell running inside
the image, in which termina, make, and the cross-compilers are all
available, and the editor provides syntax support through the bundled extension.
A project can also be started this way from scratch, by placing the
.devcontainer/devcontainer.json shown above in an empty folder, reopening the
folder in the container, and running termina new from the integrated terminal.
Building from source¶
The native installation is intended for those who wish to work on the transpiler itself, as well as for the occasional environment in which Docker is not appropriate. In this case the user assembles the same components that the image otherwise provides.
The transpiler is written in Haskell and built with Stack, which may be installed directly or through GHCup:
Stack reads the exact GHC version required by the project from its stack.yaml
file and downloads it automatically, so no compiler needs to be installed
manually and a single stack install command produces the binary. The repository is cloned at the current release tag, v0.6.0 at the time of
writing, and then built; stack install places the resulting termina
executable in Stack's local binary directory, which is ~/.local/bin by
default and should therefore be present in the PATH:
$ git clone --branch v0.6.0 https://github.com/termina-lang/termina.git
$ cd termina
$ stack install
The command termina --version confirms that the tool has been installed
correctly and reports the release it was built from:
The code emitted by the transpiler is compiled against the OSAL, which must
likewise be present on the system. The OSAL is cloned at the corresponding
release tag. The transpiler and the OSAL must share the same MAJOR.MINOR
version, so any v0.6.x of one is compatible with any v0.6.y of the other.
The Makefile generated by termina build expects to
find the OSAL under /opt/termina-osal, and the most straightforward
configuration is therefore to create a symbolic link at that location:
$ git clone --branch v0.6.0 https://github.com/termina-lang/termina-osal.git
$ sudo ln -s "$(pwd)/termina-osal" /opt/termina-osal
Should the use of /opt be undesirable, the location of the OSAL may instead
be supplied explicitly on each build through the make command line:
The steps described so far are sufficient for the default posix-gcc
platform, whose generated code is compiled with the host's own gcc and
requires nothing further. Targeting one of the embedded platforms additionally
requires the corresponding cross-toolchain to be present in the PATH. The
rtems5-leon3sf-nexysa7 platform, which targets LEON3 under RTEMS 5, relies on
the Gaisler RCC toolchain (sparc-gaisler-rtems5-gcc), available from
Gaisler. The
rtems6-zynq7000-pynqz2 platform targets the Zynq-7000 of the PYNQ-Z2 board
under RTEMS 6. It relies on the arm-rtems6-gcc toolchain and on the
xilinx_zynq_pynq board support package, both built from the RTEMS 6 sources
with the RTEMS Source Builder. They are expected
under /opt/rtems/6, and another location is passed to make as
RTEMS6_PREFIX. The freertos10-stm32l432-nucleol432kc platform, which
targets the STM32L432 microcontroller of the NUCLEO-L432KC board under
FreeRTOS 10, relies on the bare-metal ARM toolchain arm-none-eabi-gcc with
its C library, newlib. The Arm GNU Toolchain, available from
Arm,
includes both, and the image ships its release 13.3.rel1. The
target platform is selected when the project is created, by means of
termina new myproj --platform rtems5-leon3sf-nexysa7, or subsequently by
editing the platform field in the termina.yaml file.