/* * ArkOS Build System v3.0 * ----------------------- * Orchestrates the complete OS build pipeline from source to bootable images. * * Architecture: * 1. Clean previous output (preserving build caches) * 2. Stage frameworks, boot assets, vendor blobs, and service configs * 3. Compile all system binaries (init, arkrt, splash, display daemon, UI app) * 4. Build the SwiftUI framework via SPM (cached — only rebuilds on changes) * 5. Sign the runtime binary for Verified Boot * 6. Pack initramfs with all runtime binaries * 7. Assemble bootloader and generate disk images * * All intermediate artifacts go into out_staging/. Final outputs go into finished/. * The .swift_build/ directory inside out_staging/ is preserved across builds * to enable SPM incremental compilation caching. * * Usage: build */ #include "ark_parser.h" #include #include #include #include #include #include /* ── Build Configuration ───────────────────────────────────────── */ #define TOTAL_STEPS 16 static int current_step = 0; /* ── Progress Reporting ────────────────────────────────────────── */ static void step(const char *desc) { current_step++; printf("\n\033[1;36m[%2d/%d]\033[0m %s\n", current_step, TOTAL_STEPS, desc); } /* ── Command Execution ─────────────────────────────────────────── */ /** * Execute a shell command and print it. Non-zero exit codes are reported * but do NOT abort the build — callers must check return values for * critical steps. */ static int run(const char *cmd) { printf(" \033[0;90m$ %s\033[0m\n", cmd); int ret = system(cmd); if (ret != 0) { fprintf(stderr, " \033[1;31m✗ Command failed (exit %d)\033[0m\n", ret); } return ret; } /* ── Path Utilities ────────────────────────────────────────────── */ /** Join two path components. Caller must free the returned string. */ static char *pjoin(const char *a, const char *b) { size_t len = strlen(a) + 1 + strlen(b) + 1; char *out = malloc(len); snprintf(out, len, "%s/%s", a, b); return out; } /* ── Incremental Build Helpers ─────────────────────────────────── */ /** * Check if an output file exists and is newer than all listed source files. * Returns 1 if the output is up-to-date (build can be skipped), 0 otherwise. * * This enables incremental builds — Swift compilation is expensive, so we * only recompile when source files have actually changed. */ static int is_up_to_date(const char *output, const char **sources, int source_count) { struct stat out_st; if (stat(output, &out_st) != 0) { return 0; /* Output doesn't exist — must build */ } for (int i = 0; i < source_count; i++) { struct stat src_st; if (stat(sources[i], &src_st) != 0) { return 0; /* Source missing — must build */ } if (src_st.st_mtime > out_st.st_mtime) { return 0; /* Source is newer — must rebuild */ } } return 1; /* All sources are older than output — skip */ } /* ── Main Build Pipeline ───────────────────────────────────────── */ int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } const char *base = argv[1]; /* e.g. /home/user/repo/arkos */ /* Derive the repo root (one level up from the arkos directory) */ char *repo = pjoin(base, ".."); /* Output and staging directories */ char *out_dir = pjoin(base, "finished"); char *staging = pjoin(base, "out_staging"); char *staging_sys = pjoin(staging, "system"); char *staging_fw = pjoin(staging, "system/frameworks"); char *staging_vend = pjoin(staging, "vendor"); char *staging_boot = pjoin(staging, "boot"); /* Source directories */ char *fw_dir = pjoin(base, "frameworks"); char *boot_dir = pjoin(base, "boot"); char *vendor_dir = pjoin(base, "vendor"); char *system_dir = pjoin(base, "system"); char *kernel_dir = pjoin(base, "kernel"); printf("\n\033[1;35m╔══════════════════════════════════════╗\033[0m\n"); printf("\033[1;35m║ ArkOS Build System v3.0 ║\033[0m\n"); printf("\033[1;35m╚══════════════════════════════════════╝\033[0m\n"); /* ─── Step 1: Clean previous output ────────────────────────── */ step("Cleaning previous output (preserving build caches)"); { char cmd[1024]; /* Preserve out_staging/ entirely for incremental builds. * Only wipe finished/ which contains the final disk images. */ snprintf(cmd, sizeof(cmd), "rm -rf %s", out_dir); run(cmd); } /* ─── Step 2: Create staging directories ───────────────────── */ step("Creating staging directories"); { char cmd[1024]; snprintf(cmd, sizeof(cmd), "mkdir -p %s %s %s %s %s/services", out_dir, staging_fw, staging_vend, staging_boot, staging_sys); run(cmd); } /* ─── Step 3: Stage frameworks ─────────────────────────────── */ step("Staging frameworks"); { DIR *d = opendir(fw_dir); if (d) { struct dirent *ent; while ((ent = readdir(d)) != NULL) { if (ent->d_name[0] == '.') continue; /* DRM framework is excluded from the base system image */ if (strcmp(ent->d_name, "DRM") == 0) { printf(" Skipping excluded framework: %s\n", ent->d_name); continue; } char cmd[1024]; snprintf(cmd, sizeof(cmd), "cp -ur %s/%s %s/", fw_dir, ent->d_name, staging_fw); run(cmd); } closedir(d); } } /* ─── Step 4: Stage boot assets ────────────────────────────── */ step("Staging boot assets"); { DIR *d = opendir(boot_dir); if (d) { struct dirent *ent; while ((ent = readdir(d)) != NULL) { if (ent->d_name[0] == '.') continue; char cmd[1024]; snprintf(cmd, sizeof(cmd), "cp -ur %s/%s %s/", boot_dir, ent->d_name, staging_boot); run(cmd); } closedir(d); } } /* ─── Step 5: Stage system services ────────────────────────── */ step("Staging system service configs"); { char cmd[1024]; snprintf(cmd, sizeof(cmd), "cp -ur %s/system/services/* %s/services/ 2>/dev/null || true", base, staging_sys); run(cmd); } /* ─── Step 6: Stage vendor files ───────────────────────────── */ step("Staging vendor files"); { char cmd[1024]; snprintf(cmd, sizeof(cmd), "cp -ur %s/* %s/ 2>/dev/null || true", vendor_dir, staging_vend); run(cmd); } /* ─── Step 7: Verify kernel configuration ──────────────────── */ step("Verifying kernel configuration"); { char *kconfig = pjoin(kernel_dir, ".config"); if (access(kconfig, F_OK) != -1) { printf(" \033[0;32m✓\033[0m Kernel .config present\n"); } else { printf(" \033[1;33m⚠ WARNING:\033[0m Kernel .config not found!\n"); } free(kconfig); } /* ─── Step 8: Compile fb_helper.c ──────────────────────────── */ step("Compiling fb_helper.c (framebuffer ioctl bridge)"); { char cmd[1024]; snprintf(cmd, sizeof(cmd), "make -C %s out_staging/fb_helper.o", base); run(cmd); } /* ─── Step 9: Compile Swift system binaries (incremental) ──── */ step("Compiling Swift system binaries (splash, arkrt, display daemon)"); { /* Check each binary individually for incremental builds */ char arkrt_out[512], daemon_out[512]; snprintf(arkrt_out, sizeof(arkrt_out), "%s/arkrt", staging); snprintf(daemon_out, sizeof(daemon_out), "%s/system/ui_daemon", staging); /* arkrt sources */ char a1[512], a2[512], a3[512], a4[512], a5[512], a6[512], a7[512]; snprintf(a1, sizeof(a1), "%s/arkrt/main.swift", base); snprintf(a2, sizeof(a2), "%s/arkrt/KernelBridge.swift", base); snprintf(a3, sizeof(a3), "%s/arkrt/CommandRouter.swift", base); snprintf(a4, sizeof(a4), "%s/arkrt/IPC.swift", base); snprintf(a5, sizeof(a5), "%s/arkrt/ServiceManager.swift", base); snprintf(a6, sizeof(a6), "%s/arkrt/NetworkService.swift", base); snprintf(a7, sizeof(a7), "%s/arkrt/InputService.swift", base); const char *arkrt_sources[] = {a1, a2, a3, a4, a5, a6, a7}; if (is_up_to_date(arkrt_out, arkrt_sources, 7)) { printf(" \033[0;32m✓\033[0m arkrt is up-to-date (skipping)\n"); } else { char cmd[1024]; snprintf(cmd, sizeof(cmd), "make -C %s out_staging/arkrt", base); run(cmd); } /* ui_daemon sources */ char d1[512], d2[512]; snprintf(d1, sizeof(d1), "%s/system/display/main.swift", base); snprintf(d2, sizeof(d2), "%s/system/core/fb_helper.c", base); const char *daemon_sources[] = {d1, d2}; if (is_up_to_date(daemon_out, daemon_sources, 2)) { printf(" \033[0;32m✓\033[0m ui_daemon is up-to-date (skipping)\n"); } else { char cmd[1024]; snprintf(cmd, sizeof(cmd), "make -C %s out_staging/system/ui_daemon", base); run(cmd); } } /* ─── Step 10: Build SwiftUI framework (SPM cached) ────────── */ step("Building SwiftUI framework (SPM incremental build)"); { /* * Uses --scratch-path to store SPM build artifacts in out_staging/.swift_build. * This directory is preserved across builds so SPM only recompiles changed * modules instead of all 44+ every time. First build is slow (~5 min), * subsequent builds with no changes complete in seconds. * * The 2>/dev/null suppresses SPM's verbose diagnostic output. * Errors in this step are non-fatal (|| true) since the UI framework * is not yet linked into the boot chain. */ char cmd[2048]; snprintf( cmd, sizeof(cmd), "cd %s/frameworks/SwiftUI && swift build -c release " "-j $(nproc) -Xswiftc -use-ld=lld " "--scratch-path %s/.swift_build " "-Xcc -I%s/frameworks/SwiftUI/Sources/SwiftCorelibs/include " "-Xcxx -I%s/frameworks/SwiftUI/Sources/SwiftCorelibs/include " "2>/dev/null || true", base, staging, base, base); run(cmd); } /* ─── Step 11: Compile ui_test (incremental) ───────────────── */ step("Compiling ui_test (setup app)"); { char ut_out[512]; snprintf(ut_out, sizeof(ut_out), "%s/system/ui_test", staging); char u1[512], u2[512], u3[512], u4[512], u5[512]; snprintf(u1, sizeof(u1), "%s/system/apps/ui_test.swift", base); snprintf(u2, sizeof(u2), "%s/frameworks/ArkGraphics/ArkGraphics.swift", base); snprintf(u3, sizeof(u3), "%s/frameworks/ArkGraphics/ArkWrite.swift", base); snprintf(u4, sizeof(u4), "%s/frameworks/ArkGraphics/ArkShapes.swift", base); snprintf(u5, sizeof(u5), "%s/frameworks/ArkGraphics/ArkFontRobotoBold.swift", base); const char *ut_sources[] = {u1, u2, u3, u4, u5}; if (is_up_to_date(ut_out, ut_sources, 5)) { printf(" \033[0;32m✓\033[0m ui_test is up-to-date (skipping)\n"); } else { char cmd[1024]; snprintf(cmd, sizeof(cmd), "make -C %s out_staging/system/ui_test", base); run(cmd); } } /* ─── Step 12: Sign arkrt (Verified Boot) ──────────────────── */ step("Signing arkrt binary (Verified Boot)"); { char cmd[1024]; snprintf(cmd, sizeof(cmd), "python3 %s/verify/sign.py %s/verify/securebuild.ark " "%s/arkrt %s/signature.bin", vendor_dir, vendor_dir, staging, staging); run(cmd); } /* ─── Step 13: Compile init (PID 1) ────────────────────────── */ step("Compiling init (PID 1 process)"); { char init_out[512]; snprintf(init_out, sizeof(init_out), "%s/init", staging); char i1[512], i2[512]; snprintf(i1, sizeof(i1), "%s/system/core/init.c", base); snprintf(i2, sizeof(i2), "%s/vendor/verify/sha256.c", base); const char *init_sources[] = {i1, i2}; if (is_up_to_date(init_out, init_sources, 2)) { printf(" \033[0;32m✓\033[0m init is up-to-date (skipping)\n"); } else { char cmd[1024]; snprintf(cmd, sizeof(cmd), "make -C %s out_staging/init", base); run(cmd); } } /* ─── Step 14: Pack initramfs ──────────────────────────────── */ step("Packing initramfs (base + runtime + display + UI)"); { char *initramfs_ext = pjoin(staging, "initramfs_ext"); char *base_initramfs = pjoin(boot_dir, "initramfs.img"); char cmd[2048]; /* Extract base initramfs (contains busybox, kernel modules, etc.) */ snprintf(cmd, sizeof(cmd), "rm -rf %s && mkdir -p %s", initramfs_ext, initramfs_ext); run(cmd); snprintf(cmd, sizeof(cmd), "cd %s && zcat %s | cpio -id --no-preserve-owner 2>/dev/null", initramfs_ext, base_initramfs); run(cmd); /* Create required directories in initramfs */ snprintf(cmd, sizeof(cmd), "mkdir -p %s/system/services %s/run %s/tmp %s/var/log", initramfs_ext, initramfs_ext, initramfs_ext, initramfs_ext); run(cmd); /* Copy compiled binaries into initramfs */ snprintf(cmd, sizeof(cmd), "cp %s/init %s/init && chmod +x %s/init", staging, initramfs_ext, initramfs_ext); run(cmd); snprintf(cmd, sizeof(cmd), "cp %s/arkrt %s/arkrt && chmod +x %s/arkrt", staging, initramfs_ext, initramfs_ext); run(cmd); snprintf(cmd, sizeof(cmd), "cp %s/signature.bin %s/signature.bin", staging, initramfs_ext); run(cmd); /* Copy display daemon and UI test app into /system/ */ snprintf(cmd, sizeof(cmd), "cp %s/system/ui_daemon %s/system/ui_daemon && " "chmod +x %s/system/ui_daemon 2>/dev/null || true", staging, initramfs_ext, initramfs_ext); run(cmd); snprintf(cmd, sizeof(cmd), "cp %s/system/ui_test %s/system/ui_test && " "chmod +x %s/system/ui_test 2>/dev/null || true", staging, initramfs_ext, initramfs_ext); run(cmd); /* Copy service definition files */ snprintf(cmd, sizeof(cmd), "cp -r %s/services/* %s/system/services/ 2>/dev/null || true", staging_sys, initramfs_ext); run(cmd); /* Repack initramfs as gzip-compressed cpio archive */ snprintf(cmd, sizeof(cmd), "cd %s && find . | cpio -H newc -o 2>/dev/null | gzip > %s/initramfs.img", initramfs_ext, out_dir); run(cmd); free(initramfs_ext); free(base_initramfs); } /* ─── Step 15: Assemble bootloader + stage2 ────────────────── */ step("Assembling bootloader"); { char cmd[1024]; /* Generate boot animation frames from Python script */ snprintf(cmd, sizeof(cmd), "python3 %s/source/animationframes/generate_frames.py %s", boot_dir, staging); run(cmd); snprintf(cmd, sizeof(cmd), "make -C %s out_staging/bootloader.bin out_staging/stage2.bin " "out_staging/BOOTX64.EFI", base); run(cmd); } /* ─── Step 16: Generate disk images ────────────────────────── */ step("Generating disk images"); { char cmd[1024]; /* boot.img — bootloader + kernel + initramfs packed by pack_boot.py */ snprintf(cmd, sizeof(cmd), "python3 %s/tools/pack_boot.py --base-dir %s", repo, base); run(cmd); /* sys.img — system partition (frameworks, apps, services) */ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s/sys.img bs=1M count=2048 2>/dev/null", out_dir); run(cmd); snprintf(cmd, sizeof(cmd), "mkfs.ext4 -d %s %s/sys.img 2>/dev/null", staging_sys, out_dir); run(cmd); /* vend.img — vendor partition (OEM blobs, keys) */ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s/vend.img bs=1M count=100 2>/dev/null", out_dir); run(cmd); snprintf(cmd, sizeof(cmd), "mkfs.ext4 -d %s %s/vend.img 2>/dev/null", staging_vend, out_dir); run(cmd); /* avb.img — Android Verified Boot metadata (only if signing keys exist) */ char *securebuild_path = pjoin(vendor_dir, "verify/securebuild.ark"); ArkConfig *sbc = ark_parse(securebuild_path); int has_keys = 0; for (int i = 0; i < sbc->standalone_count; i++) { if (strstr(sbc->standalone[i], "ARK-OS-") != NULL) { has_keys = 1; break; } } if (has_keys) { printf(" \033[0;32m✓\033[0m Signing keys found — generating avb.img\n"); snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s/avb.img bs=1M count=1 2>/dev/null", out_dir); run(cmd); } else { printf(" \033[1;33m⚠\033[0m No signing keys — skipping avb.img\n"); } ark_free(sbc); free(securebuild_path); /* vbmeta.img + dtbo.img — boot verification and device tree overlay stubs */ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s/vbmeta.img bs=1M count=1 2>/dev/null", out_dir); run(cmd); snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s/dtbo.img bs=1M count=1 2>/dev/null", out_dir); run(cmd); } printf("\n\033[1;32m╔══════════════════════════════════════╗\033[0m\n"); printf("\033[1;32m║ Build Complete! ║\033[0m\n"); printf("\033[1;32m╚══════════════════════════════════════╝\033[0m\n"); printf("Run \033[1mmake run\033[0m to launch ArkOS in QEMU.\n\n"); /* Free all heap-allocated path strings */ free(repo); free(out_dir); free(staging); free(staging_sys); free(staging_fw); free(staging_vend); free(staging_boot); free(fw_dir); free(boot_dir); free(vendor_dir); free(system_dir); free(kernel_dir); return 0; }