// 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, extra_features: []const std.Target.Cpu.Feature = &[_] std.Target.Cpu.Feature {}, forbidden_features: []const std.Target.Cpu.Feature = &[_] std.Target.Cpu.Feature {}, archdir: ?[]const u8 = null, linkscript: ?[]const u8 = null, name: []const u8, }; const x86 = Target{ .archdir = "x86", .name = "x86-generic", }; const x86_64_nosse = std.Target.Cpu.Model { .name = "x86_64-sse2+soft_float", .llvm_name = null, .features = std.Target.x86.featureSet(&[_] std.Target.x86.Feature {std.Target.x86.Feature.@"64bit"}) }; const x86_64 = Target { .parent = &x86, .cpu_arch = std.Target.Cpu.Arch.x86_64, .extra_features = &[_] std.Target.Cpu.Feature { std.Target.x86.all_features[@enumToInt(std.Target.x86.Feature.soft_float)], }, .forbidden_features = &[_] std.Target.Cpu.Feature { std.Target.x86.all_features[@enumToInt(std.Target.x86.Feature.sse)], std.Target.x86.all_features[@enumToInt(std.Target.x86.Feature.sse2)], }, .archdir = "x86_64", .name = "x86_64-generic", }; const intel386 = Target { .parent = &x86, .cpu_arch = std.Target.Cpu.Arch.i386, .archdir = "i386", .name = "i386-generic", }; const x86_64_stivale2 = Target { .parent = &x86_64, // .archdir = "x86_64/stivale2", .linkscript = "make/x86_64/linker.ld", .name = "x86_64", }; const i386_stivale2 = Target { .parent = &intel386, .linkscript = "make/i386/linker.ld", .name = "i386", }; const OutputTargets = &[_] Target { x86_64_stivale2, i386_stivale2, }; const Targets = enum { x86_64, x86_64_stivale2, i386, i386_stivale2, pub fn getTarget(self: Targets) Target { return switch(self) { .x86_64 => x86_64_stivale2, .x86_64_stivale2 => x86_64_stivale2, .i386 => i386_stivale2, .i386_stivale2 => i386_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 getExtraFeatures(t: Target) std.Target.Cpu.Feature.Set { var parentset = std.Target.Cpu.Feature.Set.empty; if(t.parent) |p| { parentset = getExtraFeatures(p.*); } for (t.extra_features) |bl_ft| { parentset.addFeature(bl_ft.index); } return parentset; } fn getForbiddenFeatures(t: Target) std.Target.Cpu.Feature.Set { var parentset = std.Target.Cpu.Feature.Set.empty; if(t.parent) |p| { parentset = getForbiddenFeatures(p.*); } for (t.forbidden_features) |bl_ft| { parentset.addFeature(bl_ft.index); } return parentset; } 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), .cpu_features_add = getExtraFeatures(t), .cpu_features_sub = getForbiddenFeatures(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. // b.setPreferredReleaseMode(std.builtin.Mode.ReleaseSafe); const mode = b.standardReleaseOptions(); 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"); exe.addIncludeDir("src"); 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); } // const zigobj = b.addObject("zigkalloc", "src/allocator/calloc.zig"); // zigobj.setTarget(getTarget(ktarget)); // zigobj.strip = true; // // zigobj.single_threaded = true; // // zigobj.code_model = std.builtin.CodeModel.kernel; // exe.addObject(zigobj); exe.setTarget(getTarget(ktarget)); exe.setBuildMode(mode); // exe.code_model = std.builtin.CodeModel.kernel; exe.setLinkerScriptPath(getLinkerScript(ktarget)); // exe.setOutputDir("bin"); // exe.strip = true; // exe.single_threaded = true; 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); }