#define NOB_IMPLEMENTATION #include "nob.h" #include #define BUILD_FOLDER "build/" #define SRC_FOLDER "src/" bool render_clangd_config(void) { Nob_String_Builder tmpl = {0}; if (!nob_read_entire_file(".clangd.template", &tmpl)) return false; nob_sb_append_null(&tmpl); const char *include_path = getenv("I686_ELF_GCC_INCLUDE"); if (include_path == NULL) { nob_log(NOB_ERROR, "I686_ELF_GCC_INCLUDE not set"); nob_sb_free(tmpl); return false; } const char *needle = "${env:I686_ELF_GCC_INCLUDE}"; Nob_String_Builder out = {0}; const char *cursor = tmpl.items; const char *found; while ((found = strstr(cursor, needle)) != NULL) { nob_sb_append_buf(&out, cursor, found - cursor); nob_sb_append_cstr(&out, include_path); cursor = found + strlen(needle); } nob_sb_append_cstr(&out, cursor); bool ok = nob_write_entire_file(".clangd", out.items, out.count); nob_sb_free(tmpl); nob_sb_free(out); return ok; } bool build_assembly(Nob_Cmd *cmd) { const char *output = BUILD_FOLDER "boot.o"; const char *input = SRC_FOLDER "boot.s"; int rebuild = nob_needs_rebuild1(output, input); if (!rebuild) return true; nob_cmd_append(cmd, "i686-elf-as", "-o", BUILD_FOLDER "boot.o", SRC_FOLDER "boot.s"); if (!nob_cmd_run(cmd)) return false; return true; } bool build_c(Nob_Cmd *cmd) { const char *output = BUILD_FOLDER "kernel.o"; const char *input = SRC_FOLDER "kernel.c"; int rebuild = nob_needs_rebuild1(output, input); if (!rebuild) return true; nob_cmd_append(cmd, "i686-elf-gcc", "-std=gnu99", "-ffreestanding", "-Wall", "-Wextra", "-O2", "-o", BUILD_FOLDER "kernel.o", "-c", SRC_FOLDER "kernel.c"); if (!nob_cmd_run(cmd)) return false; return true; } bool link_everything(Nob_Cmd *cmd) { const char *output = BUILD_FOLDER "kernel"; const char *input[] = {SRC_FOLDER "linker.ld", BUILD_FOLDER "kernel.o", BUILD_FOLDER "boot.o"}; int rebuild = nob_needs_rebuild(output, input, NOB_ARRAY_LEN(input)); if (!rebuild) return true; Nob_File_Paths objectFiles = {0}; nob_da_append(&objectFiles, BUILD_FOLDER "kernel.o"); nob_da_append(&objectFiles, BUILD_FOLDER "boot.o"); nob_cmd_append(cmd, "i686-elf-gcc", "-T", SRC_FOLDER "linker.ld", "-o", BUILD_FOLDER "kernel", "-ffreestanding", "-O2", "-nostdlib"); // Add objectFiles as input for (size_t i = 0; i < objectFiles.count; ++i) { nob_cmd_append(cmd, objectFiles.items[i]); } nob_cmd_append(cmd, "-lgcc"); if (!nob_cmd_run(cmd)) return false; return true; } bool check_kernel(Nob_Cmd *cmd) { nob_cmd_append(cmd, "grub-file", "--is-x86-multiboot", BUILD_FOLDER "kernel"); if (!nob_cmd_run(cmd)) return false; return true; } bool clean_dir(const char *dir_path) { Nob_File_Paths children = {0}; if (!nob_read_entire_dir(dir_path, &children)) return false; bool ok = true; for (size_t i = 0; i < children.count; ++i) { const char *name = children.items[i]; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; const char *full_path = nob_temp_sprintf("%s/%s", dir_path, name); if (!nob_delete_file(full_path)) { clean_dir(full_path); nob_delete_file(full_path); } } nob_da_free(children); return ok; } bool build_bootable_image(Nob_Cmd *cmd) { const char *output = BUILD_FOLDER "kernel.iso"; const char *input[] = {BUILD_FOLDER "kernel", SRC_FOLDER "grub.cfg"}; int rebuild = nob_needs_rebuild(output, input, NOB_ARRAY_LEN(input)); if (!rebuild) return true; if (!nob_mkdir_if_not_exists(BUILD_FOLDER "isodir")) return false; if (!nob_mkdir_if_not_exists(BUILD_FOLDER "isodir/boot")) return false; if (!nob_mkdir_if_not_exists(BUILD_FOLDER "isodir/boot/grub")) return false; nob_cmd_append(cmd, "cp", BUILD_FOLDER "kernel", BUILD_FOLDER "isodir/boot/kernel"); if (!nob_cmd_run(cmd)) return false; nob_cmd_append(cmd, "cp", SRC_FOLDER "grub.cfg", BUILD_FOLDER "isodir/boot/grub/grub.cfg"); if (!nob_cmd_run(cmd)) return false; nob_cmd_append(cmd, "grub-mkrescue", "-o", BUILD_FOLDER "kernel.iso", BUILD_FOLDER "isodir"); if (!nob_cmd_run(cmd)) return false; return true; } bool runQemu(Nob_Cmd *cmd) { nob_cmd_append(cmd, "qemu-system-i386", "-cdrom", BUILD_FOLDER "kernel.iso"); if (!nob_cmd_run(cmd)) return false; return true; } int main(int argc, char **argv) { NOB_GO_REBUILD_URSELF(argc, argv); const char *program_name = shift(argv, argc); bool clean = false; bool run = false; // starts qemu bool build = false; while (argc > 0) { const char *arg = shift(argv, argc); if (strcmp(arg, "clean") == 0) clean = true; if (strcmp(arg, "run") == 0) run = true; if (strcmp(arg, "build") == 0) build = true; } if (clean) { const char *dir_path = BUILD_FOLDER; Nob_Dir_Entry dir = {0}; if (!nob_dir_entry_open(dir_path, &dir)) { nob_log(NOB_INFO, "No build directory to clean. Skipping..."); } else { if (!clean_dir(BUILD_FOLDER)) return 1; nob_delete_file(BUILD_FOLDER); } } if (build) { if (!nob_mkdir_if_not_exists(BUILD_FOLDER)) return 1; Nob_Cmd cmd = {0}; if (!render_clangd_config()) return 1; if (!build_assembly(&cmd)) return 1; if (!build_c(&cmd)) return 1; if (!link_everything(&cmd)) return 1; if (!check_kernel(&cmd)) return 1; if (!build_bootable_image(&cmd)) return 1; } if (run) { Nob_Cmd cmd = {0}; if (!runQemu(&cmd)) return 1; } if (!clean && !build && !run) { nob_log(NOB_INFO, "Usage: %s (build || clean || run)", program_name); return 1; } return 0; }