MMotor OS
Getting started

Building Motor OS

Motor OS is cross-compiled on Linux. One script builds the compilers, the system, and the VM images from a clean checkout; after that, make rebuilds the system in minutes. The script is tested on Ubuntu 24.04 and 26.04.

What you need

The packages the script installs are git, build-essential, nasm, clang, cmake, ninja-build, zlib1g-dev, libssl-dev, pkg-config, curl, qemu-system, and qemu-utils, plus rustup without a default toolchain. Cloud Hypervisor and Firecracker are not installed; QEMU is enough to build and test.

The one-script build

mkdir motor-dev
cd motor-dev
git clone https://github.com/moturus/motor-os.git
cd motor-os
src/build-motor-os.sh

src/build-motor-os.sh is the single supported entry point. In order, it:

  1. installs the missing host packages and rustup, creates the moto-tap interface with the forwarding and masquerade rules that let VMs reach the Internet, and makes /dev/kvm accessible. Each step is skipped when it has already been done; MOTOR_SKIP_HOST_NETWORK_SETUP=1 skips the network part if you have configured it yourself;
  2. checks out the exact Rust, LLVM, Cargo, and mlibc revisions declared in src/toolchain-versions.sh. A checkout that does not match the declaration, including one with a modified or untracked file, is an error; the build never advances a branch or runs cargo update;
  3. builds a Linux-host Clang and LLD from the LLVM fork;
  4. runs Rust's bootstrap once and installs the result as an immutable toolchain: rustc, rustdoc, Cargo, the standard library for Linux and for x86_64-unknown-motor, Clippy, rustfmt, and rust-src. The toolchain is registered with rustup under a name that ends in a 64-character key derived from its inputs, and validated component by component;
  5. builds the native side, called an assembly: the C-ABI shim over rt.vdso, compiler-rt, mlibc, libunwind, libc++abi, and libc++, the native LLVM multicall binary, Lua, native rustc, and ripgrep. An assembly is keyed by its inputs too, so a change to moto-rt or mlibc produces a new assembly without a new compiler;
  6. writes the toolchain and assembly manifests, then builds the base, standard, and developer images into vm_images/release/.

It does not start a VM. Its outputs live next to the checkout, in the directory MOTORH names (the parent of the checkout by default; set it before the first run to put them elsewhere):

DirectoryContents
toolchain-src/The managed Rust checkout, with LLVM and Cargo as submodules, and the mlibc checkout.
toolchains/<name>/One immutable host toolchain per key: the rustup toolchain the repository selects.
assemblies/<key>/One assembly per key: build/ (component build trees), sysroot/ (the C/C++ cross sysroot and the motor-clang linker wrappers), and images/ (the libc, ripgrep, LLVM, and rustc files the imager places into the images).

The build is incremental and safe to rerun: after a failure, run the same command again. A completed toolchain or assembly is reused only after its manifest and artifacts validate, and older keys are kept rather than deleted. A .building directory next to an output means a build is in progress or was interrupted; a MOTOR-TOOLCHAIN-REJECTED or MOTOR-ASSEMBLY-REJECTED marker keeps an invalid result for diagnosis. Fix the cause before removing either.

The toolchain the repository selects

The checkout's rust-toolchain.toml names the exact toolchain the build installed, key included, so ordinary commands run from the checkout use it without a +nightly, +stable, or +dev-* selector; do not add one. To see what is selected:

rustc -vV
cargo -Vv
rustc --print sysroot

Cargo reports 1.99.0-dev and the exact Cargo commit: the current toolchain is built from the Rust 1.99 beta, and the stable 1.99.0-motor.1 release waits for upstream Rust 1.99.0. The sysroot contains MOTOR-TOOLCHAIN-MANIFEST and lib/rustlib/MOTOR-TOOLCHAIN-KEY; every assembly, and the developer image under /devtools/toolchain/manifest, contains MOTOR-ASSEMBLY-MANIFEST. The manifests record the revisions, keys, and hashes everything was built from, and make refuses to run with a toolchain that has no key.

