// imports const std = @import("std"); const Builder = std.build.Builder; // Alloc stuff var GPAAlloc = std.heap.GeneralPurposeAllocator(.{}){}; var alloc = &GPAAlloc.allocator; // Target enumeration stuff const Target = struct { parent: ?*const Target = null, cpu_arch: ?std.Target.Cpu.Arch = null, cpu_model: ?std.build.Target.CpuModel = null, archdir: ?[]const u8 = null, linkscript: ?[]const u8 = null, name: []const u8, }; const x86 = Target{ .archdir = "x86", .name = "x86-generic" }; const x86_64 = Target { .parent = &x86, .cpu_arch = std.Target.Cpu.Arch.x86_64, .archdir = "x86_64", .name = "x86_64-generic" }; const x86_64_stivale2 = Target { .parent = &x86_64, // .archdir = "x86_64/stivale2", .linkscript = "make/x86_64/linker.ld", .name = "x86_64-stivale2", }; const Targets = enum { x86_64, x86_64_stivale2, pub fn getTarget(self: Targets) Target { return switch(self) { .x86_64 => x86_64_stivale2, .x86_64_stivale2 => x86_64_stivale2, }; } pub fn default() Targets { return Targets.x86_64_stivale2; } }; fn getCpuArch(t: Target) std.Target.Cpu.Arch { return t.cpu_arch orelse getCpuArch(t.parent.?.*); } fn getCpuModel(t: Target) std.build.Target.CpuModel { if(t.cpu_model) |m| { return m; } else if (t.parent) |p| { return getCpuModel(t.parent.?.*); } else { return std.build.Target.CpuModel.baseline; } } fn getLinkerScript(t: Target) []const u8 { return t.linkscript orelse getLinkerScript(t.parent.?.*); } fn getTarget(t: Target) std.zig.CrossTarget { return .{ .cpu_arch = getCpuArch(t), .cpu_model = getCpuModel(t), .os_tag = std.Target.Os.Tag.freestanding, }; } // Directory traversal stuff const Files = struct { generic_c: [][]const u8, archspecific_c: [][]const u8, archspecific_s: [][]const u8, nasm: [][]const u8, }; fn strPrefix(str: []const u8, prefix: []const u8) bool { if (str.len < prefix.len) return false; return std.mem.eql(u8, str[0..prefix.len], prefix); } fn fileExtEql(name: []const u8, ext: []const u8) bool { std.debug.assert(ext[0] == '.'); if(name.len <= ext.len) return false; const file_ext = name[name.len - ext.len..]; return std.mem.eql(u8, file_ext, ext); } fn findSources(dir: []const u8, ext: []const u8, prefix: ?[]const u8) ![][] const u8 { var files = std.ArrayList([]const u8).init(alloc); var walker = try std.fs.walkPath(alloc, dir); while (try walker.next()) |entry| { if(entry.kind != std.fs.Dir.Entry.Kind.File) continue; if(prefix != null and strPrefix(entry.path, prefix.?)) { // std.debug.print("Ignoring {}\n", .{entry.path}); } else { if(fileExtEql(entry.basename, ext)) { // std.debug.print("Adding file {}\n", .{entry.path}); var p = std.ArrayList(u8).init(alloc); try p.appendSlice(entry.path); try files.append(p.toOwnedSlice()); } else { // std.debug.print("wrong file extension: {}\n", .{entry.basename}); } } } walker.deinit(); return files.toOwnedSlice(); } fn findArch(t: Target, ext: []const u8) ![][] const u8 { var arch_c = std.ArrayList([]const u8).init(alloc); if (t.parent) |parent| { const parentfiles = findArch(parent.*, ext) catch unreachable; try arch_c.appendSlice( parentfiles ); } if (t.archdir) |archdir| { const dir = try std.fmt.allocPrint(alloc, "src/arch/{}", .{archdir}); const files = try findSources(dir, ext, null); try arch_c.appendSlice(files); } return arch_c.items; } fn findFiles(t: Target) !Files { const generic_c = try findSources("src", ".c", "src/arch/"); const arch_c = try findArch(t, ".c"); const arch_s = try findArch(t, ".s"); const nasm = try findArch(t, ".nasm"); return Files { .generic_c = generic_c, .archspecific_c = arch_c, .archspecific_s = arch_s, .nasm = nasm }; } pub fn build(b: *Builder) !void { const target_option = b.option(Targets, "target", "The kernel target") orelse Targets.default(); const ktarget = target_option.getTarget(); // Standard release options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); b.cache_root = "zig-cache"; const files = try findFiles(ktarget); const cflags = [_][]const u8{"-Wall", "-Wextra", "-pedantic", "-std=gnu11", "-I../ext", "-Isrc", "-fPIE", "-mno-red-zone"}; const exename = try std.fmt.allocPrint(alloc, "kernel-{}", .{ktarget.name}); const exe = b.addExecutable(exename, "src/main.zig"); var cfiles = std.ArrayList([]const u8).init(alloc); try cfiles.appendSlice(files.generic_c); try cfiles.appendSlice(files.archspecific_c); for (cfiles.items) |c_file| { // std.debug.print("CC: {}\n", .{c_file}); exe.addCSourceFile(c_file, &cflags); } for(files.archspecific_s) |s_file| { // std.debug.print("zig as: {}\n", .{s_file}); exe.addAssemblyFile(s_file); } for(files.nasm) |nasm_file| { // std.debug.print("nasm: {}\n", .{nasm_file}); const output = try std.fmt.allocPrint(alloc, "zig-cache/nasm/{}.o", .{nasm_file}); const output_folder = std.fs.cwd().makePath(std.fs.path.dirname(output).?); const assX = b.addSystemCommand(&[_][]const u8{"nasm", "-Isrc", "-felf64", nasm_file, "-o", output}); exe.step.dependOn(&assX.step); exe.addObjectFile(output); } exe.setTarget(getTarget(ktarget)); exe.setBuildMode(mode); exe.setLinkerScriptPath(getLinkerScript(ktarget)); // exe.setOutputDir("bin"); exe.install(); const run_cmd = exe.run(); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| { run_cmd.addArgs(args); } // const run_step = b.step("run", "Run the app"); // run_step.dependOn(&run_cmd.step); }