//! Copyright (c) 2025 Freya Murphy <freya@freyacat.org> const std = @import("std"); const builtin = @import("builtin"); const c_flags = &[_][]const u8{ // lang "-std=c99", // warnings "-Wall", "-Wextra", "-pedantic", // flags "-fno-pie", "-fno-stack-protector", "-fno-omit-frame-pointer", "-ffreestanding", "-fno-builtin", // symbols "-ggdb", }; const ld_flags = &[_][]const u8{ "-nmagic", "-nostdlib", "--no-warn-rwx-segments", }; const boot_src = &[_][]const u8{"boot/boot.S"}; const kernel_src = &[_][]const u8{ "kernel/cio.c", "kernel/clock.c", "kernel/isrs.S", "kernel/kernel.c", "kernel/kmem.c", "kernel/list.c", "kernel/procs.c", "kernel/sio.c", "kernel/startup.S", "kernel/support.c", "kernel/syscalls.c", "kernel/user.c", "kernel/vm.c", "kernel/vmtables.c", "lib/klibc.c", }; const lib_src = &[_][]const u8{ "lib/blkmov.c", "lib/bound.c", "lib/cvtdec0.c", "lib/cvtdec.c", "lib/cvthex.c", "lib/cvtoct.c", "lib/cvtuns0.c", "lib/cvtuns.c", "lib/memclr.c", "lib/memcpy.c", "lib/memmove.c", "lib/memset.c", "lib/pad.c", "lib/padstr.c", "lib/sprint.c", "lib/str2int.c", "lib/strcat.c", "lib/strcmp.c", "lib/strcpy.c", "lib/strlen.c", }; const ulib_src = &[_][]const u8{ "lib/entry.S", "lib/ulibc.c", "lib/ulibs.S", }; const Prog = struct { name: []const u8, source: []const []const u8, }; const util_progs = &[_]Prog{ // mkblob Prog{ .name = "mkblob", .source = &[_][]const u8{"util/mkblob.c"}, }, // listblob Prog{ .name = "listblob", .source = &[_][]const u8{"util/listblob.c"}, }, // BuildImage Prog{ .name = "BuildImage", .source = &[_][]const u8{"util/BuildImage.c"}, }, }; const user_progs = &[_]Prog{ // idle Prog{ .name = "idle", .source = &[_][]const u8{"user/idle.c"}, }, // init Prog{ .name = "init", .source = &[_][]const u8{"user/init.c"}, }, // progABC Prog{ .name = "progABC", .source = &[_][]const u8{"user/progABC.c"}, }, // progDE Prog{ .name = "progDE", .source = &[_][]const u8{"user/progDE.c"}, }, // progFG Prog{ .name = "progFG", .source = &[_][]const u8{"user/progFG.c"}, }, // progH Prog{ .name = "progH", .source = &[_][]const u8{"user/progH.c"}, }, // progI Prog{ .name = "progI", .source = &[_][]const u8{"user/progI.c"}, }, // progJ Prog{ .name = "progJ", .source = &[_][]const u8{"user/progJ.c"}, }, // progKL Prog{ .name = "progKL", .source = &[_][]const u8{"user/progKL.c"}, }, // progKL Prog{ .name = "progKL", .source = &[_][]const u8{"user/progKL.c"}, }, // progMN Prog{ .name = "progMN", .source = &[_][]const u8{"user/progMN.c"}, }, // progP Prog{ .name = "progP", .source = &[_][]const u8{"user/progP.c"}, }, // progQ Prog{ .name = "progQ", .source = &[_][]const u8{"user/progQ.c"}, }, // progR Prog{ .name = "progR", .source = &[_][]const u8{"user/progR.c"}, }, // progS Prog{ .name = "progS", .source = &[_][]const u8{"user/progS.c"}, }, // progTUV Prog{ .name = "progTUV", .source = &[_][]const u8{"user/progTUV.c"}, }, // progW Prog{ .name = "progW", .source = &[_][]const u8{"user/progW.c"}, }, // progX Prog{ .name = "progX", .source = &[_][]const u8{"user/progX.c"}, }, // progY Prog{ .name = "progY", .source = &[_][]const u8{"user/progY.c"}, }, // progZ Prog{ .name = "progZ", .source = &[_][]const u8{"user/progZ.c"}, }, // shell Prog{ .name = "shell", .source = &[_][]const u8{"user/shell.c"}, }, }; const AddSourcesOpts = struct { exe: *std.Build.Step.Compile, sources: []const []const []const u8, c_flags: []const []const u8 }; fn add_sources(b: *std.Build, opts: AddSourcesOpts) void { // add asm and c source files for (opts.sources) |source| { for (source) |file| { if (std.mem.endsWith(u8, file, ".c")) { // c file opts.exe.addCSourceFile(.{ .file = b.path(file), .flags = opts.c_flags }); } else { // assembly file opts.exe.addAssemblyFile(b.path(file)); } } } } const BuildKernBinaryOpts = struct { name: []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, sources: []const []const []const u8, linker: ?[]const u8 = null, entry: []const u8 = "_start", strip: bool = false, }; fn build_kern_binary(b: *std.Build, opts: BuildKernBinaryOpts) void { // create compile step const exe = b.addExecutable(.{ .name = opts.name, .target = opts.target, .optimize = opts.optimize, .strip = opts.strip, }); // add include path exe.addIncludePath(b.path("include/")); exe.entry = .{ .symbol_name = opts.entry }; // add asm and c source files add_sources(b, .{ .exe = exe, .sources = opts.sources, .c_flags = c_flags, }); if (opts.linker != null) { exe.setLinkerScript(b.path(opts.linker.?)); } const step = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "../bin" }, } }); b.getInstallStep().dependOn(&step.step); } const BuildNativeBinaryOpts = struct { name: []const u8, optimize: std.builtin.OptimizeMode, sources: []const []const []const u8, }; fn build_native_binary(b: *std.Build, opts: BuildNativeBinaryOpts) void { // create compile step const exe = b.addExecutable(.{ .name = opts.name, .target = b.graph.host, .optimize = opts.optimize, .strip = true, }); // include libc exe.linkLibC(); // add asm and c source files add_sources(b, .{ .exe = exe, .sources = opts.sources, .c_flags = &.{}, }); const step = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "../bin" }, } }); b.getInstallStep().dependOn(&step.step); } pub fn build(b: *std.Build) !void { // context const target = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = std.Target.Cpu.Arch.x86, .os_tag = std.Target.Os.Tag.freestanding, .abi = std.Target.Abi.gnu, .ofmt = std.Target.ObjectFormat.elf, }, }); const optimize = std.builtin.OptimizeMode.ReleaseFast; // boot build_kern_binary(b, .{ .name = "boot", .target = target, .optimize = optimize, .sources = &.{ boot_src, }, .linker = "boot/boot.ld", .entry = "bootentry", }); // kernel build_kern_binary(b, .{ .name = "kernel", .target = target, .optimize = optimize, .sources = &.{ kernel_src, lib_src, }, .linker = "kernel/kernel.ld", }); // user_progs for (user_progs) |prog| { build_kern_binary(b, .{ .name = prog.name, .target = target, .optimize = optimize, .sources = &.{ prog.source, lib_src, ulib_src, }, .linker = "user/user.ld", .strip = true, }); } // util_progs for (util_progs) |prog| { build_native_binary(b, .{ .name = prog.name, .optimize = optimize, .sources = &.{ prog.source, }, }); } }