Self-hosting — mtcc, MRP1, ruf recipes and the build pipeline¶
The defining feature of Equinox: the OS compiles its own userland
from C sources, inside the OS, with its own compiler. The ISO ships
sources, not binaries; on first boot equinoxinstall (or eggkg) runs
the in-OS compiler mtcc over them.

The pipeline¶
- Sources on the ISO —
tools_user/*.c(text filters),libc/*.c + *.h(a 13-module C subset), andmtcc.mrp(prebuilt compiler). The bash-class coreutils are not shipped; they arrive as a package (see PACKAGES.md). - GRUB module routing —
mrp_bootloaderroutes nested.cmodules into RAMFS/equinox/toolsand/equinox/libc; flat.cfiles land in/testas ready-made mtcc samples. - Compile in-OS —
equinoxinstallspawnsmtcc.mrpper file (ormtcc -makeover a recipe); each successful compile logsi/N OK name.mrpand the artifact is a MRP1 ring-3 executable. Host vs in-OS output is verified byte-identical by the regression harness. - Install & resolve — artifacts live in
/equinox/tools(+/binfor packages); the shell prefers userland tools over its own builtins via the system path: root,/bin,/equinox/tools,/equinox/games.
mtcc — the in-OS C compiler (mtcc.c)¶
A single-pass compiler: lexer → parser → direct x86-32 codegen, plus
a small preprocessor that splices <morph.h> / <multitasking.h> /
<fileio.h>. Deliberately a C subset:
- no
structreturn/pass by value, nofloat, nounsigned(mostly), noswitch, nosizeof— the libc and every tool live within these limits; - printf-family with 5 conversions,
%uas true unsigned,sscanf, ctype, string extras,qsort,randship in the libc; - undefined references are rejected with the call-site line number;
- output is an MRP1 flat ring-3 image with an 18-byte header
(loader:
mrp_loader.cpp; per-task arena0x500000…0x2600000with demand paging).
Usage:
| Command | Effect |
|---|---|
mtcc /test/hello.c |
compile and run (dev loop) |
mtcc -c <file.c> |
compile only → <file>.mrp next to the source |
mtcc -make <file.ruf> |
execute a ruf v3 build recipe (many jobs) |
ruf v3 — build recipes¶
A .ruf file is a tiny makefile dialect understood by mtcc -make
(and by equinoxinstall -build). v3 adds real variables:
# build.ruf — variables with :=, macros, copy/move ? to
name := "bash"
src := "/equinox/.local/bash/src"
out := "/equinox/.local/bash"
job cat from "$(src)/cat.c" to "$(out)/cat.mrp"
job grep from "$(src)/grep.c" to "$(out)/grep.mrp"
# ...
copy ? from "$(out)" to "/bin" # install step, '?' = every product
job <name> from <.c> to <.mrp>— one mtcc compile job; the runner reports per-job progress and a failure summary at the end.copy ? / move ? from X to Y— bulk install of all products.- The bash package's
build.ruf(in Eggkg-l) is the reference example: ~20 jobs, all installed into/binin oneeggkg install bash.
equinoxinstall — the driver of the pipeline¶
| Command | Effect |
|---|---|
equinoxinstall |
Full 4-phase wizard (disk → layout → NIC config → build) — see INSTALL.md |
equinoxinstall -compile <dir> |
Phase [4/4] only: compile <dir>/libc + <dir>/tools; on a volume the sources are kept (they are the install content) |
equinoxinstall -build <file.ruf> |
One recipe via mtcc -make |
equinoxinstall -build <name> |
One tool from /equinox/tools via mtcc -c |
equinoxinstall -build *.ruf |
Every matching recipe (single-star glob) |
RAMFS builds delete the .c after success (the next boot brings them
back); volume builds keep them so the install can be re-compiled
in place after edits.
MRP1 executables & the morph API¶
- MRP1: flat binary + 18-byte header at
0x500010; loaded into the task's demand-paged arena; per-task malloc arena reset on exit. - Program calls the kernel through syscall int 0x80 #1–#54
(SYSCALLS.md) wrapped by the morph API
(
mrp_user/Morph.h, ~20 functions: print, file I/O, gfx, spawn/wait, pipe…). - ELF32 (
ET_EXEC, static) is also supported — see ARCHITECTURE.md.
Writing a tool¶
#include <morph.h>
int main(void) {
morph_printf("hello from ring 3\n");
return 42; /* reported by wait() */
}
mtcc /test/hello.c # compile + run immediately
# or as a package product (see PACKAGES.md) -> lands in /bin
Constraints: plain C within the mtcc subset, libc subset only, ≤5 printf args, no filesystem global state — and it must compile fast, because it compiles inside an 8-task 256 MB hobby kernel.
Testing the pipeline¶
make test # host harness: mtcc vs interpreter, libc parity
python3 scripts/eggkg_build_test.py # full bash package build in-OS
make test-install # manual install path incl. set -x scripts