762 lines
34 KiB
Zig
762 lines
34 KiB
Zig
const std = @import("std");
|
|
const common = @import("src/common.zig");
|
|
const os = common.os;
|
|
|
|
// Build types
|
|
const Build = std.Build;
|
|
const CompileStep = std.Build.CompileStep;
|
|
const FileSource = std.Build.FileSource;
|
|
const Module = std.Build.Module;
|
|
const ModuleDependency = std.Build.ModuleDependency;
|
|
const OptionsStep = std.Build.OptionsStep;
|
|
const RunStep = std.Build.RunStep;
|
|
const Step = std.Build.Step;
|
|
|
|
const assert = std.debug.assert;
|
|
const Bootloader = common.Bootloader;
|
|
const Configuration = common.Configuration;
|
|
const Cpu = common.Cpu;
|
|
const CrossTarget = common.CrossTarget;
|
|
const DiskType = common.DiskType;
|
|
const ExecutionType = common.ExecutionType;
|
|
const ExecutionEnvironment = common.ExecutionEnvironment;
|
|
const FilesystemType = common.FilesystemType;
|
|
const OptimizeMode = common.OptimizeMode;
|
|
const QEMUOptions = common.QEMUOptions;
|
|
const BirthProgram = common.BirthProgram;
|
|
const Suffix = common.Suffix;
|
|
const Target = common.Target;
|
|
|
|
const Error = error{
|
|
not_implemented,
|
|
architecture_not_supported,
|
|
failed_to_run,
|
|
};
|
|
|
|
const source_root_dir = "src";
|
|
const user_program_dir_path = "src/user/programs";
|
|
|
|
var ci = false;
|
|
var ci_native = false;
|
|
var debug_user = false;
|
|
var debug_loader = false;
|
|
var modules = Modules{};
|
|
var b: *Build = undefined;
|
|
var build_steps: *BuildSteps = undefined;
|
|
var default_configuration: Configuration = undefined;
|
|
var user_modules: []const common.Module = undefined;
|
|
var options = Options{};
|
|
|
|
pub fn build(b_arg: *Build) !void {
|
|
b = b_arg;
|
|
ci = b.option(bool, "ci", "CI mode") orelse false;
|
|
ci_native = b.option(bool, "ci_native", "CI mode in self-hosted runner") orelse false;
|
|
debug_user = b.option(bool, "debug_user", "Debug user program") orelse false;
|
|
debug_loader = b.option(bool, "debug_loader", "Debug loader program") orelse false;
|
|
const default_cfg_override = b.option([]const u8, "default", "Default configuration JSON file") orelse "config/default.json";
|
|
modules = blk: {
|
|
var mods = Modules{};
|
|
inline for (comptime common.enumValues(ModuleID)) |module_id| {
|
|
mods.modules.set(module_id, b.createModule(.{
|
|
.source_file = FileSource.relative(switch (module_id) {
|
|
.limine_installer => "src/bootloader/limine/installer.zig",
|
|
else => switch (module_id) {
|
|
.bios, .uefi, .limine => "src/bootloader",
|
|
else => "src",
|
|
} ++ "/" ++ @tagName(module_id) ++ ".zig",
|
|
}),
|
|
}));
|
|
}
|
|
|
|
try mods.setDependencies(.lib, &.{});
|
|
try mods.setDependencies(.host, &.{.lib});
|
|
try mods.setDependencies(.bootloader, &.{ .lib, .privileged });
|
|
try mods.setDependencies(.bios, &.{ .lib, .privileged });
|
|
try mods.setDependencies(.limine, &.{ .lib, .privileged });
|
|
try mods.setDependencies(.uefi, &.{ .lib, .privileged });
|
|
try mods.setDependencies(.limine_installer, &.{ .lib, .privileged });
|
|
try mods.setDependencies(.privileged, &.{ .lib, .bootloader });
|
|
try mods.setDependencies(.cpu, &.{ .privileged, .lib, .bootloader, .birth });
|
|
try mods.setDependencies(.birth, &.{.lib});
|
|
try mods.setDependencies(.user, &.{ .lib, .birth });
|
|
|
|
break :blk mods;
|
|
};
|
|
|
|
options = blk: {
|
|
var opts = Options{};
|
|
opts.createOption(.bootloader);
|
|
opts.createOption(.cpu);
|
|
opts.createOption(.user);
|
|
opts.createOption(.host);
|
|
break :blk opts;
|
|
};
|
|
|
|
default_configuration = blk: {
|
|
const default_json_file = try std.fs.cwd().readFileAlloc(b.allocator, default_cfg_override, common.maxInt(usize));
|
|
const parsed_cfg = try std.json.parseFromSlice(Configuration, b.allocator, default_json_file, .{});
|
|
const cfg = parsed_cfg.value;
|
|
|
|
const optimize_mode = b.option(
|
|
std.builtin.Mode,
|
|
"optimize",
|
|
"Prioritize performance, safety, or binary size (-O flag)",
|
|
) orelse cfg.optimize_mode;
|
|
|
|
break :blk Configuration{
|
|
.architecture = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = cfg.architecture } }).getCpuArch(),
|
|
.bootloader = cfg.bootloader,
|
|
.boot_protocol = cfg.boot_protocol,
|
|
.execution_environment = cfg.execution_environment,
|
|
.optimize_mode = optimize_mode,
|
|
.execution_type = cfg.execution_type,
|
|
.executable_kind = .exe,
|
|
};
|
|
};
|
|
|
|
build_steps = try b.allocator.create(BuildSteps);
|
|
build_steps.* = .{
|
|
.build_all = b.step("all", "Build all the artifacts"),
|
|
.build_all_tests = b.step("all_tests", "Build all the artifacts related to tests"),
|
|
.run = b.step("run", "Run the operating system through an emulator"),
|
|
.debug = b.step("debug", "Debug the operating system through an emulator"),
|
|
.test_run = b.step("test", "Run unit tests"),
|
|
.test_debug = b.step("test_debug", "Debug unit tests"),
|
|
.test_all = b.step("test_all", "Run all unit tests"),
|
|
.test_host = b.step("test_host", "Run host unit tests"),
|
|
};
|
|
|
|
const disk_image_builder_modules = &.{ .lib, .host, .bootloader, .limine_installer, .bios };
|
|
const disk_image_root_path = "src/host/disk_image_builder";
|
|
const disk_image_builder = blk: {
|
|
const exe = try addCompileStep(.{
|
|
.kind = .exe,
|
|
.name = "disk_image_builder",
|
|
.root_project_path = disk_image_root_path,
|
|
.modules = disk_image_builder_modules,
|
|
});
|
|
|
|
b.default_step.dependOn(&exe.step);
|
|
|
|
break :blk exe;
|
|
};
|
|
|
|
const runner = blk: {
|
|
const exe = try addCompileStep(.{
|
|
.kind = .exe,
|
|
.name = "runner",
|
|
.root_project_path = "src/host/runner",
|
|
.modules = &.{ .lib, .host },
|
|
});
|
|
|
|
b.default_step.dependOn(&exe.step);
|
|
|
|
break :blk exe;
|
|
};
|
|
|
|
const native_tests = [_]struct {
|
|
name: []const u8,
|
|
root_project_path: []const u8,
|
|
modules: []const ModuleID,
|
|
c: ?C = null,
|
|
run_native: bool = true,
|
|
|
|
const C = struct {
|
|
include_paths: []const []const u8,
|
|
source_files: []const SourceFile,
|
|
link_libc: bool,
|
|
link_libcpp: bool,
|
|
|
|
const SourceFile = struct {
|
|
path: []const u8,
|
|
flags: []const []const u8,
|
|
};
|
|
};
|
|
}{
|
|
.{
|
|
.name = "host_native_test",
|
|
.root_project_path = "src/host",
|
|
.modules = &.{ .lib, .host },
|
|
},
|
|
.{
|
|
.name = "disk_image_builder_native_test",
|
|
.root_project_path = disk_image_root_path,
|
|
.modules = disk_image_builder_modules,
|
|
.c = .{
|
|
.include_paths = &.{"src/bootloader/limine/installables"},
|
|
.source_files = &.{
|
|
.{
|
|
.path = "src/bootloader/limine/installables/limine-deploy.c",
|
|
.flags = &.{},
|
|
},
|
|
},
|
|
.link_libc = true,
|
|
.link_libcpp = false,
|
|
},
|
|
// Skip it because it requires sudo privileges
|
|
.run_native = false,
|
|
},
|
|
};
|
|
|
|
const native_test_optimize_mode = .ReleaseFast;
|
|
for (native_tests) |native_test| {
|
|
const test_name = try std.mem.concat(b.allocator, u8, &.{ native_test.name, "_", @tagName(native_test_optimize_mode) });
|
|
const test_exe = try addCompileStep(.{
|
|
.name = test_name,
|
|
.root_project_path = native_test.root_project_path,
|
|
.optimize_mode = native_test_optimize_mode,
|
|
.modules = native_test.modules,
|
|
.kind = .@"test",
|
|
});
|
|
|
|
if (native_test.c) |c| {
|
|
for (c.include_paths) |include_path| {
|
|
test_exe.addIncludePath(include_path);
|
|
}
|
|
|
|
for (c.source_files) |source_file| {
|
|
test_exe.addCSourceFile(source_file.path, source_file.flags);
|
|
}
|
|
|
|
if (c.link_libc) {
|
|
test_exe.linkLibC();
|
|
}
|
|
|
|
if (c.link_libcpp) {
|
|
test_exe.linkLibCpp();
|
|
}
|
|
}
|
|
|
|
//run_test_step.condition = .always;
|
|
const should_run = !ci_native or (ci_native and native_test.run_native);
|
|
if (should_run) {
|
|
const run_test_step = b.addRunArtifact(test_exe);
|
|
build_steps.test_all.dependOn(&run_test_step.step);
|
|
build_steps.test_host.dependOn(&run_test_step.step);
|
|
}
|
|
}
|
|
|
|
const ovmf_downloader = try addCompileStep(.{
|
|
.name = "ovmf_downloader",
|
|
.root_project_path = "src/host/ovmf_downloader",
|
|
.optimize_mode = .Debug,
|
|
.modules = &.{ .lib, .host },
|
|
.kind = .exe,
|
|
});
|
|
const ovmf_downloader_run_step = b.addRunArtifact(ovmf_downloader);
|
|
const ovmf_path = ovmf_downloader_run_step.addOutputFileArg("OVMF.fd");
|
|
|
|
{
|
|
var user_module_list = std.ArrayList(common.Module).init(b.allocator);
|
|
var user_program_dir = try std.fs.cwd().openIterableDir(user_program_dir_path, .{ .access_sub_paths = true });
|
|
defer user_program_dir.close();
|
|
|
|
var user_program_iterator = user_program_dir.iterate();
|
|
|
|
while (try user_program_iterator.next()) |entry| {
|
|
const dir_name = entry.name;
|
|
const file_path = try std.mem.concat(b.allocator, u8, &.{ dir_name, "/module.json" });
|
|
const file = try user_program_dir.dir.readFileAlloc(b.allocator, file_path, common.maxInt(usize));
|
|
const parsed_user_program = try std.json.parseFromSlice(common.UserProgram, b.allocator, file, .{});
|
|
const user_program = parsed_user_program.value;
|
|
try user_module_list.append(.{
|
|
.program = user_program,
|
|
.name = b.dupe(dir_name), // we have to dupe here otherwise Windows CI fails
|
|
});
|
|
}
|
|
|
|
user_modules = user_module_list.items;
|
|
}
|
|
|
|
const executable_kinds = [2]CompileStep.Kind{ .exe, .@"test" };
|
|
|
|
for (common.enumValues(OptimizeMode)) |optimize_mode| {
|
|
for (common.supported_architectures, 0..) |architecture, architecture_index| {
|
|
const user_target = try getTarget(architecture, .user);
|
|
|
|
for (executable_kinds) |executable_kind| {
|
|
const is_test = executable_kind == .@"test";
|
|
const cpu_driver_path = "src/cpu";
|
|
const target = try getTarget(architecture, .privileged);
|
|
const cpu_driver = try addCompileStep(.{
|
|
.kind = executable_kind,
|
|
.name = "cpu_driver",
|
|
.root_project_path = cpu_driver_path,
|
|
.target = target,
|
|
.optimize_mode = optimize_mode,
|
|
.modules = &.{ .lib, .bootloader, .privileged, .cpu, .birth },
|
|
});
|
|
|
|
cpu_driver.force_pic = true;
|
|
cpu_driver.disable_stack_probing = true;
|
|
cpu_driver.stack_protector = false;
|
|
cpu_driver.strip = false;
|
|
cpu_driver.red_zone = false;
|
|
cpu_driver.omit_frame_pointer = false;
|
|
|
|
cpu_driver.code_model = switch (architecture) {
|
|
.x86_64 => .kernel,
|
|
.riscv64 => .medium,
|
|
.aarch64 => .small,
|
|
else => return Error.architecture_not_supported,
|
|
};
|
|
|
|
const cpu_driver_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ cpu_driver_path, "/arch/", switch (architecture) {
|
|
.x86_64 => "x86/64",
|
|
.x86 => "x86/32",
|
|
else => @tagName(architecture),
|
|
}, "/linker_script.ld" }));
|
|
|
|
cpu_driver.setLinkerScriptPath(cpu_driver_linker_script_path);
|
|
|
|
var user_module_list = try std.ArrayList(*CompileStep).initCapacity(b.allocator, user_modules.len);
|
|
const user_architecture_source_path = try std.mem.concat(b.allocator, u8, &.{ "src/user/arch/", @tagName(architecture), "/" });
|
|
const user_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ user_architecture_source_path, "linker_script.ld" }));
|
|
for (user_modules) |module| {
|
|
const user_module = try addCompileStep(.{
|
|
.kind = executable_kind,
|
|
.name = module.name,
|
|
.root_project_path = try std.mem.concat(b.allocator, u8, &.{ user_program_dir_path, "/", module.name }),
|
|
.target = user_target,
|
|
.optimize_mode = optimize_mode,
|
|
.modules = &.{ .lib, .user, .birth },
|
|
});
|
|
user_module.strip = false;
|
|
|
|
user_module.setLinkerScriptPath(user_linker_script_path);
|
|
|
|
user_module_list.appendAssumeCapacity(user_module);
|
|
}
|
|
|
|
const bootloaders = common.architecture_bootloader_map[architecture_index];
|
|
for (bootloaders) |bootloader_struct| {
|
|
const bootloader = bootloader_struct.id;
|
|
for (bootloader_struct.protocols) |boot_protocol| {
|
|
const birth_loader_path = "src/bootloader/birth/";
|
|
const limine_loader_path = "src/bootloader/limine/";
|
|
const bootloader_name = "loader";
|
|
const bootloader_modules = [_]ModuleID{ .lib, .bootloader, .privileged };
|
|
|
|
const bootloader_compile_step = switch (bootloader) {
|
|
.birth => switch (boot_protocol) {
|
|
.bios => switch (architecture) {
|
|
.x86_64 => blk: {
|
|
const bootloader_path = birth_loader_path ++ "bios";
|
|
const executable = try addCompileStep(.{
|
|
.kind = executable_kind,
|
|
.name = bootloader_name,
|
|
.root_project_path = bootloader_path,
|
|
.target = try getTarget(.x86, .privileged),
|
|
.optimize_mode = .ReleaseSmall,
|
|
.modules = &(bootloader_modules ++ .{.bios}),
|
|
});
|
|
|
|
executable.strip = true;
|
|
|
|
executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S");
|
|
executable.addAssemblyFile(bootloader_path ++ "/unreal_mode.S");
|
|
executable.setLinkerScriptPath(FileSource.relative(bootloader_path ++ "/linker_script.ld"));
|
|
executable.code_model = .small;
|
|
|
|
break :blk executable;
|
|
},
|
|
else => return Error.architecture_not_supported,
|
|
},
|
|
.uefi => blk: {
|
|
const bootloader_path = birth_loader_path ++ "uefi";
|
|
const executable = try addCompileStep(.{
|
|
.kind = executable_kind,
|
|
.name = bootloader_name,
|
|
.root_project_path = bootloader_path,
|
|
.target = .{
|
|
.cpu_arch = architecture,
|
|
.os_tag = .uefi,
|
|
.abi = .msvc,
|
|
},
|
|
.optimize_mode = .ReleaseSafe,
|
|
.modules = &(bootloader_modules ++ .{.uefi}),
|
|
});
|
|
|
|
executable.strip = true;
|
|
|
|
switch (architecture) {
|
|
.x86_64 => executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S"),
|
|
else => {},
|
|
}
|
|
|
|
break :blk executable;
|
|
},
|
|
},
|
|
.limine => blk: {
|
|
const bootloader_path = limine_loader_path;
|
|
const executable = try addCompileStep(.{
|
|
.kind = executable_kind,
|
|
.name = bootloader_name,
|
|
.root_project_path = bootloader_path,
|
|
.target = target,
|
|
.optimize_mode = .ReleaseSafe,
|
|
.modules = &(bootloader_modules ++ .{.limine}),
|
|
});
|
|
|
|
executable.force_pic = true;
|
|
executable.omit_frame_pointer = false;
|
|
executable.want_lto = false;
|
|
executable.strip = false;
|
|
|
|
executable.code_model = cpu_driver.code_model;
|
|
|
|
executable.setLinkerScriptPath(FileSource.relative(try common.concat(b.allocator, u8, &.{ limine_loader_path ++ "arch/", @tagName(architecture), "/linker_script.ld" })));
|
|
|
|
break :blk executable;
|
|
},
|
|
};
|
|
|
|
bootloader_compile_step.disable_stack_probing = true;
|
|
bootloader_compile_step.stack_protector = false;
|
|
bootloader_compile_step.red_zone = false;
|
|
|
|
if (architecture == default_configuration.architecture and bootloader == default_configuration.bootloader and boot_protocol == default_configuration.boot_protocol and optimize_mode == default_configuration.optimize_mode and !is_test) {
|
|
addObjdump(bootloader_compile_step, bootloader_name);
|
|
addFileSize(bootloader_compile_step, bootloader_name);
|
|
}
|
|
|
|
const execution_environments: []const ExecutionEnvironment = switch (bootloader) {
|
|
.birth, .limine => switch (boot_protocol) {
|
|
.bios => switch (architecture) {
|
|
.x86_64 => &.{.qemu},
|
|
else => return Error.architecture_not_supported,
|
|
},
|
|
.uefi => &.{.qemu},
|
|
},
|
|
};
|
|
|
|
const execution_types: []const ExecutionType =
|
|
switch (common.canVirtualizeWithQEMU(architecture, ci)) {
|
|
true => &.{ .emulated, .accelerated },
|
|
false => &.{.emulated},
|
|
};
|
|
|
|
for (execution_types) |execution_type| {
|
|
for (execution_environments) |execution_environment| {
|
|
const configuration = Configuration{
|
|
.architecture = architecture,
|
|
.bootloader = bootloader,
|
|
.boot_protocol = boot_protocol,
|
|
.optimize_mode = optimize_mode,
|
|
.execution_environment = execution_environment,
|
|
.execution_type = execution_type,
|
|
.executable_kind = executable_kind,
|
|
};
|
|
|
|
var disk_argument_parser = common.ArgumentParser.DiskImageBuilder{};
|
|
const disk_image_builder_run = b.addRunArtifact(disk_image_builder);
|
|
const disk_image_path = disk_image_builder_run.addOutputFileArg("disk.hdd");
|
|
|
|
while (disk_argument_parser.next()) |argument_type| switch (argument_type) {
|
|
.configuration => inline for (common.fields(Configuration)) |field| disk_image_builder_run.addArg(@tagName(@field(configuration, field.name))),
|
|
.image_configuration_path => disk_image_builder_run.addArg(common.ImageConfig.default_path),
|
|
.disk_image_path => {
|
|
// Must be first
|
|
assert(@intFromEnum(argument_type) == 0);
|
|
},
|
|
.bootloader => {
|
|
disk_image_builder_run.addArtifactArg(bootloader_compile_step);
|
|
},
|
|
.cpu => disk_image_builder_run.addArtifactArg(cpu_driver),
|
|
.user_programs => for (user_module_list.items) |user_module| disk_image_builder_run.addArtifactArg(user_module),
|
|
};
|
|
|
|
const user_init = user_module_list.items[0];
|
|
|
|
const is_default = architecture == default_configuration.architecture and bootloader == default_configuration.bootloader and boot_protocol == default_configuration.boot_protocol and optimize_mode == default_configuration.optimize_mode and execution_environment == default_configuration.execution_environment and execution_type == default_configuration.execution_type;
|
|
|
|
const runner_run = try newRunnerRunArtifact(.{
|
|
.configuration = configuration,
|
|
.disk_image_path = disk_image_path,
|
|
.cpu_driver = cpu_driver,
|
|
.loader = bootloader_compile_step,
|
|
.user_init = user_init,
|
|
.runner = runner,
|
|
.qemu_options = .{
|
|
.is_debug = false,
|
|
.is_test = is_test,
|
|
},
|
|
.ovmf_path = ovmf_path,
|
|
.is_default = is_default,
|
|
});
|
|
|
|
const runner_debug = try newRunnerRunArtifact(.{
|
|
.configuration = configuration,
|
|
.disk_image_path = disk_image_path,
|
|
.cpu_driver = cpu_driver,
|
|
.loader = bootloader_compile_step,
|
|
.user_init = user_init,
|
|
.runner = runner,
|
|
.qemu_options = .{
|
|
.is_debug = true,
|
|
.is_test = is_test,
|
|
},
|
|
.ovmf_path = ovmf_path,
|
|
.is_default = is_default,
|
|
});
|
|
|
|
if (is_test) {
|
|
build_steps.test_all.dependOn(&runner_run.step);
|
|
}
|
|
|
|
if (is_default) {
|
|
if (is_test) {
|
|
build_steps.test_run.dependOn(&runner_run.step);
|
|
build_steps.test_debug.dependOn(&runner_debug.step);
|
|
} else {
|
|
build_steps.run.dependOn(&runner_run.step);
|
|
build_steps.debug.dependOn(&runner_debug.step);
|
|
|
|
b.default_step.dependOn(&bootloader_compile_step.step);
|
|
|
|
b.default_step.dependOn(&cpu_driver.step);
|
|
|
|
for (user_module_list.items) |user_module| {
|
|
b.default_step.dependOn(&user_module.step);
|
|
}
|
|
|
|
const artifacts: []const *CompileStep = &.{ cpu_driver, user_init };
|
|
const artifact_names: []const []const u8 = &.{ "cpu", "init" };
|
|
|
|
inline for (artifact_names, 0..) |artifact_name, index| {
|
|
const artifact = artifacts[index];
|
|
addObjdump(artifact, artifact_name);
|
|
addFileSize(artifact, artifact_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const Options = struct {
|
|
arr: std.EnumArray(BirthProgram, *OptionsStep) = std.EnumArray(BirthProgram, *OptionsStep).initUndefined(),
|
|
|
|
pub fn createOption(options_struct: *Options, birth_program: BirthProgram) void {
|
|
const new_options = b.addOptions();
|
|
new_options.addOption(BirthProgram, "program_type", birth_program);
|
|
options_struct.arr.set(birth_program, new_options);
|
|
}
|
|
};
|
|
|
|
const BuildSteps = struct {
|
|
build_all: *Step,
|
|
build_all_tests: *Step,
|
|
debug: *Step,
|
|
run: *Step,
|
|
test_run: *Step,
|
|
test_debug: *Step,
|
|
test_all: *Step,
|
|
test_host: *Step,
|
|
};
|
|
|
|
fn addObjdump(artifact: *CompileStep, comptime name: []const u8) void {
|
|
switch (os) {
|
|
.linux, .macos => {
|
|
const objdump = b.addSystemCommand(&.{ "objdump", "-dxS", "-Mintel" });
|
|
objdump.addArtifactArg(artifact);
|
|
const objdump_step = b.step("objdump_" ++ name, "Objdump " ++ name);
|
|
objdump_step.dependOn(&objdump.step);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
fn addFileSize(artifact: *CompileStep, comptime name: []const u8) void {
|
|
switch (os) {
|
|
.linux, .macos => {
|
|
const file_size = b.addSystemCommand(switch (os) {
|
|
.linux => &.{ "stat", "-c", "%s" },
|
|
.macos => &.{ "wc", "-c" },
|
|
else => unreachable,
|
|
});
|
|
file_size.addArtifactArg(artifact);
|
|
|
|
const file_size_step = b.step("file_size_" ++ name, "Get the file size of " ++ name);
|
|
file_size_step.dependOn(&file_size.step);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
fn newRunnerRunArtifact(arguments: struct {
|
|
configuration: Configuration,
|
|
disk_image_path: FileSource,
|
|
loader: *CompileStep,
|
|
runner: *CompileStep,
|
|
cpu_driver: *CompileStep,
|
|
user_init: *CompileStep,
|
|
qemu_options: QEMUOptions,
|
|
ovmf_path: FileSource,
|
|
is_default: bool,
|
|
}) !*RunStep {
|
|
const runner = b.addRunArtifact(arguments.runner);
|
|
|
|
var argument_parser = common.ArgumentParser.Runner{};
|
|
|
|
while (argument_parser.next()) |argument_type| switch (argument_type) {
|
|
.configuration => inline for (common.fields(Configuration)) |field| runner.addArg(@tagName(@field(arguments.configuration, field.name))),
|
|
.image_configuration_path => runner.addArg(common.ImageConfig.default_path),
|
|
.cpu_driver => runner.addArtifactArg(arguments.cpu_driver),
|
|
.loader_path => runner.addArtifactArg(arguments.loader),
|
|
.init => runner.addArtifactArg(arguments.user_init),
|
|
.disk_image_path => runner.addFileSourceArg(arguments.disk_image_path),
|
|
.qemu_options => inline for (common.fields(QEMUOptions)) |field| runner.addArg(if (@field(arguments.qemu_options, field.name)) "true" else "false"),
|
|
.ci => runner.addArg(if (ci) "true" else "false"),
|
|
.debug_user => runner.addArg(if (debug_user) "true" else "false"),
|
|
.debug_loader => runner.addArg(if (debug_loader) "true" else "false"),
|
|
.ovmf_path => runner.addFileSourceArg(arguments.ovmf_path),
|
|
.is_default => runner.addArg(if (arguments.is_default) "true" else "false"),
|
|
};
|
|
|
|
return runner;
|
|
}
|
|
|
|
const ExecutableDescriptor = struct {
|
|
kind: CompileStep.Kind,
|
|
name: []const u8,
|
|
root_project_path: []const u8,
|
|
target: CrossTarget = .{},
|
|
optimize_mode: OptimizeMode = .Debug,
|
|
modules: []const ModuleID,
|
|
};
|
|
|
|
fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep {
|
|
const main_file = try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/main.zig" });
|
|
const compile_step = switch (executable_descriptor.kind) {
|
|
.exe => blk: {
|
|
const executable = b.addExecutable(.{
|
|
.name = executable_descriptor.name,
|
|
.root_source_file = FileSource.relative(main_file),
|
|
.target = executable_descriptor.target,
|
|
.optimize = executable_descriptor.optimize_mode,
|
|
});
|
|
|
|
build_steps.build_all.dependOn(&executable.step);
|
|
|
|
break :blk executable;
|
|
},
|
|
.@"test" => blk: {
|
|
const test_file = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/test.zig" }));
|
|
const test_exe = b.addTest(.{
|
|
.name = executable_descriptor.name,
|
|
.root_source_file = test_file,
|
|
.target = executable_descriptor.target,
|
|
.optimize = executable_descriptor.optimize_mode,
|
|
.test_runner = if (executable_descriptor.target.os_tag) |_| main_file else null,
|
|
});
|
|
|
|
build_steps.build_all_tests.dependOn(&test_exe.step);
|
|
|
|
break :blk test_exe;
|
|
},
|
|
else => return Error.not_implemented,
|
|
};
|
|
|
|
compile_step.link_gc_sections = true;
|
|
|
|
if (executable_descriptor.target.getOs().tag == .freestanding) {
|
|
compile_step.entry_symbol_name = "_start";
|
|
}
|
|
|
|
compile_step.setMainPkgPath(source_root_dir);
|
|
|
|
for (executable_descriptor.modules) |module| {
|
|
modules.addModule(compile_step, module);
|
|
}
|
|
|
|
return compile_step;
|
|
}
|
|
|
|
const ModuleID = enum {
|
|
/// This module has typical common stuff used everywhere
|
|
lib,
|
|
/// This module contains code that is used by host programs when building and trying to run the OS
|
|
host,
|
|
/// This module contains code related to the bootloaders
|
|
bootloader,
|
|
bios,
|
|
uefi,
|
|
limine,
|
|
limine_installer,
|
|
/// This module contains code that is used by birth privileged programs
|
|
privileged,
|
|
/// This module contains code that is unique to birth CPU drivers
|
|
cpu,
|
|
/// This module contains code that is used by userspace programs
|
|
user,
|
|
/// This module contains code that is interacting between userspace and cpu in birth
|
|
birth,
|
|
};
|
|
|
|
pub const Modules = struct {
|
|
modules: std.EnumArray(ModuleID, *Module) = std.EnumArray(ModuleID, *Module).initUndefined(),
|
|
dependencies: std.EnumArray(ModuleID, []const ModuleDependency) = std.EnumArray(ModuleID, []const ModuleDependency).initUndefined(),
|
|
|
|
fn addModule(mods: Modules, compile_step: *CompileStep, module_id: ModuleID) void {
|
|
compile_step.addModule(@tagName(module_id), mods.modules.get(module_id));
|
|
}
|
|
|
|
fn setDependencies(mods: Modules, module_id: ModuleID, dependencies: []const ModuleID) !void {
|
|
const module = mods.modules.get(module_id);
|
|
try module.dependencies.put(@tagName(module_id), module);
|
|
|
|
for (dependencies) |dependency_id| {
|
|
const dependency_module = mods.modules.get(dependency_id);
|
|
try module.dependencies.put(@tagName(dependency_id), dependency_module);
|
|
}
|
|
}
|
|
};
|
|
|
|
fn getTarget(asked_arch: Cpu.Arch, execution_mode: common.TraditionalExecutionMode) Error!CrossTarget {
|
|
var enabled_features = Cpu.Feature.Set.empty;
|
|
var disabled_features = Cpu.Feature.Set.empty;
|
|
|
|
if (execution_mode == .privileged) {
|
|
switch (asked_arch) {
|
|
.x86, .x86_64 => {
|
|
// disable FPU
|
|
const Feature = Target.x86.Feature;
|
|
disabled_features.addFeature(@intFromEnum(Feature.x87));
|
|
disabled_features.addFeature(@intFromEnum(Feature.mmx));
|
|
disabled_features.addFeature(@intFromEnum(Feature.sse));
|
|
disabled_features.addFeature(@intFromEnum(Feature.sse2));
|
|
disabled_features.addFeature(@intFromEnum(Feature.avx));
|
|
disabled_features.addFeature(@intFromEnum(Feature.avx2));
|
|
disabled_features.addFeature(@intFromEnum(Feature.avx512f));
|
|
|
|
enabled_features.addFeature(@intFromEnum(Feature.soft_float));
|
|
},
|
|
else => return Error.architecture_not_supported,
|
|
}
|
|
}
|
|
|
|
return CrossTarget{
|
|
.cpu_arch = asked_arch,
|
|
.cpu_model = switch (common.cpu.arch) {
|
|
.x86 => .determined_by_cpu_arch,
|
|
.x86_64 => if (execution_mode == .privileged) .determined_by_cpu_arch else
|
|
// zig fmt off
|
|
.determined_by_cpu_arch,
|
|
// .determined_by_cpu_arch,
|
|
// TODO: this causes some problems: https://github.com/ziglang/zig/issues/15524
|
|
//.{ .explicit = &common.Target.x86.cpu.x86_64_v3 },
|
|
else => .determined_by_cpu_arch,
|
|
},
|
|
.os_tag = .freestanding,
|
|
.abi = .none,
|
|
.cpu_features_add = enabled_features,
|
|
.cpu_features_sub = disabled_features,
|
|
};
|
|
}
|