Reika v0.0.3

Next:   [Contents][Index]

Reika

This manual is for Reika (v0.0.3, 31 December 2025), a dependency manager and build initiator.

Table of Contents

Short Table of Contents


Next: , Previous: , Up: Reika   [Contents][Index]

1 Overview of Reika

1.1 Introduction

Reika is a dependency manager and build initiator, written entirely in the Crystal programming language. That is, it doesn’t perform the actual build/compilation itself, but simply ensures dependencies can be found in-tree, and then initiates the build process. It is primarily intended for Common Lisp projects, though it is not necessarily limited to this language. Projects are defined in a Project File (reika.rbp), and can have both multiple targets and custom options.


Next: , Previous: , Up: Reika   [Contents][Index]

2 Basic Usage

Reika is built around “commands”, where the command dictates how Reika behaves. Usually you use the build command, but there are actually a few different commands. To see a list of commands, run Reika like this:

reika help

Then, to run a command, just replace help with the name of the command. To see additional help information for that specific command, add --help after the command name. For example, to see help for the clean command, run Reika like this:

reika clean --help

Next: , Up: Basic Usage   [Contents][Index]

2.1 Building a Project Using Reika

Projects that are designed for Reika will have a project file named reika.rbp in the root of their directory. This file defines how to build the project’s targets.

To build the default target, simply run:

reika build

This will cause Reika to first ensure all needed dependencies are satisfied, possibly downloading them as-needed. After these have been satisfied, Reika will start the build. Dependencies will be either Fossil or Git repositories, so be sure you have both of those installed on your system and somewhere in your $PATH before running Reika.

Projects can define various options in their Project File. To see if this is the case, run Reika like this:

reika build --help

This will output both Reika’s own help documentation, as well as all of the project’s options and their descriptions. Using these is as simple as passing them as command line options. For example, if you see an option called --with-foo, then you can enable this option like this:

reika build --with-foo

Some options are simple flag options such as in the above example, but others may expect you to provide a value. You can tell that an option expects a value when it’s displayed with an extra “x” after the name. For example, if you see an option called --using-bar x, then you know --using-bar expects a value. To give it one (say the value “69abc”), you can call Reika like this:

reika build --using-bar 69abc

You can specify as many options as you need. All of Reika’s commands are documented in the next section.


2.2 Commands

help

This command simply lists all of the available commands, and gives you a brief overview of how to run Reika.

version

This command shows the current version of Reika and some license information.

update

This cause Reika to update all dependencies for a project. That is, it ensures all dependencies are downloaded and that the correct version/tag is checked out for each one. This will also run post-download commands for all dependencies that contain that key. If a dependency has already been downloaded, and a tag/version is not found in the downloaded dependency, you should try running force-update instead.

force-update

This cause Reika to update and synchronize all dependencies for a project. That is, it ensures all dependencies are downloaded, fully synchronized with the upstream repository, and that the correct version/tag is checked out for each one. This will also run post-download commands for all dependencies that contain that key.

build

This first checks that all of the dependencies are updated and checked out at the correct version (any post-download command is not run). Then, this will initiate the build process defined in the Project File.

clean

This removes any existing files defined in the Project File’s clean-files key. Only files that exist are removed. When doing the deletion, Reika does an equivalent of rm -rf, so directories can also be removed.


Next: , Previous: , Up: Reika   [Contents][Index]

3 Project Files


3.1 Format Overview

Reika Project Files are written in a format called RSConf. This format is similar to both JSON and YAML, and is specifically meant for configuration files. Anyone familiar with either JSON or YAML should be pretty comfortable using it. All Project Files must use the filename reika.rbp, and must be located in the root of your project’s directory.

The following is an example of a fairly simple project file, along with comments giving a brief explanation of the various keys. Don’t worry, these will be explained in depth soon.

name: "Neat Project" ;; Human-readable name, just for metadata
license: "AGPLv3"    ;; License of the project, just metadata

;; Authors of this project.  Just more metadata.
authors: [
  "Remilia Scarlet <remilia@posteo.jp>"
]

;; Files that will be removed when running "reika clean".  These are removed
;; with an equivalent to "rm -rf".
clean-files: [
  "bin/neat-project"
  "bin/" ;; Directories are fine, too
]

