const std = @import("std"); const LazyPath = std.Build.LazyPath; // Although this function looks imperative, note that its job is to // declaratively construct a build graph that will be executed by an external // runner. pub fn build(b: *std.Build) void { const bootloader = addNasmFiles(b, AddNasmFilesOptions{ .filename = "src/bootloader.asm", .outputname = "bootloader.bin", .flags = &.{"-f bin"}, }); const kernel = addNasmFiles(b, AddNasmFilesOptions{ .filename = "src/kernel.asm", .outputname = "kernel.bin", .flags = &.{"-f bin"}, }); const target = b.standardTargetOptions(.{ .default_target = .{ .os_tag = .uefi, .cpu_arch = .x86, }, }); const floppy = createFloppy(b, "main_floppy.img"); var installToFloppy = installImgToFloppy(b, floppy, bootloader, &.{ NamedFile{ .file = kernel, .name = "::kernel.bin" }, NamedFile{ .file = b.path("src/reference_implementations/test.txt"), .name = "::test.txt" }, }); const img_install = b.addInstallBinFile(floppy, "main_floppy.img"); img_install.step.dependOn(&installToFloppy.step); b.getInstallStep().dependOn(&img_install.step); const fat_reference_mod = b.createModule(.{ .root_source_file = b.path("src/reference_implementations/fat.zig"), .target = target, .link_libc = true, }); const run = b.addSystemCommand(&.{ "qemu-system-i386", "-fda", "zig-out/bin/main_floppy.img" }); const run_step = b.step("run", "run the os in qemu"); run_step.dependOn(&run.step); const fat_reference = b.addExecutable(.{ .root_module = fat_reference_mod, .name = "fat_reference_implementation", .use_lld = false, .use_llvm = false, }); const fat_install = b.addInstallArtifact(fat_reference, .{}); const build_references = b.step("references", "build the reference implementations"); build_references.dependOn(&fat_install.step); } const AddNasmFilesOptions = struct { filename: []const u8, outputname: ?[]const u8 = null, flags: []const []const u8 = &.{}, }; const NamedFile = struct { file: std.Build.LazyPath, name: []const u8, }; fn createFloppy(b: *std.Build, name: []const u8) LazyPath { const dd = b.addSystemCommand(&.{ "dd", "if=/dev/zero" }); const Floppy = dd.addPrefixedOutputFileArg("of=", name); dd.has_side_effects = true; dd.addArgs(&.{ "bs=512", "count=2880" }); return Floppy; } fn installImgToFloppy(b: *std.Build, floppy: LazyPath, bootloader: LazyPath, additional_files: []const NamedFile) *std.Build.Step.Run { const mkfs = b.addSystemCommand(&.{ "mkfs.fat", "-F12", "-nNBOS" }); mkfs.addFileArg(floppy); mkfs.has_side_effects = true; const dd = b.addSystemCommand(&.{"dd"}); dd.addPrefixedFileArg("if=", bootloader); dd.addPrefixedFileArg("of=", floppy); dd.addArg("conv=notrunc"); dd.step.dependOn(&mkfs.step); var last_step = dd; for (additional_files) |f| { const mcopy = b.addSystemCommand(&.{"mcopy"}); mcopy.addPrefixedFileArg("-i", floppy); mcopy.addFileArg(f.file); mcopy.addArg(f.name); mcopy.step.dependOn(&last_step.step); last_step = mcopy; } return last_step; } // adapted from https://codeberg.org/raddari/zig-nasm-lib.git fn addNasmFiles(b: *std.Build, options: AddNasmFilesOptions) LazyPath { std.debug.assert(!std.fs.path.isAbsolute(options.filename)); const src_file = b.path(options.filename); const output = options.outputname orelse b.fmt("{s}.o", .{std.mem.sliceTo(options.filename, '.')}); const nasm = b.addSystemCommand(&.{"nasm"}); nasm.addArgs(options.flags); nasm.addPrefixedDirectoryArg("-i", b.path("src")); const obj = nasm.addPrefixedOutputFileArg("-o", output); nasm.addFileArg(src_file); nasm.has_side_effects = true; return obj; }