Build and run

Build darwinOS from source

Check out the monorepo, install the host dependencies, and produce a bootable kernel and root filesystem.

This guide walks through building darwinOS end-to-end on a fresh Linux or macOS host. When you’re done you’ll have a bootable kernel image and a minimal root filesystem under build/. A companion guide covers running the output in QEMU.

The build is driven by a top-level Makefile that orchestrates the per-component builds (XNU, launchd, dyld, libSystem, userland). You don’t need to understand the internals to follow this guide.

Host requirements

You need a modern Unix-like host. Both Linux and macOS are supported; Windows works through WSL2 with the Linux instructions.

Tooling:

  • clang 17 or newer
  • make, ninja, and cmake
  • git 2.40+
  • python3 3.10+
  • A working C++ toolchain (libstdc++ or libc++)

Disk space: about 8 GB for a full build, plus 4 GB for the source checkout.

RAM: 8 GB minimum; 16 GB recommended if you want to build in parallel.

On Debian/Ubuntu

sudo apt install -y \
  build-essential clang lld ninja-build cmake \
  git python3 python3-pip \
  qemu-system-aarch64 qemu-system-x86

On Fedora

sudo dnf install -y \
  clang lld ninja-build cmake make \
  git python3 python3-pip \
  qemu-system-aarch64 qemu-system-x86

On macOS

Install the Xcode command-line tools:

xcode-select --install

Then install the rest via Homebrew:

brew install cmake ninja qemu

Check out the monorepo

git clone https://github.com/darwinos-io/darwinos.git
cd darwinos
git submodule update --init --recursive

The checkout bundles every component darwinOS builds — XNU, launchd, dyld, libSystem, userland — plus the build system under glue/. Submodules track upstream Apple Open Source drops where relevant.

Configure your build

Pick a target architecture and build profile:

./configure --arch=arm64 --profile=debug

Supported architectures: arm64, x86_64. Supported profiles: debug (default), release, release-with-debug.

Configure writes build/config.mk. Re-run with different flags to switch targets; the output directory is keyed on the combination so multiple configs can coexist.

Build

make -j$(nproc) world

On a modern 8-core machine a clean debug build takes 40–60 minutes. Incremental rebuilds after small changes finish in seconds.

make world builds every component in dependency order. If you only need the kernel, make xnu is faster; make userland builds userland without the kernel.

What gets built

When the build finishes, you’ll have:

  • build/arm64-debug/kernel.img — the XNU kernel image
  • build/arm64-debug/initrd.img — a minimal initial ramdisk
  • build/arm64-debug/rootfs.img — the root filesystem
  • build/arm64-debug/boot.efi — a UEFI boot stub (used by QEMU and real hardware)

Run the tests

Before you push a change, run the host-side test suite:

make test

This runs unit tests for the components that have them — today that’s launchd, dyld, and the build system itself. Integration tests that require a booted VM are covered by make vm-test (which internally uses the image you just built).

Next steps

If the build fails, grab the tail of build/<target>/build.log and open a discussion on GitHub. Include your ./configure arguments and the host OS.