Forest OS Build System — Overview
Table of Contents
- Architecture Overview
- The Build Pipeline
- Build Dependencies Between Components
- The Make-Based Build System
- Configuration System Integration
- Feature Gating and Conditional Compilation
- Build Outputs and Artifacts
- The createos.sh Interactive Build Tool
- Build Troubleshooting
- Clean Builds vs Incremental Builds
Architecture Overview
Forest OS is assembled from three independently-built components plus a cross-toolchain. Here's the breakdown:
| Component | Directory | What it produces |
|---|---|---|
| Fern (kernel) | fern/ |
fern.bin (BIOS), fern.elf / BOOTX64.EFI (UEFI) |
| ForeB (bootloader) | foreboots/ |
stage1/2/3.bin, BOOTX64.EFI, forebo.img, esp.img, forebo.iso |
| Userspace | userspace/ |
Cross-compiled ELF binaries (ls, cat, forest-shell, etc.) |
| Cross-toolchain | forestos-toolchain/ |
i686-forestos-gcc, x86_64-forestos-gcc and friends |
The Fern kernel build system at fern/Makefile is the central orchestrator. It delegates to ForeB for disk image and ISO creation, and to the userspace tree for initial ramdisk contents.
$FOREST/
├── fern/ # Kernel source tree + Make-based build system
│ ├── Makefile # Top-level orchestrator (698 lines)
│ ├── conf.sh # Kconfig-style configurator
│ └── build/ # Make fragments (*.mk) + features/
├── foreboots/ # ForeB bootloader (BIOS assembly + UEFI C)
│ ├── Makefile # 955 lines, BIOS + UEFI targets
│ ├── bios/ # NASM assembly stages (1/2/3)
│ └── uefi/ # Clang-compiled UEFI application
├── forestos-toolchain/ # Cross-toolchain source package
│ ├── build-toolchain.sh # Canonical builder (787 lines)
│ └── install/ # Built compilers (generated)
├── userspace/ # 40+ cross-compiled userspace apps
│ └── Makefile
├── libs/ # Shared libraries (libc, uacpi, qrcodegen)
├── createos.sh # Interactive build GUI (1012 lines)
└── MAKE_AN_OS.md # End-to-end build guide
The Build Pipeline
Building Forest OS follows a strict linear pipeline. Each stage depends on the previous one.
┌─────────────────────────────────────────────────────────────────┐
│ Forest OS Build Pipeline │
├─────────────────────────────────────────────────────────────────┤
│ 1. TOOLCHAIN forestos-toolchain/build-toolchain.sh │
│ └─> install/bin/{i686,x86_64}-forestos-{gcc,ld,...} │
│ │
│ 2. CONFIGURATION fern/conf.sh --defconfig / --menuconfig │
│ └─> .forestos_config --> build-config.mk │
│ │
│ 3. KERNEL BUILD fern/make build (ARCH=32/64, BOOT_MODE=...) │
│ └─> build/<arch>bit-<mode>-<type>/boot/fern.bin │
│ │
│ 4. USERSPACE userspace/make all │
│ └─> userspace/build/bin/{ls,cat,forest-shell,...} │
│ │
│ 5. INITRD tar -C fern/initrd -cf initrd.tar . │
│ └─> initrd.tar (userspace binaries + scripts) │
│ │
│ 6. BOOTLOADER fern/make iso (or img for UEFI) │
│ └─> foreboots/forebo.img, esp.img, forebo.iso │
│ │
│ 7. QEMU TEST fern/make run / run-uefi / run64 │
│ └─> Boots the image in QEMU │
└─────────────────────────────────────────────────────────────────┘
Quick Start
# One-liner for a working build:
cd forest/fern && make defconfig && make all
# Or use the interactive GUI:
cd forest && ./createos.sh
Build Dependencies Between Components
┌──────────────┐
│ toolchain │
└──────┬───────┘
│
┌──────────▼──────────┐
│ kernel (Fern) │
└──────────┬──────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────────▼──────┐ ┌─────▼──────┐ ┌──────▼─────┐
│ userspace │ │ initrd │ │ ForeB │
│ (40+ apps) │ │ (tarball) │ │ (bootloader)
└─────────┬──────┘ └─────┬──────┘ └──────┬─────┘
└────────────────┼────────────────┘
│
┌──────────▼──────────┐
│ bootable image │
│ iso / img / esp.img │
└─────────────────────┘
Key rules:
- Toolchain must exist before anything else compiles.
- Kernel must link before ForeB can embed it into a disk image.
- Userspace must compile before the initrd can be packed with real binaries.
- ForeB embeds the kernel at a fixed disk sector offset (
KERNEL_SEEK = 48). - Initrd can be built any time but must exist before the final bootable image.
The Make-Based Build System
Fragment Architecture
The main fern/Makefile is a thin orchestrator — it contains only compile/link pattern rules and top-level targets. All configuration, directory layout, toolchain detection, flags, source selection, feature gating, and cleanup logic lives in build/*.mk fragments.
Include order matters:
include build/config.mk # 1. Load configuration
include build/dirs.mk # 2. Directory layout
include build/toolchain.mk # 3. Toolchain detection
include build/flags.mk # 4. Compiler/linker flags
include build/kernel-sources.mk # 5. Source file aggregation
include build/features/memory.mk # 6-21. Feature gates (16 fragments)
include build/features/filesystems.mk
include build/features/graphics.mk
include build/features/opengl.mk
include build/features/networking.mk
include build/features/audio.mk
include build/features/usb.mk
include build/features/storage.mk
include build/features/input.mk
include build/features/ipc.mk
include build/features/timers.mk
include build/features/interrupts.mk
include build/features/security.mk
include build/features/hardware.mk
include build/features/scheduler.mk
include build/features/compat.mk
include build/iso.mk # 22. Initrd + bootable image plumbing
include build/foreb.mk # 23. ForeB bootloader integration
include build/qemu-run.mk # 24. QEMU launch targets
include build/clean.mk # 25. Cleanup targets
Key Targets
| Target | What it does |
|---|---|
make help |
Show available targets |
make defconfig |
Write sane defaults |
make menuconfig |
Interactive TUI config editor |
make configcheck |
Validate and print effective config |
make all |
Build kernel + create bootable image |
make build |
Build kernel binary only |
make iso / make img |
Create bootable ISO or disk image |
make run |
Build and launch in QEMU |
make debug |
Launch with GDB stub |
make clean / make clean-all |
Remove build artifacts |
Multi-arch shortcuts: make build32, make build64, make buildarm, make buildaarch64, make buildriscv64, make buildall.
Configuration System Integration
Forest OS uses a Kconfig-inspired flow:
.forestos_config ──(conf.sh --generate)──> build-config.mk ──(include)──> Make vars
Steps
- Edit choices:
./conf.sh --defconfig,--menuconfig,--oldconfig,--allnoconfig,--allyesconfig - Generate:
./conf.sh --generatereads.forestos_configand writesbuild-config.mk - Build:
makereadsbuild-config.mkautomatically
Core Choice Variables
| Variable | Valid values | Default |
|---|---|---|
ARCH |
32, 64, arm, aarch64, riscv64 |
32 |
BOOT_MODE |
bios, uefi |
bios |
BUILD_TYPE |
debug, release, optimize |
debug |
Plus ~170 boolean ENABLE_* flags and ~40 numeric tunables (heap sizes, buffer limits, etc.).
CLI Overrides
Override any variable on the command line — CLI values win over build-config.mk:
make ARCH=64 BOOT_MODE=uefi BUILD_TYPE=release
How config.mk Works
build/config.mk is the first include. It:
- Loads
build-config.mkvia-include(no error if missing) - Validates
ARCH,BOOT_MODE,BUILD_TYPEagainst valid lists - Forces every undefined
ENABLE_*tono(no undefined variables) - Provides
?=defaults for numeric tunables
You can run make without ever running conf.sh — it uses safe defaults.
Feature Gating and Conditional Compilation
Feature gating uses exclusion lists, not inclusion lists. The system controls what source files get compiled by appending to EXCLUDED_CSOURCES.
How It Works
build/kernel-sources.mkdefines a baseEXCLUDED_CSOURCES(always-excluded files).- Each
build/features/*.mkfragment appends toEXCLUDED_CSOURCESwhen a feature is disabled. build/kernel-sources.mkcomputesCSOURCESvia$(filter-out $(EXCLUDED_CSOURCES),$(wildcard src/*.c)).- Because
CSOURCESuses deferred expansion (=not:=), feature appends are picked up at rule-evaluation time.
Example
# In build/features/networking.mk:
ifeq ($(ENABLE_NETWORKING),no)
EXCLUDED_CSOURCES += $(SRCDIR)/net.c $(wildcard $(SRCDIR)/networking/*.c)
endif
Setting ENABLE_NETWORKING=no excludes net.c, virtio_net.c, and all src/networking/*.c from compilation.
The 16 Feature Fragments
| Fragment | Controls |
|---|---|
memory.mk |
Paging, slab, COW, swap, OOM, memory protection |
filesystems.mk |
VFS, ext2, FAT32, exFAT, ISO9660, tmpfs, procfs |
graphics.mk |
Framebuffer, VGA, GPU drivers, Wayland, X11, fonts |
opengl.mk |
Software OpenGL renderer |
networking.mk |
TCP/IP stack, protocols, NIC drivers |
audio.mk |
Sound system (PC speaker, SB16, AC97, HDA) |
usb.mk |
USB host controllers (EHCI, UHCI, OHCI, xHCI) |
storage.mk |
ATA, AHCI, NVMe, SCSI, block devices |
input.mk |
PS/2 keyboard/mouse, input events |
ipc.mk |
IPC channels, POSIX SHM, SysV IPC |
timers.mk |
PIT, HPET, TSC, timer abstraction |
interrupts.mk |
APIC, IOAPIC, PIC, interrupt management |
security.mk |
Authentication, ASLR, SMEP/SMAP |
hardware.mk |
PCI, ACPI, serial, TTY, char devices |
scheduler.mk |
Process scheduler, SMP |
compat.mk |
Linux compatibility layer |
Deferred Expansion Trick
Source list variables use recursive (=) expansion so feature fragment appends are visible when the variable is finally expanded. EXCLUDED_CSOURCES is := (simple) for its base list; feature fragments use += which appends at the point of the +=. The final CSOURCES value includes all feature exclusions.
Build Outputs and Artifacts
All build outputs are keyed by the config triple :
fern/
├── obj/32bit-bios-debug/ # Object files (.o)
├── build/32bit-bios-debug/ # Final binaries
│ ├── boot/fern.bin # BIOS kernel binary
│ └── boot/initrd.tar # Initial ramdisk
└── build/64bit-uefi-release/
├── fern.elf # ELF kernel (before PE conversion)
└── BOOTX64.EFI # UEFI PE32+ application
foreboots/build/
├── stage1.bin # MBR (512 bytes, exactly)
├── stage2.bin # GUI + menu (max 8 KiB)
├── stage3.bin # PM ELF loader (max 8 KiB)
├── forebo.img # Raw disk image
├── esp.img # FAT EFI System Partition (~48 MiB)
└── forebo.iso # Hybrid BIOS+UEFI ISO
BIOS Disk Layout
| Sectors | Content |
|---|---|
| 0 | Stage 1 (MBR, 512 bytes) |
| 1–16 | Stage 2 (8 KiB max) |
| 17–32 | Stage 3 (8 KiB max) |
| 48+ | Kernel binary |
| Computed | Initrd / multiboot module |
UEFI ESP Layout
esp.img (FAT16, ~48 MiB)
├── /EFI/BOOT/BOOTX64.EFI # ForeB's UEFI loader
├── /forebo/kernel.elf # Fern kernel
├── /forebo/forebo.cfg # Boot menu config
├── /forebo/bg.bmp # Theme background
├── /forebo/icons/*.tga # Menu icons
└── /forebo/initrd.tar # Multiboot module
The createos.sh Interactive Build Tool
createos.sh is a menu-driven build GUI that orchestrates the entire pipeline.
Usage
./createos.sh # Interactive TUI
./createos.sh --quick # Quick build with defaults
./createos.sh --arch 64 --boot uefi --type release
Interactive Menu
- Architecture — 32, 64, arm, aarch64, riscv64
- Boot Mode — BIOS or UEFI
- Build Type — debug or release
- Features — OpenGL, networking, audio, SMP, X11 (toggle on/off)
- Initrd Style — minimal, standard, full, custom
- Output Name — customize image filename
- Build / Reset / Quit
Pipeline Steps
When you hit "Build":
- Install prerequisites (auto-detects apt/pacman/dnf)
- Build cross-toolchain (if not already built)
- Build userspace apps (40+ cross-compiled binaries)
- Build initrd (packs userspace + scripts into
initrd.tar) - Configure kernel (writes
.forestos_config, runsconf.sh --generate) - Build kernel (compiles Fern)
- Build bootloader (ForeB + disk image/ISO)
- Package output (copies everything to
output/)
Skip flags: --skip-toolchain, --skip-userspace.
Build Troubleshooting
"Architecture toolchain not found" — Bridge the toolchain path:
ln -sf ../forestos-toolchain ~/forest/fern/forestos-toolchain
# Or: export FORESTOS_TOOLCHAIN_DIR=$HOME/forest/forestos-toolchain
"build-config.mk not found" — Generate it:
cd fern && ./conf.sh --defconfig && ./conf.sh --generate
"x86_64-forestos-gcc not found" (ARCH=64) — Build the 64-bit toolchain:
cd forestos-toolchain && ./build-toolchain.sh --arch 64
Stage 2 exceeds 8 KiB — BIOS stage2 must fit in 8192 bytes. Reduce features or debug the stage2 source.
"NASM not found" — ForeB BIOS stages need NASM: sudo apt install nasm
"clang/lld not found" — ForeB UEFI app uses clang (deliberate toolchain split): sudo apt install clang lld
Host binary in initrd — If binaries show /lib64/ld-linux, they were compiled with the host compiler. Rebuild: cd userspace && make clean && make
QEMU UEFI firmware not found — OVMF path varies by distro. Check /usr/share/edk2/ and update foreboots/Makefile or set OVMF_CODE/OVMF_VARS.
Diagnostic commands:
make configcheck # Show effective config
make show-config # Full config dump
make list-objects # Object files to build
make stats # Build statistics
make size # Binary size
make validate-toolchain # Verify toolchain
Clean Builds vs Incremental Builds
Incremental Builds (Default)
Only files whose dependencies changed are recompiled. Object files live in obj/ — changing ARCH, BOOT_MODE, or BUILD_TYPE creates a separate directory, so multiple configurations coexist.
Clean Build
make clean # Clean current config only
make clean-all # Clean ALL configurations
make clean-kernel # Remove only .o files (fast, keeps deps)
make all CLEAN_BEFORE_BUILD=yes # Force clean before every build
| Target | Removes |
|---|---|
clean |
obj/ and build/ for current config |
clean-all |
All obj/, all build/bit-, dist/, *.iso |
clean-kernel |
Only .o files in current obj dir |
ForeB: cd foreboots && make clean
Toolchain: cd forestos-toolchain && ./build-toolchain.sh --arch both --clean
Config-Driven Object Directories
obj/32bit-bios-debug/ # x86 32-bit, BIOS, debug symbols
obj/64bit-uefi-release/ # x86 64-bit, UEFI, optimized
obj/arm-bios-debug/ # ARM, BIOS, debug
No cross-contamination — build multiple configurations in parallel safely.
See also: MAKE_AN_OS.md for the end-to-end build guide.