Files
2026-10-27 09:28:31 +01:00

738 lines
45 KiB
C

// Project build orchestrator. This file describes the Windows/MSVC build graph and keeps driver dependencies explicit.
#define NOB_IMPLEMENTATION
#include "nob.h"
// Compiler and linker flags stay in macros so they can be used in file-scope aggregate initializers.
// MSVC C rejects initializing a static object from another object, even when the source object is const.
// Drivers and user binaries do not inherit kernel include paths so private dependencies fail at compile time.
#define COMMON_CFLAGS_BASE_LITERAL "/nologo /WX /O2 /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi- /Z7 /std:c17 /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /Icode\\public /Icode\\common\\include"
#define COMMON_CFLAGS_LITERAL COMMON_CFLAGS_BASE_LITERAL " /W3"
#define KERNEL_CFLAGS_LITERAL COMMON_CFLAGS_LITERAL " /Icode\\kernel\\include"
#define DRIVER_CFLAGS_LITERAL COMMON_CFLAGS_LITERAL
#define USER_CFLAGS_LITERAL COMMON_CFLAGS_LITERAL
// POSIX targets own their standard C/POSIX headers; /X prevents the host UCRT from entering the target ABI.
#define POSIX_USER_CFLAGS_LITERAL "/X /Icode\\public\\posix /Icode\\public\\freestanding " USER_CFLAGS_LITERAL
#define BOOT_CFLAGS_LITERAL KERNEL_CFLAGS_LITERAL
#define USER_HEAP_OBJS_LITERAL "obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj"
#define NATIVE_RUNTIME_OBJS_LITERAL "obj\\runtime\\native_exception.obj obj\\runtime\\native_start.obj"
#define NATIVE_EXE_LIBS_LITERAL "bin\\syscall.lib obj\\libc.obj " USER_HEAP_OBJS_LITERAL " " NATIVE_RUNTIME_OBJS_LITERAL
#define NATIVE_INIT_LIBS_LITERAL "bin\\syscall_s.lib obj\\libc.obj obj\\runtime\\native_exception.obj"
#define NATIVE_SERVICE_LIBS_LITERAL "bin\\syscall_s.lib obj\\libc.obj " USER_HEAP_OBJS_LITERAL " obj\\runtime\\native_exception.obj"
#define WIN32_EXE_LIBS_LITERAL "bin\\win32.lib bin\\syscall.lib obj\\libc.obj obj\\runtime\\native_start.obj"
#define POSIX_EXE_LIBS_LITERAL "bin\\posix.lib bin\\syscall.lib obj\\libc.obj obj\\runtime\\posix_start.obj"
#define NATIVE_EXE_ENTRY_LITERAL "/ENTRY:NtlNativeStart"
#define USER_IMAGE_FLAGS_LITERAL "/DYNAMICBASE /HIGHENTROPYVA"
#define ACPICA_CFLAGS_LITERAL COMMON_CFLAGS_BASE_LITERAL " /W0 /DACPI_NTLITE /Icode\\drivers\\platform\\acpi\\third_party\\acpica\\include /wd4100 /wd4101 /wd4127 /wd4214 /wd4244 /wd4267 /wd4706 /wd4702"
#define LWIP_CFLAGS_LITERAL DRIVER_CFLAGS_LITERAL " /Icode\\drivers\\net\\lwip\\include /Icode\\drivers\\net\\lwip\\third_party\\lwip\\src\\include /wd4005 /wd4100 /wd4103 /wd4127 /wd4244 /wd4267 /wd4706"
#define EXT4_CFLAGS_LITERAL DRIVER_CFLAGS_LITERAL " /Icode\\drivers\\fs\\ext4\\include /Icode\\drivers\\fs\\ext4\\third_party\\lwext4\\include /DCONFIG_USE_DEFAULT_CFG=1 /DCONFIG_HAVE_OWN_ERRNO=1 /DCONFIG_DEBUG_PRINTF=0 /DCONFIG_DEBUG_ASSERT=0 /DCONFIG_BLOCK_DEV_ENABLE_STATS=0 /DCONFIG_EXT4_BLOCKDEVS_COUNT=8 /DCONFIG_EXT4_MOUNTPOINTS_COUNT=8 /wd4005 /wd4100 /wd4101 /wd4103 /wd4127 /wd4244 /wd4267 /wd4706 /wd4996"
#define NTFS3G_CFLAGS_LITERAL DRIVER_CFLAGS_LITERAL " /Icode\\drivers\\fs\\ntfs\\ntfs3g\\include /Icode\\drivers\\fs\\ntfs\\ntfs3g\\third_party\\ntfs-3g\\include /Icode\\drivers\\fs\\ntfs\\ntfs3g\\third_party\\ntfs-3g\\include\\ntfs-3g /DHAVE_CONFIG_H=1 /DNO_NTFS_DEVICE_DEFAULT_IO_OPS=1 /wd4005 /wd4018 /wd4100 /wd4101 /wd4103 /wd4127 /wd4133 /wd4146 /wd4244 /wd4267 /wd4305 /wd4311 /wd4312 /wd4706 /wd4996"
#define LFLAGS_LITERAL "/nologo /NODEFAULTLIB /DEBUG"
// Partition offsets inside disk.vhd
#define VHD_PART1 "disk.vhd@@1M" // ESP / Partition 1 (Offset 1MB)
#define VHD_PART2 "disk.vhd@@65M" // Test/Data / Partition 2 (Offset 65MB)
#define DEFAULT_VHD_DISK_SIGNATURE 0x4E544C31u
const char* CommonCflags = COMMON_CFLAGS_LITERAL;
const char* KernelCflags = KERNEL_CFLAGS_LITERAL;
const char* UserCflags = USER_CFLAGS_LITERAL;
const char* PosixUserCflags = POSIX_USER_CFLAGS_LITERAL;
const char* BootCflags = BOOT_CFLAGS_LITERAL;
const char* LwipCflags = LWIP_CFLAGS_LITERAL;
const char* Ext4Cflags = EXT4_CFLAGS_LITERAL;
const char* Ntfs3gCflags = NTFS3G_CFLAGS_LITERAL;
const char* LFLAGS = LFLAGS_LITERAL;
typedef struct Driver_Target {
const char* name;
const char* obj_dir;
const char* sources;
const char* cflags;
const char* libs;
const char* entry_flag;
} Driver_Target;
#define DRIVER_TARGET(name_, obj_dir_, sources_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, "", "/NOENTRY" }
#define DRIVER_TARGET_LIBS(name_, obj_dir_, sources_, libs_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, (libs_), "/NOENTRY" }
#define DRIVER_TARGET_FLAGS(name_, obj_dir_, sources_, cflags_) { (name_), (obj_dir_), (sources_), (cflags_), "", "/NOENTRY" }
#define DRIVER_TARGET_DECLARATIVE(name_, obj_dir_, sources_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, "", "/NOENTRY" }
static const Driver_Target PCI_BUS_DRIVER =
DRIVER_TARGET_FLAGS("pci", "obj\\drivers\\pci", "code\\drivers\\bus\\pci\\pci.c", DRIVER_CFLAGS_LITERAL " /DPCI_SYS_BUILD");
static const Driver_Target STANDALONE_DRIVERS[] = {
DRIVER_TARGET("ps2", "obj\\drivers\\ps2", "code\\drivers\\input\\ps2\\ps2.c code\\drivers\\input\\ps2\\ps2_i8042.c"),
DRIVER_TARGET_DECLARATIVE("serial", "obj\\drivers\\serial", "code\\drivers\\input\\serial\\serial.c"),
DRIVER_TARGET_LIBS("hda", "obj\\drivers\\hda", "code\\drivers\\audio\\hda\\hda.c code\\drivers\\audio\\hda\\hda_transport.c code\\drivers\\audio\\hda\\hda_codec.c", "bin\\pci.lib"),
DRIVER_TARGET_LIBS("ata", "obj\\drivers\\ata", "code\\drivers\\storage\\ata\\ata.c", "bin\\pci.lib"),
DRIVER_TARGET_LIBS("ahci", "obj\\drivers\\ahci", "code\\drivers\\storage\\ahci\\ahci.c code\\drivers\\storage\\ahci\\ahci_port.c", "bin\\pci.lib"),
DRIVER_TARGET("fat", "obj\\drivers\\fat", "code\\drivers\\fs\\fat\\fat.c code\\drivers\\fs\\fat\\fat_directory.c code\\drivers\\fs\\fat\\fat_file.c code\\drivers\\fs\\fat\\fat_async.c code\\drivers\\fs\\fat\\fat_driver.c code\\drivers\\fs\\fat\\fat_mount.c"),
DRIVER_TARGET_LIBS("vmwaresvga", "obj\\drivers\\vmwaresvga", "code\\drivers\\display\\vmwaresvga\\vmwaresvga.c", "bin\\pci.lib"),
DRIVER_TARGET_LIBS("usb", "obj\\drivers\\usb", "code\\drivers\\bus\\usb\\usb.c code\\drivers\\bus\\usb\\usb_bus.c code\\drivers\\bus\\usb\\usb_composite.c code\\drivers\\bus\\usb\\usb_core.c code\\drivers\\bus\\usb\\usb_xhci.c code\\drivers\\bus\\usb\\usb_hid.c code\\drivers\\bus\\usb\\usb_storage.c code\\drivers\\bus\\usb\\usb_hub.c", "bin\\pci.lib"),
DRIVER_TARGET_LIBS("rtl816x", "obj\\drivers\\rtl816x", "code\\drivers\\net\\rtl816x\\rtl816x.c", "bin\\pci.lib"),
DRIVER_TARGET_LIBS("rtl8139", "obj\\drivers\\rtl8139", "code\\drivers\\net\\rtl8139\\rtl8139.c", "bin\\pci.lib"),
};
// Creates layout.
void create_layout() {
const char* dirs[] = {
"obj", "bin", "obj\\boot", "obj\\ddk", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\ntfs3g", "obj\\acpica", "obj\\hal",
"obj\\syscall", "obj\\runtime", "obj\\win32", "obj\\posix", "obj\\drivers", "obj\\init", "obj\\rssrv", "obj\\posixsrv", "obj\\dwm",
"obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native",
"obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget",
"obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping", "obj\\doom", "obj\\video", "obj\\memtop",
"obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\anonymous_queue_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap",
"obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread",
"obj\\all_tests", "obj\\posix_service_smoke", "obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_commit", "obj\\surface_cross_process_smoke", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke", "obj\\posix_runtime_smoke", "obj\\posix_fd_smoke", "obj\\posix_process_smoke", "obj\\posix_signal_smoke", "obj\\posix_pipe_smoke", "obj\\exception_dispatcher_smoke", "obj\\native_exception_runtime_smoke", "obj\\native_raise_exception_smoke", "obj\\exception_smoke", "obj\\win32_exception_smoke", "obj\\win32_seh_smoke"
};
for (size_t i = 0; i < NOB_ARRAY_LEN(dirs); i++) nob_mkdir_if_not_exists(dirs[i]);
nob_mkdir_if_not_exists(PCI_BUS_DRIVER.obj_dir);
for (size_t i = 0; i < NOB_ARRAY_LEN(STANDALONE_DRIVERS); i++) nob_mkdir_if_not_exists(STANDALONE_DRIVERS[i].obj_dir);
}
// Runs a shell command and waits for it to finish.
bool run_sync(const char* cmd_str) {
Nob_Cmd cmd = {0};
nob_cmd_append(&cmd, "cmd.exe", "/c", cmd_str);
bool ok = nob_cmd_run_sync(cmd);
nob_cmd_free(cmd);
return ok;
}
// Launches a shell command while the build graph continues.
Nob_Proc run_async(const char* cmd_str) {
Nob_Cmd cmd = {0};
nob_cmd_append(&cmd, "cmd.exe", "/c", cmd_str);
Nob_Proc proc = nob_cmd_run_async(cmd);
nob_cmd_free(cmd);
return proc;
}
// Deletes stale object files from a target directory before compiling into it.
bool clean_object_directory(const char* obj_dir) {
const char* cmd = nob_temp_sprintf("if exist %s\\*.obj del /q %s\\*.obj", obj_dir, obj_dir);
return run_sync(cmd);
}
// Cleans object directories that are linked through wildcards.
bool clean_build_object_directories(void) {
const char* dirs[] = {
"obj\\boot", "obj\\ddk", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\acpica", "obj\\hal",
"obj\\syscall", "obj\\runtime", "obj\\win32", "obj\\posix",
"obj\\init", "obj\\rssrv", "obj\\posixsrv", "obj\\dwm", "obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native",
"obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget",
"obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping",
"obj\\doom", "obj\\video", "obj\\memtop", "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\anonymous_queue_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap",
"obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread",
"obj\\all_tests", "obj\\posix_service_smoke", "obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_commit", "obj\\surface_cross_process_smoke", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke", "obj\\posix_runtime_smoke", "obj\\posix_fd_smoke", "obj\\posix_process_smoke", "obj\\posix_signal_smoke", "obj\\posix_pipe_smoke", "obj\\exception_dispatcher_smoke", "obj\\native_exception_runtime_smoke", "obj\\native_raise_exception_smoke", "obj\\exception_smoke", "obj\\win32_exception_smoke", "obj\\win32_seh_smoke"
};
for (size_t i = 0; i < NOB_ARRAY_LEN(dirs); i++) {
if (!clean_object_directory(dirs[i])) return false;
}
if (!clean_object_directory(PCI_BUS_DRIVER.obj_dir)) return false;
for (size_t i = 0; i < NOB_ARRAY_LEN(STANDALONE_DRIVERS); i++) {
if (!clean_object_directory(STANDALONE_DRIVERS[i].obj_dir)) return false;
}
return true;
}
// Creates the USB fat root once and leaves its contents alone between builds.
bool create_usbfatroot_if_missing(void) {
return nob_mkdir_if_not_exists("usbfatroot");
}
// Copies one local file into a FAT image using mcopy without admin rights.
bool mcopy_file(const char* src, const char* vhd_target, const char* dst_path) {
const char* cmd = nob_temp_sprintf("mcopy -o -i %s %s ::%s", vhd_target, src, dst_path);
return run_sync(cmd);
}
// Copies several same-name files into one FAT directory with a single mcopy process.
bool mcopy_files_to_dir(const char* vhd_target,
const char* dst_dir,
const char* const* sources,
size_t source_count) {
if (!vhd_target || !dst_dir || (!sources && source_count))
return false;
if (!source_count)
return true;
Nob_String_Builder command = {0};
nob_sb_appendf(&command, "mcopy -o -i %s", vhd_target);
for (size_t i = 0; i < source_count; ++i)
nob_sb_appendf(&command, " %s", sources[i]);
nob_sb_appendf(&command, " ::%s", dst_dir);
nob_sb_append_null(&command);
bool ok = run_sync(command.items);
nob_sb_free(command);
return ok;
}
// Utility to create a directory in a FAT image using mmd.
bool mmd_dir(const char* vhd_target, const char* dir_path) {
const char* cmd = nob_temp_sprintf("mmd -i %s ::%s 2>nul", vhd_target, dir_path);
run_sync(cmd);
return true;
}
// Writes a 512-byte Connectix VHD Fixed Disk Footer at the end of the file.
static void write_vpc_footer(FILE* f, uint64_t disk_size_bytes) {
uint8_t footer[512] = {0};
memcpy(&footer[0], "conectix", 8);
// Features: 0x00000002 (Big Endian)
footer[11] = 0x02;
// File Format Version: 0x00010000
footer[13] = 0x01;
// Data Offset: 0xFFFFFFFFFFFFFFFF (Fixed disk)
memset(&footer[16], 0xFF, 8);
// Creator Application: "qemu"
memcpy(&footer[28], "qemu", 4);
// Creator Version: 0x00020000
footer[33] = 0x02;
// Creator Host OS: "Wi2k"
memcpy(&footer[36], "Wi2k", 4);
// Original & Current Size (uint64_t Big Endian)
for (int i = 0; i < 8; i++) {
uint8_t b = (uint8_t)(disk_size_bytes >> ((7 - i) * 8));
footer[40 + i] = b;
footer[48 + i] = b;
}
// Geometry for 512MB: 1024 Cylinders (0x0400), 16 Heads, 63 Sectors/Track
footer[56] = 0x04;
footer[57] = 0x00;
footer[58] = 16;
footer[59] = 63;
// Disk Type: 2 (Fixed Hard Disk)
footer[63] = 0x02;
// Unique ID GUID
static const uint8_t guid[16] = {
0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x78, 0x90,
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0
};
memcpy(&footer[68], guid, 16);
// Checksum: 1's complement sum of all bytes in footer with checksum cleared
uint32_t sum = 0;
for (int i = 0; i < 512; i++) {
sum += footer[i];
}
uint32_t checksum = ~sum;
footer[64] = (uint8_t)(checksum >> 24);
footer[65] = (uint8_t)(checksum >> 16);
footer[66] = (uint8_t)(checksum >> 8);
footer[67] = (uint8_t)(checksum & 0xFF);
fseek(f, (long)disk_size_bytes, SEEK_SET);
fwrite(footer, 1, 512, f);
}
// Ensures the managed development VHD carries the stable identity used by boot.ini.
static bool ensure_disk_vhd_signature(void) {
FILE* f = fopen("disk.vhd", "r+b");
if (!f) return false;
uint32_t signature = DEFAULT_VHD_DISK_SIGNATURE;
if (fseek(f, 440, SEEK_SET) != 0 ||
fwrite(&signature, sizeof(signature), 1, f) != 1) {
fclose(f);
return false;
}
fclose(f);
return true;
}
// Creates a 512 MB partitioned disk.vhd image with a valid VPC footer.
bool create_disk_vhd_if_missing(void) {
if (nob_file_exists("disk.vhd")) return ensure_disk_vhd_signature();
nob_log(NOB_INFO, "disk.vhd missing, creating new 512 MB fixed VHD image...");
const uint64_t disk_size = (uint64_t)512 * 1024 * 1024; // 512 MB Payload
FILE* f = fopen("disk.vhd", "wb");
if (!f) {
nob_log(NOB_ERROR, "Failed to create disk.vhd file!");
return false;
}
// Expand file to 512MB payload
if (fseek(f, (long)(disk_size - 1), SEEK_SET) != 0) {
fclose(f);
return false;
}
fputc(0, f);
// Build standard MBR partition table (Sector 0)
uint8_t mbr[512] = {0};
uint32_t disk_signature = DEFAULT_VHD_DISK_SIGNATURE;
memcpy(&mbr[440], &disk_signature, sizeof(disk_signature));
mbr[510] = 0x55;
mbr[511] = 0xAA;
// Partition 1 Entry: 64MB FAT32 @ LBA 2048 (1MB offset)
uint8_t* p1 = &mbr[0x01BE];
p1[0] = 0x80; // Active / Bootable
p1[1] = 0x00; p1[2] = 0x02; p1[3] = 0x00; // CHS Start
p1[4] = 0x0C; // FAT32 LBA
p1[5] = 0xFE; p1[6] = 0xFF; p1[7] = 0xFF; // CHS End (LBA boundary)
uint32_t p1_lba = 2048;
uint32_t p1_sectors = 131072; // 64 MB
memcpy(&p1[8], &p1_lba, 4);
memcpy(&p1[12], &p1_sectors, 4);
// Partition 2 Entry: 256MB FAT32 @ LBA 133120 (65MB offset)
uint8_t* p2 = &mbr[0x01CE];
p2[0] = 0x00; // Inactive
p2[1] = 0x00; p2[2] = 0x01; p2[3] = 0x08; // CHS Start
p2[4] = 0x0C; // FAT32 LBA
p2[5] = 0xFE; p2[6] = 0xFF; p2[7] = 0xFF; // CHS End (LBA boundary)
uint32_t p2_lba = 133120;
uint32_t p2_sectors = 524288; // 256 MB
memcpy(&p2[8], &p2_lba, 4);
memcpy(&p2[12], &p2_sectors, 4);
// Write Sector 0
fseek(f, 0, SEEK_SET);
fwrite(mbr, 1, 512, f);
// Append 512-byte Connectix VPC Footer
write_vpc_footer(f, disk_size);
fclose(f);
nob_log(NOB_INFO, "Formatting disk.vhd partitions with mformat...");
if (!run_sync("mformat -i " VHD_PART1 " -F")) return false;
if (!run_sync("mformat -i " VHD_PART2 " -F")) return false;
return true;
}
// Runs the requested operation.
int main(int argc, char** argv) {
NOB_GO_REBUILD_URSELF(argc, argv);
bool with_tests = argc == 2 && strcmp(argv[1], "--with-tests") == 0;
if (argc != 1 && !with_tests) {
nob_log(NOB_ERROR, "Usage: build.exe [--with-tests]");
return 1;
}
if (!create_disk_vhd_if_missing()) return 1;
if (!create_usbfatroot_if_missing()) return 1;
create_layout();
if (!nob_file_exists("code\\drivers\\fs\\ext4\\third_party\\lwext4\\src\\ext4.c")) {
nob_log(NOB_ERROR, "lwext4 is not vendored. Run: powershell -ExecutionPolicy Bypass -File code\\drivers\\fs\\ext4\\vendor_lwext4.ps1");
return 1;
}
if (!nob_file_exists("code\\drivers\\fs\\ntfs\\ntfs3g\\third_party\\ntfs-3g\\libntfs-3g\\volume.c")) {
nob_log(NOB_ERROR, "ntfs-3g is not vendored. Run: powershell -ExecutionPolicy Bypass -File code\\drivers\\fs\\ntfs\\ntfs3g\\vendor_ntfs3g.ps1");
return 1;
}
if (!clean_build_object_directories()) return 1;
nob_log(NOB_INFO, "--- Building Core Subsystems ---");
const char* core_tasks[] = {
"ml64 /nologo /c /Foobj\\kernel\\ke_switch.obj code\\kernel\\ke_switch.asm",
"ml64 /nologo /c /Foobj\\kernel\\ke_syscall_asm.obj code\\kernel\\ke_syscall.asm",
"ml64 /nologo /c /Foobj\\kernel\\ke_gdt_reload.obj code\\kernel\\ke_gdt_reload.asm",
"ml64 /nologo /c /Foobj\\syscall\\stubs.obj code\\user\\syscall\\syscall_stubs.asm",
"ml64 /nologo /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\stubs_s.obj code\\user\\syscall\\syscall_stubs.asm",
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\pe_loader.obj code\\user\\syscall\\pe_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\pe_loader_s.obj code\\user\\syscall\\pe_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\module_loader.obj code\\user\\syscall\\module_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\module_loader_s.obj code\\user\\syscall\\module_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\process_loader.obj code\\user\\syscall\\process_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\process_loader_s.obj code\\user\\syscall\\process_loader.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\resource.obj code\\user\\syscall\\resource.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\resource_s.obj code\\user\\syscall\\resource.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\input_service.obj code\\user\\syscall\\input_service.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\input_service_s.obj code\\user\\syscall\\input_service.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\console_service.obj code\\user\\syscall\\console_service.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\console_service_s.obj code\\user\\syscall\\console_service.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\desktop.obj code\\user\\syscall\\desktop.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\desktop_s.obj code\\user\\syscall\\desktop.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_BUILD /c /Foobj\\syscall\\path.obj code\\user\\syscall\\path.c", UserCflags),
nob_temp_sprintf("cl %s /DNTL_SYSCALL_STATIC /c /Foobj\\syscall\\path_s.obj code\\user\\syscall\\path.c", UserCflags),
"for %f in (code\\boot\\uefi\\*.asm) do ml64 /nologo /c /Fo\"obj\\boot\\%~nf.obj\" \"%f\"",
nob_temp_sprintf("cl %s /MP /c /Foobj\\ code\\common\\printf.c", CommonCflags),
"cl /nologo /c /Foobj\\libc.obj code\\common\\libc.c",
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\user_heap.obj code\\user\\runtime\\heap.c", UserCflags),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\user_heap_exports.obj code\\user\\runtime\\heap_exports.c", UserCflags),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\native_exception.obj code\\user\\runtime\\native_exception.c", UserCflags),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\native_start.obj code\\user\\runtime\\native_start.c", UserCflags),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\posix_start.obj code\\user\\runtime\\posix_start.c", PosixUserCflags),
// Bootloader.
nob_temp_sprintf("cl %s /MP /c /Icode\\boot\\uefi\\include /Foobj\\boot\\ code\\boot\\uefi\\*.c code\\common\\boot_volume_selector.c && link %s /FORCE:MULTIPLE /SUBSYSTEM:EFI_APPLICATION /ENTRY:UefiMain /OUT:bin\\bootx64.efi obj\\libc.obj code\\boot\\uefi\\efilib.lib obj\\boot\\*.obj", BootCflags, LFLAGS),
// HAL.
nob_temp_sprintf("cl %s /DNTL_HAL_BUILD /MP /c /Foobj\\hal\\ code\\kernel\\hal\\*.c && link %s /DLL /FIXED /DYNAMICBASE:NO /HIGHENTROPYVA:NO /RELEASE /BASE:0xFFFF800010000000 /OUT:bin\\hal.dll /NOENTRY /IGNORE:4216 obj\\printf.obj obj\\libc.obj obj\\hal\\*.obj", KernelCflags, LFLAGS),
// Kernel.
nob_temp_sprintf("cl %s /DNTL_KERNEL_BUILD /DKE_DISPLAY_BUILD /MP /c /Foobj\\kernel\\ code\\kernel\\*.c code\\common\\boot_volume_selector.c && link %s /SUBSYSTEM:NATIVE /FIXED /DYNAMICBASE:NO /HIGHENTROPYVA:NO /RELEASE /BASE:0xFFFF800000000000 /ENTRY:KernelMain /MAP:bin\\kernel.map /OUT:bin\\kernel.exe bin\\hal.lib obj\\printf.obj obj\\libc.obj obj\\kernel\\*.obj", KernelCflags, LFLAGS),
// Subsystem DLLs.
"link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL " USER_IMAGE_FLAGS_LITERAL " /OUT:bin\\syscall.dll obj\\syscall\\stubs.obj obj\\syscall\\pe_loader.obj obj\\syscall\\module_loader.obj obj\\syscall\\process_loader.obj obj\\syscall\\resource.obj obj\\syscall\\input_service.obj obj\\syscall\\console_service.obj obj\\syscall\\desktop.obj obj\\syscall\\path.obj obj\\libc.obj",
"lib /nologo /OUT:bin\\syscall_s.lib obj\\syscall\\stubs_s.obj obj\\syscall\\pe_loader_s.obj obj\\syscall\\module_loader_s.obj obj\\syscall\\process_loader_s.obj obj\\syscall\\resource_s.obj obj\\syscall\\input_service_s.obj obj\\syscall\\console_service_s.obj obj\\syscall\\desktop_s.obj obj\\syscall\\path_s.obj",
nob_temp_sprintf("cl %s /DUSER_HEAP_EXPORTS /DNTL_EXCEPTION_EXPORTS /MP /c /Foobj\\win32\\ code\\user\\runtime\\heap.c code\\user\\runtime\\heap_exports.c code\\user\\runtime\\native_exception.c code\\user\\personalities\\win32\\*.c && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL " USER_IMAGE_FLAGS_LITERAL " /OUT:bin\\win32.dll bin\\syscall.lib obj\\libc.obj obj\\win32\\*.obj", UserCflags),
"ml64 /nologo /Icode\\user\\syscall /c /Foobj\\posix\\posix_vfork_asm.obj code\\user\\personalities\\posix\\posix_vfork.asm",
nob_temp_sprintf("cl %s /DNTL_POSIX_BUILD /DUSER_HEAP_EXPORTS /DNTL_EXCEPTION_EXPORTS /MP /c /Foobj\\posix\\ code\\user\\runtime\\heap.c code\\user\\runtime\\heap_exports.c code\\user\\runtime\\native_exception.c code\\user\\personalities\\posix\\*.c && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL " USER_IMAGE_FLAGS_LITERAL " /OUT:bin\\posix.dll bin\\syscall.lib obj\\libc.obj obj\\posix\\*.obj", PosixUserCflags)
};
for (size_t i = 0; i < NOB_ARRAY_LEN(core_tasks); i++) {
if (!run_sync(core_tasks[i])) return 1;
}
// The support library contains only driver-side helpers, never kernel policy.
if (!run_sync(nob_temp_sprintf(
"cl %s /MP /c /Foobj\\ddk\\ code\\ddk\\*.c && lib /nologo /OUT:bin\\ntlddk.lib obj\\ddk\\*.obj",
DRIVER_CFLAGS_LITERAL))) return 1;
nob_log(NOB_INFO, "--- Building Bus Drivers ---");
const char* pci_cmd = nob_temp_sprintf(
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys /IMPLIB:bin\\%s.lib /NOENTRY bin\\kernel.lib bin\\ntlddk.lib obj\\printf.obj obj\\libc.obj %s\\*.obj",
PCI_BUS_DRIVER.cflags, PCI_BUS_DRIVER.obj_dir, PCI_BUS_DRIVER.sources,
PCI_BUS_DRIVER.name, PCI_BUS_DRIVER.name, PCI_BUS_DRIVER.obj_dir
);
if (!run_sync(pci_cmd)) return 1;
nob_log(NOB_INFO, "--- Compiling Drivers and Apps in Parallel ---");
Nob_Procs procs = {0};
for (size_t i = 0; i < NOB_ARRAY_LEN(STANDALONE_DRIVERS); i++) {
const Driver_Target* driver = &STANDALONE_DRIVERS[i];
const char* cmd = nob_temp_sprintf(
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys %s bin\\kernel.lib %s obj\\libc.obj %s\\*.obj",
driver->cflags, driver->obj_dir, driver->sources, driver->name,
driver->entry_flag, driver->libs ? driver->libs : "", driver->obj_dir
);
nob_da_append(&procs, run_async(cmd));
}
// Compile the pinned ACPICA interpreter from its reproducible source manifest.
Nob_String_Builder acpi_manifest = {0}, acpi_files = {0};
if (!nob_read_entire_file("code\\drivers\\platform\\acpi\\acpica_sources.txt", &acpi_manifest)) return 1;
Nob_String_View acpi_view = nob_sv_from_parts(acpi_manifest.items, acpi_manifest.count);
while (acpi_view.count > 0) {
Nob_String_View line = nob_sv_trim(nob_sv_chop_by_delim(&acpi_view, '\n'));
if (!line.count || line.data[0] == '#') continue;
nob_sb_append_cstr(&acpi_files, "code\\");
for (size_t i = 0; i < line.count; i++) {
char ch = line.data[i] == '/' ? '\\' : line.data[i];
nob_sb_append_buf(&acpi_files, &ch, 1);
}
nob_sb_append_cstr(&acpi_files, " ");
}
if (!nob_write_entire_file("obj\\acpica\\sources.rsp", acpi_files.items, acpi_files.count)) return 1;
nob_da_append(&procs, run_async(nob_temp_sprintf(
"cl %s /MP /c /Foobj\\acpica\\ code\\drivers\\platform\\acpi\\acpi.c code\\drivers\\platform\\acpi\\acpi_osl.c code\\drivers\\platform\\acpi\\acpi_ec.c @obj\\acpica\\sources.rsp && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /NOENTRY /OUT:bin\\acpi.sys bin\\kernel.lib obj\\acpica\\*.obj",
ACPICA_CFLAGS_LITERAL)));
Nob_String_Builder sb = {0}, lwip_files = {0};
if (nob_read_entire_file("code\\drivers\\net\\lwip\\lwip_sources.txt", &sb)) {
Nob_String_View sv = nob_sv_from_parts(sb.items, sb.count);
while (sv.count > 0) {
Nob_String_View line = nob_sv_trim(nob_sv_chop_by_delim(&sv, '\n'));
if (line.count > 0 && line.data[0] != '#') {
nob_sb_append_cstr(&lwip_files, "code\\");
for (size_t i = 0; i < line.count; i++) nob_sb_append_buf(&lwip_files, &(char){line.data[i] == '/' ? '\\' : line.data[i]}, 1);
nob_sb_append_cstr(&lwip_files, " ");
}
}
nob_sb_append_null(&lwip_files);
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\lwip\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\lwip.sys /NOENTRY bin\\kernel.lib obj\\libc.obj obj\\lwip\\*.obj", LwipCflags, lwip_files.items)));
}
Nob_String_Builder ext4_sb = {0}, ext4_files = {0};
if (nob_read_entire_file("code\\drivers\\fs\\ext4\\lwext4_sources.txt", &ext4_sb)) {
Nob_String_View sv = nob_sv_from_parts(ext4_sb.items, ext4_sb.count);
while (sv.count > 0) {
Nob_String_View line = nob_sv_trim(nob_sv_chop_by_delim(&sv, '\n'));
if (line.count > 0 && line.data[0] != '#') {
nob_sb_append_cstr(&ext4_files, "code\\");
for (size_t i = 0; i < line.count; i++) nob_sb_append_buf(&ext4_files, &(char){line.data[i] == '/' ? '\\' : line.data[i]}, 1);
nob_sb_append_cstr(&ext4_files, " ");
}
}
nob_sb_append_null(&ext4_files);
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ext4\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ext4.sys /NOENTRY bin\\kernel.lib obj\\libc.obj obj\\ext4\\*.obj", Ext4Cflags, ext4_files.items)));
}
Nob_String_Builder ntfs3g_sb = {0}, ntfs3g_files = {0};
if (nob_read_entire_file("code\\drivers\\fs\\ntfs\\ntfs3g\\ntfs3g_sources.txt", &ntfs3g_sb)) {
Nob_String_View sv = nob_sv_from_parts(ntfs3g_sb.items, ntfs3g_sb.count);
while (sv.count > 0) {
Nob_String_View line = nob_sv_trim(nob_sv_chop_by_delim(&sv, '\n'));
if (line.count > 0 && line.data[0] != '#') {
nob_sb_append_cstr(&ntfs3g_files, "code\\");
for (size_t i = 0; i < line.count; i++) nob_sb_append_buf(&ntfs3g_files, &(char){line.data[i] == '/' ? '\\' : line.data[i]}, 1);
nob_sb_append_cstr(&ntfs3g_files, " ");
}
}
nob_sb_append_null(&ntfs3g_files);
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ntfs3g\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ntfs.sys /NOENTRY bin\\kernel.lib obj\\printf.obj obj\\libc.obj obj\\ntfs3g\\*.obj", Ntfs3gCflags, ntfs3g_files.items)));
}
const char* apps[][4] = {
{"system\\init", NATIVE_INIT_LIBS_LITERAL, "init", "/ENTRY:_start"},
{"system\\rssrv", NATIVE_SERVICE_LIBS_LITERAL, "rssrv", "/ENTRY:_start"},
{"system\\posixsrv", NATIVE_SERVICE_LIBS_LITERAL, "posixsrv", "/ENTRY:_start"},
{"system\\dwm", NATIVE_SERVICE_LIBS_LITERAL, "dwm", "/ENTRY:_start"},
{"tests\\all_tests", NATIVE_EXE_LIBS_LITERAL, "all_tests", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\posix_service_smoke", NATIVE_EXE_LIBS_LITERAL, "posix_service_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\shell_win32", WIN32_EXE_LIBS_LITERAL, "shell_win32", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\shell_posix", POSIX_EXE_LIBS_LITERAL, "shell_posix", "/ENTRY:PosixStart"},
{"apps\\shell_native", NATIVE_EXE_LIBS_LITERAL, "shell_native", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\async_apc", NATIVE_EXE_LIBS_LITERAL, "async_apc", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\apc_return_smoke", NATIVE_EXE_LIBS_LITERAL, "apc_return_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\async_cancel", NATIVE_EXE_LIBS_LITERAL, "async_cancel", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\file_stat_smoke", NATIVE_EXE_LIBS_LITERAL, "file_stat_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\handle_dup_smoke", NATIVE_EXE_LIBS_LITERAL, "handle_dup_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\security_smoke", NATIVE_EXE_LIBS_LITERAL, "security_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\port_ipc_smoke", NATIVE_EXE_LIBS_LITERAL, "port_ipc_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\resource_service_smoke", NATIVE_EXE_LIBS_LITERAL, "resource_service_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\anonymous_queue_smoke", NATIVE_EXE_LIBS_LITERAL, "anonymous_queue_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\input_service_smoke", NATIVE_EXE_LIBS_LITERAL, "input_service_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\console_service_smoke", NATIVE_EXE_LIBS_LITERAL, "console_service_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\desktop_smoke", NATIVE_EXE_LIBS_LITERAL, "desktop_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\timer_smoke", NATIVE_EXE_LIBS_LITERAL, "timer_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\stats_abi_smoke", NATIVE_EXE_LIBS_LITERAL, "stats_abi_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\realpath_smoke", NATIVE_EXE_LIBS_LITERAL, "realpath_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\file_lock_smoke", NATIVE_EXE_LIBS_LITERAL, "file_lock_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\process_protection_smoke", NATIVE_EXE_LIBS_LITERAL, "process_protection_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\process_exit_multithread_smoke", NATIVE_EXE_LIBS_LITERAL, "process_exit_multithread_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\thread_name_smoke", NATIVE_EXE_LIBS_LITERAL, "thread_name_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\process_builder_smoke", "bin\\syscall_s.lib obj\\libc.obj " NATIVE_RUNTIME_OBJS_LITERAL, "process_builder_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\socket_addr_smoke", NATIVE_EXE_LIBS_LITERAL, "socket_addr_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\net_irp_smoke", NATIVE_EXE_LIBS_LITERAL, "net_irp_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\module_load_smoke", NATIVE_EXE_LIBS_LITERAL, "module_load_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\posix_module_smoke", POSIX_EXE_LIBS_LITERAL, "posix_module_smoke", "/ENTRY:PosixStart"},
{"tests\\posix_runtime_smoke", POSIX_EXE_LIBS_LITERAL, "posix_runtime_smoke", "/ENTRY:PosixStart"},
{"tests\\posix_fd_smoke", POSIX_EXE_LIBS_LITERAL, "posix_fd_smoke", "/ENTRY:PosixStart"},
{"tests\\posix_process_smoke", POSIX_EXE_LIBS_LITERAL, "posix_process_smoke", "/ENTRY:PosixStart"},
{"tests\\posix_signal_smoke", POSIX_EXE_LIBS_LITERAL, "posix_signal_smoke", "/ENTRY:PosixStart"},
{"tests\\posix_pipe_smoke", POSIX_EXE_LIBS_LITERAL, "posix_pipe_smoke", "/ENTRY:PosixStart"},
{"tests\\exception_dispatcher_smoke", NATIVE_EXE_LIBS_LITERAL, "exception_dispatcher_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\native_exception_runtime_smoke", NATIVE_EXE_LIBS_LITERAL, "native_exception_runtime_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\native_raise_exception_smoke", NATIVE_EXE_LIBS_LITERAL, "native_raise_exception_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\exception_smoke", NATIVE_EXE_LIBS_LITERAL, "exception_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_exception_smoke", WIN32_EXE_LIBS_LITERAL, "win32_exception_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_seh_smoke", WIN32_EXE_LIBS_LITERAL, "win32_seh_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\syscall_contract_smoke", NATIVE_EXE_LIBS_LITERAL, "syscall_contract_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\would_block_smoke", NATIVE_EXE_LIBS_LITERAL, "would_block_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\audio_stream_smoke", NATIVE_EXE_LIBS_LITERAL, "audio_stream_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\winmm_waveout_smoke", WIN32_EXE_LIBS_LITERAL, "winmm_waveout_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_condition_variable_smoke", WIN32_EXE_LIBS_LITERAL, "win32_condition_variable_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_mutex_smoke", WIN32_EXE_LIBS_LITERAL, "win32_mutex_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_thread_suspend_smoke", WIN32_EXE_LIBS_LITERAL, "win32_thread_suspend_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\win32_module_lookup_smoke", WIN32_EXE_LIBS_LITERAL, "win32_module_lookup_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\cwd_userspace_smoke", NATIVE_EXE_LIBS_LITERAL, "cwd_userspace_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\mmap_smoke", NATIVE_EXE_LIBS_LITERAL, "mmap_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\pcache_smoke", NATIVE_EXE_LIBS_LITERAL, "pcache_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\pcache_pressure", NATIVE_EXE_LIBS_LITERAL, "pcache_pressure", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\dirty_writeback", NATIVE_EXE_LIBS_LITERAL, "dirty_writeback", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\shared_mmap", NATIVE_EXE_LIBS_LITERAL, "shared_mmap", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\pcache_invalidate", NATIVE_EXE_LIBS_LITERAL, "pcache_invalidate", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\pcache_mmap_read_reuse", NATIVE_EXE_LIBS_LITERAL, "pcache_mmap_read_reuse", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\pcache_multithread", NATIVE_EXE_LIBS_LITERAL, "pcache_multithread", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\surface_smoke", NATIVE_EXE_LIBS_LITERAL, "surface_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\surface_invalid_rect", NATIVE_EXE_LIBS_LITERAL, "surface_invalid_rect", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\surface_multithread_commit", NATIVE_EXE_LIBS_LITERAL, "surface_multithread_commit", NATIVE_EXE_ENTRY_LITERAL},
{"tests\\surface_cross_process_smoke", NATIVE_EXE_LIBS_LITERAL, "surface_cross_process_smoke", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\edit", NATIVE_EXE_LIBS_LITERAL, "edit", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\httpget", NATIVE_EXE_LIBS_LITERAL, "httpget", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\tcpserve", NATIVE_EXE_LIBS_LITERAL, "tcpserve", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\udp", NATIVE_EXE_LIBS_LITERAL, "udp", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\resolve", NATIVE_EXE_LIBS_LITERAL, "resolve", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\ping", NATIVE_EXE_LIBS_LITERAL, "ping", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\doom", NATIVE_EXE_LIBS_LITERAL, "doom", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\video", "bin\\syscall.lib obj\\libc.obj " NATIVE_RUNTIME_OBJS_LITERAL " code\\user\\apps\\video\\bink2w64.lib", "video", NATIVE_EXE_ENTRY_LITERAL},
{"apps\\memtop", NATIVE_EXE_LIBS_LITERAL, "memtop", NATIVE_EXE_ENTRY_LITERAL}
};
for (size_t i = 0; i < NOB_ARRAY_LEN(apps); i++) {
if (!with_tests && strncmp(apps[i][0], "tests\\", 6) == 0)
continue;
const char* appCflags = strcmp(apps[i][3], "/ENTRY:PosixStart") == 0
? PosixUserCflags
: UserCflags;
const char* cmd = nob_temp_sprintf(
"cl %s /MP /c /Foobj\\%s\\ code\\user\\%s\\*.c && link %s /SUBSYSTEM:NATIVE " USER_IMAGE_FLAGS_LITERAL " %s /OUT:bin\\%s.exe /PDB:bin\\%s.pdb %s obj\\%s\\*.obj",
appCflags, apps[i][2], apps[i][0], LFLAGS, apps[i][3], apps[i][2], apps[i][2], apps[i][1], apps[i][2]
);
nob_da_append(&procs, run_async(cmd));
}
if (!nob_procs_wait(procs)) {
nob_log(NOB_ERROR, "One or more parallel build jobs failed!");
return 1;
}
// 4. Formatting and Packing VHD via mtools (No Admin, Fast).
nob_log(NOB_INFO, "--- Formatting & Packing disk.vhd with mtools ---");
run_sync("mformat -i " VHD_PART1 " -F");
run_sync("mformat -i " VHD_PART2 " -F");
mmd_dir(VHD_PART1, "EFI");
mmd_dir(VHD_PART1, "EFI/BOOT");
mmd_dir(VHD_PART1, "EFI/NTLite");
mmd_dir(VHD_PART1, "EFI/NTLite/fs");
mmd_dir(VHD_PART2, "apps");
mmd_dir(VHD_PART2, "system");
mmd_dir(VHD_PART2, "drivers");
if (with_tests) mmd_dir(VHD_PART2, "tests");
// The ESP is only the boot anchor; the configured system volume carries the OS payload.
if (!mcopy_file("bin\\bootx64.efi", VHD_PART1, "EFI/BOOT/bootx64.efi")) return 1;
if (!mcopy_file("code\\boot.ini", VHD_PART1, "boot.ini")) return 1;
if (!run_sync("if exist code\\boot\\uefi\\fs\\*.efi mcopy -o -i " VHD_PART1 " code\\boot\\uefi\\fs\\*.efi ::EFI/NTLite/fs/")) return 1;
if (!mcopy_file("bin\\kernel.exe", VHD_PART2, "system/colonel.exe")) return 1;
const char* system_files[] = {
"bin\\hal.dll",
"bin\\syscall.dll",
"bin\\win32.dll",
"bin\\posix.dll",
"code\\settings.ini",
"code\\services.ini",
"bin\\init.exe",
"bin\\rssrv.exe",
"bin\\posixsrv.exe",
"bin\\dwm.exe",
};
if (!mcopy_files_to_dir(VHD_PART2, "system/", system_files, NOB_ARRAY_LEN(system_files))) return 1;
const char* driver_files[] = {
"bin\\pci.sys",
"bin\\ps2.sys",
"bin\\hda.sys",
"bin\\ata.sys",
"bin\\ahci.sys",
"bin\\fat.sys",
"bin\\ntfs.sys",
"bin\\ext4.sys",
"bin\\vmwaresvga.sys",
"bin\\lwip.sys",
"bin\\usb.sys",
"bin\\acpi.sys",
"bin\\rtl816x.sys",
"bin\\rtl8139.sys",
"code\\drivers\\drivers.ini",
};
if (!mcopy_files_to_dir(VHD_PART2, "drivers/", driver_files, NOB_ARRAY_LEN(driver_files))) return 1;
// The build artifact keeps the descriptive target name; the installed serial driver gets the cereal joke.
if (!mcopy_file("bin\\serial.sys", VHD_PART2, "drivers/cereal.sys")) return 1;
const char* app_files[] = {
"bin\\shell_win32.exe",
"bin\\shell_posix.exe",
"bin\\shell_native.exe",
"bin\\edit.exe",
"bin\\httpget.exe",
"bin\\tcpserve.exe",
"bin\\udp.exe",
"bin\\resolve.exe",
"bin\\ping.exe",
"bin\\doom.exe",
"bin\\video.exe",
"bin\\memtop.exe",
};
if (!mcopy_files_to_dir(VHD_PART2, "apps/", app_files, NOB_ARRAY_LEN(app_files))) return 1;
const char* test_files[] = {
"bin\\all_tests.exe",
"bin\\posix_service_smoke.exe",
"bin\\async_apc.exe",
"bin\\async_cancel.exe",
"bin\\apc_return_smoke.exe",
"bin\\file_stat_smoke.exe",
"bin\\handle_dup_smoke.exe",
"bin\\security_smoke.exe",
"bin\\port_ipc_smoke.exe",
"bin\\resource_service_smoke.exe",
"bin\\anonymous_queue_smoke.exe",
"bin\\input_service_smoke.exe",
"bin\\console_service_smoke.exe",
"bin\\desktop_smoke.exe",
"bin\\timer_smoke.exe",
"bin\\stats_abi_smoke.exe",
"bin\\realpath_smoke.exe",
"bin\\file_lock_smoke.exe",
"bin\\process_protection_smoke.exe",
"bin\\process_exit_multithread_smoke.exe",
"bin\\thread_name_smoke.exe",
"bin\\process_builder_smoke.exe",
"bin\\socket_addr_smoke.exe",
"bin\\net_irp_smoke.exe",
"bin\\module_load_smoke.exe",
"bin\\posix_module_smoke.exe",
"bin\\posix_runtime_smoke.exe",
"bin\\posix_fd_smoke.exe",
"bin\\posix_process_smoke.exe",
"bin\\posix_signal_smoke.exe",
"bin\\posix_pipe_smoke.exe",
"bin\\exception_dispatcher_smoke.exe",
"bin\\native_exception_runtime_smoke.exe",
"bin\\native_raise_exception_smoke.exe",
"bin\\exception_smoke.exe",
"bin\\win32_exception_smoke.exe",
"bin\\win32_seh_smoke.exe",
"bin\\syscall_contract_smoke.exe",
"bin\\would_block_smoke.exe",
"bin\\audio_stream_smoke.exe",
"bin\\winmm_waveout_smoke.exe",
"bin\\win32_condition_variable_smoke.exe",
"bin\\win32_mutex_smoke.exe",
"bin\\win32_thread_suspend_smoke.exe",
"bin\\win32_module_lookup_smoke.exe",
"bin\\cwd_userspace_smoke.exe",
"bin\\mmap_smoke.exe",
"bin\\pcache_smoke.exe",
"bin\\pcache_pressure.exe",
"bin\\dirty_writeback.exe",
"bin\\shared_mmap.exe",
"bin\\pcache_invalidate.exe",
"bin\\pcache_mmap_read_reuse.exe",
"bin\\pcache_multithread.exe",
"bin\\surface_smoke.exe",
"bin\\surface_invalid_rect.exe",
"bin\\surface_multithread_commit.exe",
"bin\\surface_cross_process_smoke.exe",
};
if (with_tests && !mcopy_files_to_dir(VHD_PART2, "tests/", test_files, NOB_ARRAY_LEN(test_files))) return 1;
const char* optional_app_files[3];
size_t optional_app_count = 0;
if (nob_file_exists("code\\user\\apps\\video\\bink2w64.dll"))
optional_app_files[optional_app_count++] = "code\\user\\apps\\video\\bink2w64.dll";
if (nob_file_exists("code\\user\\apps\\video\\video.bk2"))
optional_app_files[optional_app_count++] = "code\\user\\apps\\video\\video.bk2";
if (nob_file_exists("code\\user\\apps\\doom\\doom1.wad"))
optional_app_files[optional_app_count++] = "code\\user\\apps\\doom\\doom1.wad";
if (!mcopy_files_to_dir(VHD_PART2, "apps/", optional_app_files, optional_app_count)) return 1;
nob_log(NOB_INFO, "Build complete!");
return 0;
}