const std = @import("std"); const builtin = @import("builtin"); const config = @import("build/config.zig"); const image = @import("build/image.zig"); const run = @import("build/run.zig"); const zonfig = @import("zonfig"); pub fn build(b: *std.Build) void { const zonfig_dep = b.dependency("zonfig", .{}); zonfig.addConfigStep(b, zonfig_dep, "config.zig.zon", ".config.zig.zon"); // Parse the config or fail if file is not present const cfg = config.parseConfig(b) catch |err| { b.default_step.dependOn( &b.addFail(b.fmt("Failed to parse config, have you run `zig build config`? (Error: {any})", .{err})).step, ); return; }; // Get the target platform const plat = config.getPlatform(cfg) catch { b.default_step.dependOn( &b.addFail("Invalid platform (this should not happen!)").step, ); return; }; const optimize = b.standardOptimizeOption(.{}); const config_module = zonfig.createConfigModule(b, zonfig_dep, "config.zig.zon", ".config.zig.zon"); const ksyms_module = b.createModule(.{ .root_source_file = b.path("src/ksyms.zig"), .optimize = optimize, }); const ksyms_exe = b.addExecutable(.{ .name = "ksyms", .root_module = b.createModule(.{ .root_source_file = b.path("build/ksyms.zig"), .target = b.graph.host, }), }); const empty_cmd = b.addRunArtifact(ksyms_exe); const empty_ksyms_s = empty_cmd.addOutputFileArg("ksyms_empty.s"); empty_cmd.stdio = .inherit; const rtl_module = b.createModule(.{ .root_source_file = b.path("src/rtl/root.zig"), .optimize = optimize, }); rtl_module.addImport("rtl", rtl_module); const arch = switch (plat.arch) { .x86_64 => "amd64", else => @tagName(plat.arch), }; const arch_module = b.createModule(.{ .root_source_file = b.path(b.fmt("src/arch/{s}/root.zig", .{arch})), .optimize = optimize, .imports = &.{ .{ .name = "config", .module = config_module }, .{ .name = "rtl", .module = rtl_module }, }, }); const kernel_nosym = addKernel(b, plat, optimize, config_module, ksyms_module, rtl_module, arch_module); kernel_nosym.root_module.addAssemblyFile(empty_ksyms_s); b.step("kernel-nosym", "Build nosym kernel").dependOn(&kernel_nosym.step); // generate symbols const ksyms = b.addRunArtifact(ksyms_exe); ksyms.step.dependOn(&kernel_nosym.step); const ksyms_s = ksyms.addOutputFileArg("ksyms.s"); ksyms.addFileArg(kernel_nosym.getEmittedBin()); // second pass const kernel = addKernel(b, plat, optimize, config_module, ksyms_module, rtl_module, arch_module); kernel.root_module.addAssemblyFile(ksyms_s); const loader = addLoader(b, plat, optimize, config_module, rtl_module, arch_module); b.default_step.dependOn(&b.addInstallArtifact(loader, .{}).step); b.default_step.dependOn(&b.addInstallArtifact(kernel, .{}).step); const docs_module = b.createModule(.{ .root_source_file = b.path("src/root.zig"), .target = b.graph.host, .imports = &.{ .{ .name = "config", .module = config_module }, .{ .name = "ksyms", .module = ksyms_module }, .{ .name = "rtl", .module = rtl_module }, .{ .name = "arch", .module = arch_module }, }, }); const docs_obj = b.addObject(.{ .name = "kernel_docs", .root_module = docs_module, }); const install_docs = b.addInstallDirectory(.{ .source_dir = docs_obj.getEmittedDocs(), .install_dir = .prefix, .install_subdir = "docs", }); const docs_step = b.step("docs", "Generate and install documentation"); docs_step.dependOn(&install_docs.step); const iso_step = b.step("iso", "Build the ISO image"); const result = image.addIso(b, plat.arch, "myos", kernel, loader, b.dependency("limine", .{})); iso_step.dependOn(result.step); run.addRun(b, kernel, loader, plat); const rtl_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/rtl/root.zig"), .target = b.graph.host, .optimize = optimize, }), }); rtl_tests.root_module.addImport("rtl", rtl_tests.root_module); const run_tests = b.addRunArtifact(rtl_tests); const test_step = b.step("test", "Run tests"); test_step.dependOn(&run_tests.step); } fn targetQueryForPlatform(plat: config.Platform) std.Target.Query { var q: std.Target.Query = .{ .cpu_arch = plat.arch, .os_tag = plat.os, .abi = .none, .cpu_model = .baseline, }; switch (plat.arch) { .x86_64 => { const f = std.Target.Cpu.Feature.FeatureSetFns(std.Target.x86.Feature); q.cpu_features_add = f.featureSet(&.{.soft_float}); q.cpu_features_sub = f.featureSet(&.{ .x87, .mmx, .sse, .sse2 }); }, .aarch64 => { const f = std.Target.Cpu.Feature.FeatureSetFns(std.Target.aarch64.Feature); q.cpu_features_sub = f.featureSet(&.{ .fp_armv8, .neon }); }, .riscv64 => {}, else => {}, } return q; } fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.OptimizeMode, config_module: *std.Build.Module, ksyms_module: *std.Build.Module, rtl: *std.Build.Module, arch: *std.Build.Module) *std.Build.Step.Compile { const target = b.resolveTargetQuery(targetQueryForPlatform(plat)); const name = b.fmt("kernel-{s}", .{@tagName(plat.arch)}); const kernel = b.addExecutable(.{ .name = name, .root_module = b.createModule(.{ .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, }), }); kernel.root_module.addImport("arch", arch); kernel.root_module.addImport("config", config_module); kernel.root_module.addImport("ksyms", ksyms_module); kernel.root_module.addImport("rtl", rtl); kernel.link_gc_sections = false; kernel.use_llvm = true; kernel.use_lld = true; kernel.root_module.omit_frame_pointer = false; const modules: []const *std.Build.Module = &.{ config_module, ksyms_module, rtl, arch, kernel.root_module }; kernel.linkage = .static; kernel.pie = false; for (modules) |m| { m.pic = false; m.strip = false; m.stack_check = false; m.stack_protector = false; m.unwind_tables = .none; m.omit_frame_pointer = false; m.code_model = switch (plat.arch) { .x86_64 => .kernel, .riscv64 => .medium, else => .large, }; switch (plat.arch) { .x86_64 => { m.red_zone = false; }, else => {}, } } kernel.entry = .{ .symbol_name = "kmain" }; switch (plat.arch) { .x86_64 => { kernel.root_module.addAssemblyFile(b.path("src/kern/amd64/locore.s")); kernel.root_module.addAssemblyFile(b.path("src/platform/pc/ap.s")); }, .riscv64 => { kernel.root_module.addAssemblyFile(b.path("src/kern/riscv64/locore.s")); kernel.root_module.addAssemblyFile(b.path("src/platform/riscv64/trap.s")); }, else => {}, } kernel.linker_script = b.path(b.fmt("build/linker-scripts/kernel/{s}.lds", .{@tagName(plat.arch)})); return kernel; } fn addLoader(b: *std.Build, plat: config.Platform, optimize: std.builtin.OptimizeMode, config_module: *std.Build.Module, rtl: *std.Build.Module, arch_module: *std.Build.Module) *std.Build.Step.Compile { const target = b.resolveTargetQuery(targetQueryForPlatform(plat)); const name = b.fmt("loader-{s}-{s}", .{ @tagName(plat.arch), @tagName(plat.bootloader) }); const loader = b.addExecutable(.{ .name = name, .root_module = b.createModule(.{ .root_source_file = b.path("src/loader/root.zig"), .target = target, .optimize = optimize, }), }); loader.root_module.addImport("arch", arch_module); loader.root_module.addImport("config", config_module); loader.root_module.addImport("rtl", rtl); switch (plat.arch) { .x86_64 => loader.root_module.addAssemblyFile(b.path("src/loader/x86_64/jump.s")), .riscv64 => loader.root_module.addAssemblyFile(b.path("src/loader/riscv64/jump.s")), else => {}, } loader.use_llvm = true; loader.use_lld = true; loader.root_module.omit_frame_pointer = false; loader.entry = .{ .symbol_name = "loader_entry" }; const modules: []const *std.Build.Module = &.{ arch_module, config_module, rtl, loader.root_module }; loader.linkage = .static; loader.pie = false; for (modules) |m| { m.pic = false; m.strip = false; m.stack_check = false; m.stack_protector = false; m.unwind_tables = .none; m.omit_frame_pointer = false; m.code_model = switch (plat.arch) { .x86_64 => .kernel, .riscv64 => .medium, else => .large, }; switch (plat.arch) { .x86_64 => { m.red_zone = false; }, else => {}, } } loader.linker_script = b.path(b.fmt("build/linker-scripts/loader/{s}.lds", .{@tagName(plat.arch)})); return loader; }