The build also records which assembly this checkout uses, in the ignored directory .motor-os/. make clean keeps that record, and a second clone under the same MOTORH makes its own. When the runtime sources change and no assembly matches any more, run src/build-motor-os.sh again: it derives the new key and builds only what is missing. To inspect or change the selection by hand:

src/select-toolchain-assembly.sh --show
src/select-toolchain-assembly.sh --list
src/select-toolchain-assembly.sh --pin ASSEMBLY_KEY

Building with make

With the toolchain in place, the Makefile in the repository root builds the system, the programs, and the images. Debug is the default profile:

make -j"$(nproc)"                  # base and standard images, debug, into vm_images/debug/
make -j"$(nproc)" images           # the developer image as well
make -j"$(nproc)" BUILD=release    # the same into vm_images/release/
make -j"$(nproc)" dev.img BUILD=release
make sys-io rush                   # individual components
make system-tty.img                # the test-only System-console image
make clippy
make clean                         # removes build/ and vm_images/; keeps the toolchains and assemblies

Every component is a target named after its binary: kernel, vdso, sys-io, sys-init, sys-tty, strobe, dns-resolver, sysbox, rush, russhd, red, rmux, kibim, httpd, httpd-axum, lorry, curl, gears, mdbg, systest, and so on; the image targets are base.img, main.img, dev.img, and system-tty.img. Stripped binaries go to build/bin/<profile>/, and Cargo's target directories, with the unstripped binaries that addr2line needs, to build/obj/<toolchain-key>/<profile>/<component>/.

The imager (src/imager) assembles each image from a YAML description and the permission policy; see Filesystem. The standard and developer images additionally take the libc, ripgrep, LLVM, and rustc files from the selected assembly, so they cannot be built without one; the base image needs none.

Running the tests

src/tests/full-test.sh              # debug
src/tests/full-test.sh --release
src/tests/full-test-dev.sh --release

full-test.sh is the suite every change is expected to pass. It runs the host-side tests of the build scripts, builds the standard image and the test programs, runs the host cargo test suites (the imager, Motor FS, the networking stack, rush, rmux, red, russhd, rnetbench, sys-init, and the runtime libraries) and the System-console, TUI, and terminal-size tests, then boots the image under QEMU and drives it over SSH: systest, mio-test, tokio-tests, crossterm-smoke, SFTP, and the rest. A run is limited to fifteen minutes. full-test-dev.sh does the same against the developer image, then compiles the source snapshots natively inside the VM and runs Lorry's own suite; it wants a 4 GiB VM. full-test-networking.sh is the networking subset with rnetbench throughput runs, and stress-soak.sh keeps one VM under concurrent load for as long as you ask, watching for crashes and stalls.

On the developer image the same test programs are in /devtools/tests and can be run from the shell; see Developer image.

Working on the compilers

Changes to the Rust or LLVM forks are built from your own checkout, without touching the managed one:

src/build-motor-os.sh \
  --source-mode authoring \
  --rust-source /absolute/path/to/rust \
  --authoring-base FULL_40_CHARACTER_BASE_COMMIT

The checkout owns its src/llvm-project submodule. Local commits, modified files, and untracked files in both trees become part of the toolchain's identity, and the build reads them as they are, without fetching, switching, resetting, or cleaning anything. Such a toolchain gets a distinct, non-release name and key and cannot replace the one named in rust-toolchain.toml. A managed source is updated only by changing its revision in src/toolchain-versions.sh.

Further reading

The repository describes the build in more depth: docs/build-motor-os.md (the source modes, build order, and manifests), docs/build-llvm.md and docs/build-rustc.md (the C/C++ and Rust components and their layout on the developer image), and docs/assembly-selection.md (how an assembly is chosen and validated, and how to recover a stale selection). What the toolchains contain and how programs are compiled with them is on the Toolchains page.

Once you have images, Running Motor OS describes the VM scripts, and Hello Motor OS walks through running your own program.