// SPDX-License-Identifier: LCL-1.0 // Copyright (c) 2026 Markus Maiwald // Stewardship: Self Sovereign Society Foundation // // This file is part of the Nexus Commonwealth. // See legal/LICENSE_COMMONWEALTH.md for license terms. //! Rumpk Build System — Unified Pipeline //! //! Single command builds everything: Nim kernel + Zig HAL + LwIP + link. //! //! Usage: //! zig build # Build kernel (default: riscv64) //! zig build -Darch=aarch64 # ARM64 target //! zig build -Darch=x86_64 # AMD64 target //! zig build nim # Recompile Nim kernel sources only //! zig build test-lwf # Run LWF tests (native host) const std = @import("std"); const Arch = enum { riscv64, aarch64, x86_64 }; /// Per-architecture build configuration. const ArchConfig = struct { cpu_arch: std.Target.Cpu.Arch, code_model: std.builtin.CodeModel, switch_asm: []const u8, linker_script: []const u8, /// Arch name for build_nim.sh nim_arch: []const u8, }; fn archConfig(arch: Arch) ArchConfig { return switch (arch) { .riscv64 => .{ .cpu_arch = .riscv64, .code_model = .medany, .switch_asm = "hal/arch/riscv64/switch.S", .linker_script = "boot/linker.ld", .nim_arch = "riscv64", }, .aarch64 => .{ .cpu_arch = .aarch64, .code_model = .default, .switch_asm = "hal/arch/aarch64/switch.S", .linker_script = "boot/linker_aarch64.ld", .nim_arch = "aarch64", }, .x86_64 => .{ .cpu_arch = .x86_64, .code_model = .kernel, .switch_asm = "hal/arch/x86_64/switch.S", .linker_script = "boot/linker_x86_64.ld", .nim_arch = "x86_64", }, }; } /// Apply freestanding kernel settings to a module. fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void { mod.red_zone = false; mod.stack_check = false; mod.code_model = code_model; mod.sanitize_thread = false; mod.single_threaded = true; mod.strip = true; } pub fn build(b: *std.Build) void { // ========================================================= // Target Selection: -Darch=riscv64|aarch64|x86_64 // ========================================================= const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64; const config = archConfig(arch); var query: std.Target.Query = .{ .cpu_arch = config.cpu_arch, .os_tag = .freestanding, .abi = .none, }; // Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores if (config.cpu_arch == .aarch64) { query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon}); } const target = b.resolveTargetQuery(query); // HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked. const optimize: std.builtin.OptimizeMode = .ReleaseSmall; // ========================================================= // Step: Nim Kernel Compilation (nim -> C -> .o) // ========================================================= // Calls build_nim.sh which handles: // 1. nim c --compileOnly -> generates C // 2. zig cc -> cross-compiles C to .o // Incremental: only recompiles changed files. const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch }); const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)"); nim_build_step.dependOn(&nim_step.step); // ========================================================= // L0: Hardware Abstraction Layer (Zig) // ========================================================= const hal_mod = b.createModule(.{ .root_source_file = b.path("hal/abi.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(hal_mod, config.code_model); const hal = b.addLibrary(.{ .name = "rumpk_hal", .root_module = hal_mod, .linkage = .static, }); b.installArtifact(hal); // ========================================================= // Boot: Entry Point (Assembly + Zig) // ========================================================= const boot_mod = b.createModule(.{ .root_source_file = b.path("boot/header.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(boot_mod, config.code_model); const boot = b.addObject(.{ .name = "boot", .root_module = boot_mod, }); // ========================================================= // LwIP: Lightweight TCP/IP Stack // ========================================================= // Compiled as C objects with freestanding flags. // ubsan disabled per-file to avoid runtime dependency. const lwip_base = "libs/membrane/external/lwip/src/"; const lwip_srcs = [_][]const u8{ // Core lwip_base ++ "core/init.c", lwip_base ++ "core/def.c", lwip_base ++ "core/dns.c", lwip_base ++ "core/inet_chksum.c", lwip_base ++ "core/ip.c", lwip_base ++ "core/mem.c", lwip_base ++ "core/memp.c", lwip_base ++ "core/netif.c", lwip_base ++ "core/pbuf.c", lwip_base ++ "core/raw.c", lwip_base ++ "core/sys.c", lwip_base ++ "core/tcp.c", lwip_base ++ "core/tcp_in.c", lwip_base ++ "core/tcp_out.c", lwip_base ++ "core/timeouts.c", lwip_base ++ "core/udp.c", // IPv4 lwip_base ++ "core/ipv4/autoip.c", lwip_base ++ "core/ipv4/dhcp.c", lwip_base ++ "core/ipv4/etharp.c", lwip_base ++ "core/ipv4/icmp.c", lwip_base ++ "core/ipv4/ip4.c", lwip_base ++ "core/ipv4/ip4_addr.c", lwip_base ++ "core/ipv4/ip4_frag.c", // Netif lwip_base ++ "netif/ethernet.c", // SysArch (Rumpk integration) "libs/membrane/sys_arch.c", }; const lwip_flags: []const []const u8 = switch (arch) { .riscv64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DNO_SYS=1", "-mcmodel=medany", "-Icore", "-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include", }, .aarch64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DNO_SYS=1", "-Icore", "-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include", }, .x86_64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DNO_SYS=1", "-mcmodel=kernel", "-Icore", "-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include", }, }; const liblwip = b.addLibrary(.{ .name = "lwip", .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), .linkage = .static, }); liblwip.root_module.sanitize_thread = false; liblwip.root_module.red_zone = false; liblwip.root_module.single_threaded = true; for (lwip_srcs) |src| { liblwip.addCSourceFile(.{ .file = b.path(src), .flags = lwip_flags, }); } b.installArtifact(liblwip); // ========================================================= // LittleFS: Sovereign Filesystem (Persistent Storage) // ========================================================= // Compiled as C object with freestanding flags. // Uses LFS_CONFIG=lfs_rumpk.h to replace lfs_util.h entirely, // avoiding stdio/assert deps. malloc/free provided by clib.c. const lfs_flags: []const []const u8 = switch (arch) { .riscv64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=medany", }, .aarch64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", }, .x86_64 => &.{ "-Os", "-fno-sanitize=all", "-Wno-everything", "-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=kernel", }, }; const lfs_obj = b.addObject(.{ .name = "littlefs", .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); lfs_obj.root_module.sanitize_thread = false; lfs_obj.root_module.red_zone = false; lfs_obj.root_module.single_threaded = true; lfs_obj.addCSourceFile(.{ .file = b.path("vendor/littlefs/lfs.c"), .flags = lfs_flags, }); // ========================================================= // Dependencies (CLib, LibC Shim, LWF Adapter, Switch) // ========================================================= // 1. CLib (Minimal C stdlib) const clib = b.addObject(.{ .name = "clib", .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); clib.addCSourceFile(.{ .file = b.path("libs/membrane/clib.c"), .flags = switch (arch) { .riscv64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=medany" }, .aarch64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1" }, .x86_64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=kernel" }, }, }); // 2. LibC Shim (Zig) const libc_shim_mod = b.createModule(.{ .root_source_file = b.path("libs/membrane/libc_shim.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(libc_shim_mod, config.code_model); const libc_shim = b.addObject(.{ .name = "libc_shim", .root_module = libc_shim_mod, }); // 3. LittleFS HAL (VirtIO-Block ↔ LittleFS glue) const lfs_hal_mod = b.createModule(.{ .root_source_file = b.path("hal/littlefs_hal.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(lfs_hal_mod, config.code_model); const lfs_hal = b.addObject(.{ .name = "lfs_hal", .root_module = lfs_hal_mod, }); // 4. LWF Adapter (Project LibWeb — Libertaria Wire Frame) const lwf_adapter_mod = b.createModule(.{ .root_source_file = b.path("libs/libertaria/lwf_adapter.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(lwf_adapter_mod, config.code_model); const lwf_adapter = b.addObject(.{ .name = "lwf_adapter", .root_module = lwf_adapter_mod, }); // 4. Context Switch (Architecture-specific Assembly) const switch_obj = b.addObject(.{ .name = "switch", .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); switch_obj.addAssemblyFile(b.path(config.switch_asm)); // ========================================================= // Final Link: rumpk.elf // ========================================================= const kernel_mod = b.createModule(.{ .root_source_file = b.path("hal/abi.zig"), .target = target, .optimize = optimize, }); applyKernelSettings(kernel_mod, config.code_model); const kernel = b.addExecutable(.{ .name = "rumpk.elf", .root_module = kernel_mod, }); kernel.setLinkerScript(b.path(config.linker_script)); kernel.addObject(boot); kernel.addObject(clib); kernel.addObject(libc_shim); kernel.addObject(lfs_hal); kernel.addObject(lwf_adapter); kernel.addObject(switch_obj); // LwIP linked into kernel — Membrane/NetSwitch drives DHCP/TCP/ICMP. kernel.linkLibrary(liblwip); // LittleFS IS linked into kernel — provides /nexus persistent storage. kernel.addObject(lfs_obj); // Nim-generated objects from build/nimcache/ { var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| { std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err}); return; }; defer nimcache_dir.close(); var it = nimcache_dir.iterate(); while (it.next() catch null) |entry| { if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".o")) { const path = b.fmt("build/nimcache/{s}", .{entry.name}); kernel.addObjectFile(b.path(path)); } } } // Embedded InitRD — assemble for the target architecture const initrd_obj = b.addObject(.{ .name = "initrd", .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S")); kernel.addObject(initrd_obj); b.installArtifact(kernel); // Make default install depend on Nim compilation kernel.step.dependOn(&nim_step.step); // ========================================================= // Tests (always run on native host) // ========================================================= // HAL unit tests const hal_test_mod = b.createModule(.{ .root_source_file = b.path("hal/abi.zig"), .target = b.graph.host, }); const hal_tests = b.addTest(.{ .root_module = hal_test_mod, }); const run_hal_tests = b.addRunArtifact(hal_tests); const test_step = b.step("test", "Run Rumpk HAL tests (native host)"); test_step.dependOn(&run_hal_tests.step); // LWF Adapter + Membrane tests (Project LibWeb) const lwf_test_mod = b.createModule(.{ .root_source_file = b.path("libs/libertaria/lwf_adapter.zig"), .target = b.graph.host, }); const lwf_tests = b.addTest(.{ .root_module = lwf_test_mod, }); const run_lwf_tests = b.addRunArtifact(lwf_tests); const lwf_test_step = b.step("test-lwf", "Run LWF Adapter tests (Project LibWeb)"); lwf_test_step.dependOn(&run_lwf_tests.step); const lwf_membrane_mod = b.createModule(.{ .root_source_file = b.path("libs/libertaria/lwf_membrane.zig"), .target = b.graph.host, }); const lwf_membrane_tests = b.addTest(.{ .root_module = lwf_membrane_mod, }); const run_lwf_membrane_tests = b.addRunArtifact(lwf_membrane_tests); lwf_test_step.dependOn(&run_lwf_membrane_tests.step); }