Site logo
Stories around the Genode Operating System RSS feed
Emery Hemingway avatar

Packaging Nim


This is a brief writeup on creating and publishing Nim packages. This instructions assume that you are using a fairly up-to-date Linux system and have a copy of the Nim distribution installed as well as the Tup utility. This article assumes some familiarity with the package archives used by Sculpt.

I will not cover writing Nim components or using Genode-specific Nim libraries here. For that I recommend reading the Nim documentation first and then looking at some of the Nim repositories I have published on GitHub, imap_report being a simple example of a native component.

Toolchain

The Genode toolchain is required to be present on the system, see https://genode.org/download/tool-chain for instructions.

SDK

A copy of my SDK is required, which must be present under /opt/genode. The SDK can be downloaded from https://github.com/ehmry/genode/releases and can be extracted as follows:

 wget https://github.com/ehmry/genode/releases/download/sdk-19.02-r1/sdk.tar.xz
 tar xPf sdk.tar.xz

Nimble

It is strongly recommended to use the Nimble tooling for building Genode components, and required for following the rest of this guide. See https://github.com/nim-lang/nimble for Nimble instructions. The only Genode quirk is that the genode package should be added as a dependency to your *.nimble file. This modules is registered in the global index and will be automatically fetched by Nimble at build time.

 # example.nimble
 requires "nim >= 0.19", "genode >= 19.02"

Imports

Compiling a Nim application for Genode requires the genode module to be imported somewhere within the application. This is simply to inform the Nim compiler of where the Genode toolchain is and configures the proper option flags. Nimble will resolve the module path automatically when the dependency is declared as explained before.

 # example.nim
 import genode
 echo("hello world!")

Tupfile

Now the Nimble tools must be combined with a higher-level build-system. The standard recursive Make scripts are more or less incompatible, so for Nim we must roll our own. I use a git super-repo with a Tup build system and add my Nimble projects as submodules. I have a skeleton of the repo I use at https://github.com/ehmry/genode-tup-super, which handles building Nimble projects and produces signed packages. The following assumes that the skeleton is being used at the d834e652d81ea54bef575576f2d5706f41c8db6d commit.

First, add your project into the super-repo under the nim directory and add a file named Tupfile into the root of your project directory with the following contents:

 TARGET_NAME = your_project

 PKG_DEPENDS += \
   @(PUBLIC_SRC_LIBC) \
   @(PUBLIC_SRC_VFS) \
   _/src/$(TARGET_NAME) \

 include_rules
 include $(NIMBLE_BINARIES_INCLUDE)
 include $(NIMBLE_PACKAGE_INCLUDE)

This examples assumes a runtime file is present in your project directory that will be included into a launchable package, if this is not the case, remove the last line.

To describe each directive,

  1. The TARGET_NAME variable is the name used by for the bin, raw, and pkg archives, unless BIN_NAME, RAW_NAME, or PKG_NAME variables are defined.

  2. The PKG_DEPENDS variable contains the versioned archive that will be added to the archives file within the output package. An archive path with the _ character in the place of the depot user will be replaced with a path referring to the current version of that package in the local repo, if present. A variable reference in the form of @(...) refers to a configuration define, which will be explained later.

  3. include_rules will include the Tuprules.tup files found in each parent directory from the root down, placing this Tupfile in the nim directory implies that nim/Tuprules.tup will be included.

  4. The NIMBLE_BINARIES_INCLUDE variable is defined in ../Tuprules.tup and points to a file containing rules for building Nimble projects.

  5. The NIMBLE_PACKAGE_INCLUDE variables is the equivalent of NIMBLE_BINARIES_INCLUDE for creating pkg archives.

Building

To build the project invoke the tup utility within the super-repo root or a subdirectory. The skeleton is configured to a build x86_64 variant (the only variant tested so far) at the build-x86_64 directory. This directory contains important configuration at tup.config.

Variables in this file that begin with CONFIG_ are available anywhere within this repo as @(...), so CONFIG_FOO=bar will be available as @(FOO). The skeleton tup.config file contains some pegged packages versions that were referenced in the project Tupfile as described earlier. This file contains other required variables, which should be self-explanatory.

The current build of you package should be available under build-x86_64/depot/bin/... and build-x86_64/depot/pkg/.... These directories can be copied into the local depot directory of a Sculpt instance and should run if the dependency specification is complete.

Tup will also create build-x86_64/Makefile. This makefile will create signed tarballs from the depot directory into a public directory, and can be copied to a file server for distribution. Extending the build-system with Make is necessary because Tup cannot create files with names produced by evaluation of rules, all outputs must be explicit.

Its a work in progress and probably needs a bit of massaging at first, but so far it has been the most effective way to build and deploy Nim.