;; Defines how to build the project.  The key of each element in this key
;; becomes the name of a build target.
build: {
  binary: {
    ;; If you specify a build-file, then this is a Common Lisp Project.  This
    ;; must be a Lisp file, which will be called by the compiler.  Put your code
    ;; to build your binary into it (e.g., calls to SB-EXT:SAVE-LISP-AND-DIE).
    ;;
    ;; Only SBCL is supported right now.  Other Lisps that come with their own
    ;; ASDF implementation will be supported in the future.
    build-file: "make-neat-project.lisp"
    lisp: "sbcl" ;; Which implementation to use for the build.

    ;; The other option is to use a "cmd" instead of "build-file".  When you do
    ;; this, then "cmd" is just a command that gets called using the shell.  It
    ;; could be something as simple as a call to "make" or a shell script, or a
    ;; complicated call to some sort of compiler.
  }
}

;; Defines additional command line arguments for this project.  Use these to
;; pass build options to your build-file.
options: [
  {
    name: "compress" ;; This makes an argument called "--compress"
    type: "flag" ;; Or "string" for an argument that takes a string

    ;; When an "env" key is present, then calling this option on the command
    ;; line will set this as an environment variable and pass it to the build.
    ;; For flag arguments, this sets the environment variable to "1".  For
    ;; string arguments, it sets it to whatever was passed to the option on the
    ;; command line.
    ;;
    ;; This works for both "cmd" and "build-file" builds.
    env: "XQATOOL_COMPRESS"
    help: "Produce a compressed binary."
  }

  ;; When the "feature" key is used, then this will be pushed onto *FEATURES*
  ;; at build time.  Flag options require either "feature" or "env".  String
  ;; options only require "env", but can also use "feature".
  {
    name: "neat-feature"
    type: "flag"
    feature: ":some-feature" ;; Note that "feature" is ignored for "cmd" builds.
    help: "Enable a Neat Feature!"
  }
]

;; Dependencies can be either Fossil repos, Git repos, or local directories.
;; Mercurial will be added soon.  There can be no dependencies, or as many as
;; you need.
dependencies: {
  ;; The name of the dependency doesn't matter as long as it's unique.  It's
  ;; just used for informational display.
  cl-sdm: {
    ;; The type of this dependency.  Can be "fossil", "git", or "local".
    type: "fossil"

    ;; If the type is "fossil" or "git", then this is the URL to the
    ;; repository.  If the type is "local", then this should be the full path
    ;; to the directory.
    location: "https://chiselapp.com/user/MistressRemilia/repository/cl-sdm/"

    ;; The version, branch, checkout, commit, tag, etc. to check out.  This is
    ;; ignored for "local" dependencies.
    tag: "trunk"
  }
}

3.1.1 Top-level Keys

Key NameDescription
name The name of this project. Must be a string, and must be provided. This is only used for informational purposes.
license The license that this project falls under. Must be a string, and must be provided. This is only used for informational purposes.
authors The authors of the project. Must be a list of strings, and is optional. This is only used for informational purposes.
clean-files A list of files to clean when the clean command is used. Must be a list of strings, and is optional. When doing the deletion, Reika does an equivalent of rm -rf, so directories can also be removed.
extra-help Additional help text that is appended to the output of --help when that is used on the build, update, and force-update commands. Must be a string, and is optional. Whitespace at the start and end of the string is automatically stripped.
build Defines the list of build targets. This must be an object, where the keys are the names of the targets, and the values are objects defining each target. See Build Keys.
options Defines a list of options for the project. This must be a list of objects where each object defines an option. These options become project-specific command line arguments, and are used to control build behavior. See Option Keys.
dependencies Defines the list of dependencies for the project. This must be an object, where the keys are the names of the dependencies, and the values are objects defining each dependency. The dependencies key is optional. See Dependency Keys.

3.1.2 Build Keys

