Files
Zag/build.zig
T
2026-04-17 20:35:44 -04:00

273 lines
9.0 KiB
Zig

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");
pub fn build(b: *std.Build) void {
const config_step = b.step("config", "Show the config menu");
config_step.dependOn(config.configStep(b));
// Parse the config
const cfg = config.parseConfig(b) catch |err| {
if (err == error.FileNotFound) {
b.default_step.dependOn(
&b.addFail("No .config found, run `zig build config` first").step,
);
} else {
b.default_step.dependOn(
&b.addFail("Failed to parse .config").step,
);
}
return;
};
// Generate the zig config file we can import in the code
const config_wf = config.generateConfig(b, cfg) catch {
b.default_step.dependOn(
&b.addFail("Failed to generate config.gen.zig").step,
);
return;
};
// Get the target platform (might be linux or freestanding)
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 = b.createModule(.{
.root_source_file = config_wf.getDirectory().path(b, "config.gen.zig"),
.optimize = optimize,
});
const ksyms_module = b.createModule(.{
.root_source_file = b.path("src/ksyms.zig"), // Use the static wrapper
.optimize = optimize,
});
// first pass
const empty_cmd = b.addSystemCommand(&.{ "python3", "build/ksyms.py", "--empty" });
empty_cmd.addArg("--output");
const empty_ksyms_s = empty_cmd.addOutputFileArg("ksyms_empty.s");
const rtl_module = b.createModule(.{
.root_source_file = b.path("src/rtl/root.zig"),
.optimize = optimize,
});
rtl_module.addImport("rtl", rtl_module);
const arch = if (plat.os == .freestanding) switch (plat.arch) {
.x86_64 => "amd64",
else => @tagName(plat.arch),
} else "um";
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 base_nosym = makeBaseModule(b, optimize, config_module, rtl_module, ksyms_module);
const kernel_nosym = addKernel(b, plat, optimize, config_module, ksyms_module, rtl_module, base_nosym, 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.addSystemCommand(&.{ "python3", "build/ksyms.py" });
ksyms.addArg("--input");
ksyms.step.dependOn(&kernel_nosym.step);
ksyms.addFileArg(kernel_nosym.getEmittedBin());
ksyms.addArg("--output");
const ksyms_s = ksyms.addOutputFileArg("ksyms.s");
const base_module = makeBaseModule(b, optimize, config_module, rtl_module, ksyms_module);
// second pass
const kernel = addKernel(b, plat, optimize, config_module, ksyms_module, rtl_module, base_module, arch_module);
kernel.root_module.addAssemblyFile(ksyms_s);
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 },
},
});
docs_module.addImport("base", docs_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, b.dependency("limine", .{}));
iso_step.dependOn(result.step);
run.addRun(b, kernel, 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 = if (plat.os == .freestanding) .none else .gnu,
.cpu_model = .baseline,
};
if (plat.os == .freestanding) {
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 makeBaseModule(
b: *std.Build,
optimize: std.builtin.OptimizeMode,
config_module: *std.Build.Module,
rtl_module: *std.Build.Module,
ksyms_module: *std.Build.Module,
) *std.Build.Module {
const base = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.imports = &.{
.{ .name = "config", .module = config_module },
.{ .name = "rtl", .module = rtl_module },
.{ .name = "ksyms", .module = ksyms_module },
},
.optimize = optimize,
});
base.addImport("base", base);
return base;
}
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, base: *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 platform_name = if (plat.bootloader) |bl|
switch (bl) {
.Limine => "boot/limine",
}
else if (plat.os == .linux) "platform/um" else @tagName(plat.arch);
base.addImport("arch", arch);
const root_source_file = b.fmt("src/{s}/entry.zig", .{platform_name});
const kernel = b.addExecutable(.{
.name = name,
.root_module = b.createModule(.{
.root_source_file = b.path(root_source_file),
.target = target,
.optimize = optimize,
}),
});
base.addImport("ksyms", ksyms_module);
kernel.root_module.addImport("config", config_module);
kernel.root_module.addImport("ksyms", ksyms_module);
kernel.root_module.addImport("base", base);
kernel.root_module.addImport("rtl", rtl);
kernel.use_llvm = true;
kernel.use_lld = true;
kernel.root_module.omit_frame_pointer = false;
if (plat.os == .freestanding) {
kernel.linkage = .static;
kernel.pie = false;
kernel.root_module.pic = false;
kernel.root_module.strip = false;
kernel.root_module.stack_check = false;
kernel.root_module.stack_protector = false;
kernel.root_module.unwind_tables = .none;
kernel.entry = .{ .symbol_name = "kmain" };
kernel.root_module.code_model = switch (plat.arch) {
.x86_64 => .kernel,
.riscv64 => .medium,
else => .large,
};
switch (plat.arch) {
.x86_64 => kernel.root_module.red_zone = false,
else => {},
}
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"));
},
else => {},
}
kernel.linker_script = b.path(b.fmt("build/linker-scripts/{s}.lds", .{@tagName(plat.arch)}));
} else {
kernel.linker_script = b.path("build/linker-scripts/uml.lds");
kernel.root_module.link_libc = true;
kernel.linker_allow_shlib_undefined = true;
kernel.root_module.linkSystemLibrary("sdl2-compat", .{ .use_pkg_config = .force });
kernel.root_module.addLibraryPath(.{ .cwd_relative = "/usr/lib64" });
}
return kernel;
}