Key NameDescription
build-file This must be either a string or nil. When this is a string, it must be the path to a Common Lisp file, relative to where the project file is located. This file will be used during the build and will be passed to the Common Lisp implementation on the command line for loading. Using this key defines the project as a Common Lisp Project. Only one of build-file and cmd can be used.
cmd This must be either a string or nil. When this is a string, it defines the project as a Generic Project, and will be treated as a command that will be called by Reika using the shell when performing a build. Only one of build-file and cmd can be used.
lisp This tells Reika what Common Lisp implementation to use for the build. It is only used when build-file is used, and is ignored otherwise. This is optional, and must be a string when supplied. The only currently supported value is the string "sbcl", which tells Reika to use Steel Bank Common Lisp (SBCL).
extra-lisp-args This key is optional and must be a list of strings. These are additional arguments that will be passed when calling the Common Lisp implementation. It is only used when build-file is used, otherwise it is ignored. When specifying arguments that take a value, you must separate the value from the option. For example, if you would normally use --dynamic-space-size 8192 on the command line, and wish to use this in the build, then you would specify --dynamic-space-size and 8192 as two separate strings.
default This declares whether or not this target is the default target. Using this key is optional, and it must be a boolean value (true or false). If you don’t specifically add this key, the default is false unless there is only one target for the entire project, in which case the default is true. Each project must have exactly one default target.
pre-build-cmd This is a command that should be executed by the shell after the dependencies are updated, and before the build is initated. Using this key is optional, and it must be a string when used.
post-build-cmd This is a command that should be executed by the shell after a build successfully completes. Using this key is optional, and it must be a string when used.

3.1.3 Option Keys

Key NameDescription
name The name of the option. This is required and must be a string. It also cannot start with dashes, must be at least two characters long, and cannot be one of the built-in command line options. Do not add - or -- to the beginning this string; this will be handled automatically by Reika.
type The type of the option. This is required and must be a string. There are two supported types: "flag" (which are options that do not take a value) and "string" (options that require a value).

When using a "flag" option, then either the feature or env key must also be present (or both). When using a "string" option, then either the env key must be present, but the feature key is optional. See below for the description of these keys.

help A string that describes what this options does. This is required and must be a string. It will be shown when using --help together with the build command. If this is a "string" option, then the extra “x” will automatically be added by Reika, so there is no need to specify this yourself.
feature A Common Lisp feature that will be pushed onto the Common Lisp implementation’s *FEATURES* when this option is called. This key is optional and must be a string. You should almost always specify a Common Lisp keyword as this key’s value, for example feature: ":some-feature".
env Defines an environment variable that will be defined when this option is used. These environment variables are passed to the Common Lisp implementation when performing the build. This key is optional and must be a string.

When the option is a "string" option, then the value of this environment variable will be the string that as passed on the command line to this option. When the option is a "flag" option, then this environment variable is simply set to "1".


3.1.4 Dependency Keys

Key NameDescription
type The type of the dependency. This is required and must be a string. There are three supported types: "fossil", "git", and "local".

When updating dependencies, Reika will clone "fossil" and git dependencies. For "local" dependencies, Reika will instead symlink them into the deps/ directory. See How Reika Works.

location Where to find this dependency. This is required and must be a string. For "fossil" and "git" dependencies, this must be a URI, and will be passed to the Fossil or Git program. For "local" dependencies, this should be a path name (either a full path, or relative to where the Project File is located) pointing to the dependency.
local This is optional and must be a string or nil. When this is a string, then it is used as a “local name” for this dependency within the deps/ directory. For example, if you have a "fossil" dependency with a location of "https://fossil.cyberia9.org/cl-remimatrix/", and local is set to "foo", then you will end up with a directory named deps/foo/ after the dependency has been cloned.
tag This indicates which branch/tag/version/etc to check out for the dependency. It is only used for dependencies that are not of type "local". This is optional and must be a string, an object, or nil. When it is nil or not specified, then the default is "trunk".

String values are checked out verbatim, so if this is equal to "foo", then it will attempt to check out a branch/tag/whatever named "foo" in the downloaded repository.

When this is an object, then it must be a Version Object. See Version Object Keys.


3.1.5 Version Object Keys

Key NameDescription
version A version number. This is required and must be a string. This key is treated as a Semantic Version. It will also handle versions that start with "v" or "version-".
mod A version modifier that indicates how the version is to be interpreted. This is required and must be a string. The only supported values are "or_later, "this_minor, and "strict.

When this is "or_later", then the tag who’s name is a version that is greater than or equal to the value in the version key will be selected.

When this is "this_minor", then the tag who’s name is a version that has the same major and minor version as the value in the version key, and is also greater than or equal to the value in version, will be selected.

When this is "strict", then a tag that matches the version exactly will be selected. In this case, it is likely better not to use a Version Object and instead specify the version as a string in the parent dependency.


Next: , Previous: , Up: Reika   [Contents][Index]

4 How Reika Works

This chapter describes how Reika works internally. If you are only interested in using Reika to build a piece of software, then you can skip this chapter. However, if you are interested in integrating Reika into your project, then read on to understand how Reika works.

Reika is primarily a dependency manager and build initiator. That is, it doesn’t perform the actual build/compilation itself, but simply initiates the process. It is primarily designed for Common Lisp projects that produce binary executables, but can (in theory) be used with other languages as well (though this is not officially supported). These two types of projects are called Common Lisp Projects and Generic Projects, respectively. Likewise, their corresponding build processes are called Common Lisp Builds and Generic Builds. These will be described in the following sections.


4.1 A Note on Dependencies

Unlike many other dependency managers, Reika specifically does NOT solve dependencies, and this is by design. So if your project has a dependency on some package X, and X has a dependency on Y, then you must list both X and Y in your project file as dependencies. There are three reasons for this design:

  1. Solving those would be prohibitively difficult since Reika is specifically not for libraries, so dependencies usually would not have project files. Also, there are multiple ways to specify a version, and these methods are not portable across languages.
  2. Though it won’t prevent supply chain attacks, this will hopefully help prevent them (at least a little bit) by forcing devs to think twice and examine the deps a bit better before adding them.
  3. I (Remilia) am a Slackware user and I prefer to apply the KISS principal when it comes to dependencies.

4.2 Generic Builds

Generic Builds are simple, so we’ll cover those first. These are defined by the presence of a cmd key in the Project File. See Build Keys. For a Generic Build, the value of this key is just a command that Reika will pass to the shell for execution. Thus you can use this to call an external Makefile, a Rakefile, a shell script, a compiler, and so on.

Keep in mind that special handling of the dependencies are done with this type of build; Reika just ensures that the dependencies have been cloned to the deps/ directory and that their check outs are correct. It is up to you to actually utilize them with your command. Reika will, however, still pass environment variables (specified using option in the Project File) on through to the command.


4.3 Common Lisp Builds

Reika has been specifically designed to hide as much of the Common Lisp build process from the end user as possible, making it easy for people not accustomed to Common Lisp to build software written in the language. Essentially, if the user has to see a REPL, use Quicklisp, or mess with ASDF in any way, then Reika is not doing its job. Instead, you as a project’s developer hide as many of Common Lisp’s intricacies as possible using Reika’s features. It does this using a Project File, and a Build File.

The Project File is a special file named reika.rbp located in the root of your project’s source tree. See Project Files. Build Files are described later in this chapter.

Before a build is initiated, Reika will ensure that all required dependencies are placed into a special directory named deps/, which will be created in the root of your project’s source tree. These dependencies are either cloned (in the case of Fossil and Git dependencies) or symlinked (for local dependencies) into this deps/ directory prior to the build. Reika will also ensure that each dependency is at the correct checkout/tag/branch/etc. This last step is only done for Fossil and Git dependencies. Once the dependencies have been checked, Reika begins the process of a Common Lisp Build.

Common Lisp Builds are what Reika was originally designed to perform. When doing this type of build, Reika first constructs a set of arguments that it will pass to the selected Common Lisp implementation. This list of arguments begins with any arguments that are defined in the Project File’s extra-lisp-args key. See Build Keys.

After these arguments will be an argument to ensure ASDF is loaded, followed by another argument that clears the ASDF central registry. This is important, as it is key to how Reika performs Common Lisp Builds. After this, Reika looks at all of the dependencies it put into deps/, then generates more arguments to place the directories of these dependencies onto the ASDF central registry, thereby recreating it. It also places the project’s root directory into the ASDF central registry.

Following this, Reika processes any project-specific options that were called. If an option requires a feature symbol be placed into *FEATURES*, then it will generate an argument to do this. It also takes note of any environment variables it needs to define for later use.

Finally, Reika looks at the build-file key in the Project File and generates an argument to load this into the Common Lisp implementation. It is this file that performs the actual build itself. Usually this means that this file contains a call into the function that dumps a binary, such as sb-ext:save-lisp-and-die for SBCL.

Once it constructs this set of arguments, the Common Lisp implementation is called with them, as well as any environment variables that were set with the options defined in the project file.


4.4 The Build File

The Build File (which is pointed to by the build-file key in the Project File) is a file containing Common Lisp code that performs the actual build process. This file is only used for Common Lisp Projects. Reika will use this file in conjunction with the selected Common Lisp implementation to perform the build. For Common Lisp Builds, there is one Build File per target. A Build File can be as simple as something like this (assuming SBCL is used):

(asdf:load-system :my-program)

(let* ((outdir (merge-pathnames #P"./bin/"))
       (filename (merge-pathnames outdir "my-program"))
       (toplevel 'my-program:main))
  (ensure-directories-exist outdir)
  (sb-ext:save-lisp-and-die
   filename
   :executable t
   :toplevel toplevel
   :save-runtime-options t
   :purify t))

There are many ways to write a Build File, including ways to call into ASDF to produce a binary. Options defined in the Project File that declare environment variables should also be processed by your Build File, as these are intended to control the behavior of the build. How this is accomplished depends on the Common Lisp implementation, but one possible solution with SBCL may look like the following:

(cond
  ((equalp (sb-posix:getenv "COOL_PROGRAM_MODE") "release")
   (format t "Doing a release build...~%")
   ;; ... more code ...
   )

  ((equalp (sb-posix:getenv "COOL_PROGRAM_MODE") "debug")
   (format t "Doing a debug build...~%")
   ;; ... more code ...
   )

  (t
   (format *error-output* "ERROR: bad program mode!~%")
   (sb-ext:exit :code 1)))

You can also define options in the Project File that push new features onto the *FEATURES* variable in Common Lisp, which you can then use in your code to do conditional building. For example, if you have an option in your Project File that declares feature: ":foo-bar", then you might have code inside your Common Lisp program that looks like this:

(defun foo ()
  #-foo-bar
  (progn
    (format t "The foobar feature was disabled at build time...~%")
    ;; ... more code ...
    )

  #+foo-bar
  (progn
    (format t "The foobar feature was enabled at build time...~%")
    ;; ... more code ...
    ))

Previous: , Up: Reika   [Contents][Index]

Index

Jump to:   B   C   D   G   I   P  
Index Entry  Section

B
build file: The Build File
building a project: Building a Project Using Reika

C
commands: Commands
common lisp builds: Common Lisp Builds

D
dependencies: A Note on Dependencies

G
generic builds: Generic Builds

I
introduction: Overview

P
project files: Project Files
project files, overview: Format Overview
project files, toplevel keys: Top-level Keys
project files, toplevel keys, name: Top-level Keys
project files, toplevel keys, license: Top-level Keys
project files, toplevel keys, authors: Top-level Keys
project files, toplevel keys, clean-files: Top-level Keys
project files, toplevel keys, extra-help: Top-level Keys
project files, toplevel keys, build: Top-level Keys
project files, toplevel keys, options: Top-level Keys
project files, toplevel keys, dependencies: Top-level Keys
project files, build keys: Build Keys
project files, build keys, build-file: Build Keys
project files, build keys, cmd: Build Keys
project files, build keys, lisp: Build Keys
project files, build keys, extra-lisp-args: Build Keys
project files, build keys, default: Build Keys
project files, build keys, default: Build Keys
project files, build keys, default: Build Keys
project files, option keys: Option Keys
project files, option keys, name: Option Keys
project files, option keys, type: Option Keys
project files, option keys, help: Option Keys
project files, option keys, feature: Option Keys
project files, option keys, feature: Option Keys
project files, dependency keys: Dependency Keys
project files, dependency keys, type: Dependency Keys
project files, dependency keys, name: Dependency Keys
project files, dependency keys, local: Dependency Keys
project files, dependency keys, tag: Dependency Keys
project files, version object keys: Version Object Keys
project files, version object keys, version: Version Object Keys
project files, version object keys, mod: Version Object Keys

Jump to:   B   C   D   G   I   P