const std = @import("std"); pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); // Get output filename from build system const args = try std.process.argsAlloc(allocator); if (args.len < 3) @panic("Missing output filename"); const out_path = args[1]; // Generate a startup module function that invokes // startup module for each zig function // and generates an api var writer = std.ArrayList(u8){}; const moduleList = args[2..]; for (moduleList) |mod| { try writer.print(allocator, "pub const {s} = @import(\"{s}\").module;\n", .{ mod, mod }); } try writer.print(allocator, "pub const moduleList:[]const []const u8 = &.{{\n", .{}); for (moduleList) |mod| { try writer.print(allocator, "\"{s}\",\n", .{mod}); } try writer.print(allocator, "}};\n", .{}); try writer.print(allocator, \\var shutdownList: std.ArrayListUnmanaged(*const fn (std.mem.Allocator) void) = .{{}}; \\var shutdownModuleNames: std.ArrayListUnmanaged([]const u8) = .{{}}; \\ \\ pub const NwArgs = struct {{ \\ useGPA: bool = false, // slower zig based memory allocator, provides detailed tracking of leaks and memory violations \\ vulkanValidation: bool = true, \\ fastTest: bool = false, \\ dmt: bool = false, // detailed memory tracking, implements a timeline for tracking all memory allocations \\ fatDump: bool = false, // takes a full fat minidump on crash, very large files are produced \\ }}; \\ \\ pub fn getArgs() !NwArgs {{ \\ const a = try core.ParseArgs(NwArgs); \\ \\ return a; \\ }} , .{}); try writer.print(allocator, "pub fn start_modules_(spec: *core.SpecVariantMap, maybeArgs: ?NwArgs, allocator: std.mem.Allocator) !void {{", .{}); try writer.print(allocator, \\ \\ var z = core.tracy.ZoneN(@src(), "Starting all Modules"); \\ defer z.End(); \\ inline for (moduleList) |feature| {{ \\ if (@hasDecl(@This(), feature)) {{ \\ const Struct = @field(@This(), feature); \\ if (core.isModuleEnabled(Struct.Module, spec)) {{ \\ var z1 = core.tracy.ZoneN(@src(), @ptrCast("Initializing Module")); \\ defer z1.End(); \\ core.tracy.Message(Struct.Module.name); \\ if (maybeArgs) |args| {{ \\ try Struct.start_module(spec, args, allocator); \\ }} else {{ \\ try Struct.start_module(spec, NwArgs{{}}, allocator); \\ }} \\ if(core.MemoryTracker.MTGet()) |_| \\ {{ \\ core.MemoryTracker.MTPrintStatsDelta(); \\ }} \\ try shutdownList.append(allocator, Struct.shutdown_module); \\ try shutdownModuleNames.append(allocator, feature); \\ core.engine_logs("module started >>>> " ++ feature ++ " <<<<"); \\ }} \\ }} \\ }} }} \\pub fn start_modules(spec: *core.SpecVariantMap, maybeArgs: ?NwArgs, allocator: std.mem.Allocator) bool {{ start_modules_(spec, maybeArgs, allocator) catch return false; return true;}} \\ \\ pub fn shutdown_modules(allocator: std.mem.Allocator) void {{ \\ var i: isize = @intCast(shutdownList.items.len - 1); \\ while (i >= 0) : (i -= 1) {{ \\ core.engine_log("module shutting down >>>> {{s}} <<<<", .{{shutdownModuleNames.items[@intCast(i)]}}); \\ shutdownList.items[@intCast(i)](allocator); \\ }} \\ shutdownList.deinit(allocator); \\ shutdownModuleNames.deinit(allocator); \\ }} \\ , .{}); _ = try writer.appendSlice(allocator, \\ pub fn startEngine(vtable: *core.EngineObjectVTable, spec: *core.SpecVariantMap) bool { \\ const args = getArgs() catch return false; \\ \\ var backingAllocator: std.mem.Allocator = std.heap.smp_allocator; \\ var gpa: std.heap.GeneralPurposeAllocator(.{ \\ .stack_trace_frames = 20, \\ }) = .{}; \\ \\ defer { \\ const cleanupStatus = gpa.deinit(); \\ if (cleanupStatus == .leak) { \\ std.debug.print("gpa cleanup leaked memory\n", .{}); \\ } \\ } \\ \\ if (spec.get("useGPA")) |arg| { \\ if (arg.boolean == true) { \\ backingAllocator = gpa.allocator(); \\ } \\ } \\ \\ const memory = core.MemoryTracker; \\ memory.MTSetup(backingAllocator, .{ .timeline = args.dmt }); \\ defer memory.MTShutdown(); \\ \\ var tracker = memory.MTGet().?; \\ const allocator = tracker.allocator(); \\ \\ _ = core.createNameRegistry(allocator) catch return false; \\ core.maybeInitPackerFs(allocator) catch return false; \\ \\ if(core.BuildOption("static_build")) \\ { \\ core.engine_log("static build, using embedded shaders", .{}); \\ const loader = @import("staticShaderLoader"); \\ loader.installStaticResources() catch return false; \\ } \\ \\ if (!start_modules(spec, args, allocator)) return false; \\ defer shutdown_modules(allocator); \\ \\ run_everything_vtable(vtable) catch return false; \\ \\ return true; \\ } \\ \\ \\ pub const loadDynamicModules = @import("loadDynamics").loadDynamicModules; \\ \\ pub fn run_everything_vtable(gameVtable: *core.EngineObjectVTable) !void { \\ core.engine_logs("creating Game context"); \\ \\ //_ = try core.createObject(GameContext, .{}); \\ _ = try core.createObjectVTable(gameVtable, .{}); \\ \\ core.engine_logs("calling gEngine.run"); \\ \\ try core.getEngine().run(); \\ \\ while (!core.getEngine().exitFinished()) { \\ const z = core.tracy.ZoneN(@src(), "shutdown poll"); \\ z.End(); \\ } \\ } \\ \\ pub fn getSpec(comptime name:[]const u8) !core.SpecVariantMap { \\ return try core.createSpecVariant(.{ \\ .useGPA = true, \\ .name = name, ); for (moduleList) |mod| { try writer.print(allocator, ".{s} = true,\n", .{mod}); } try writer.print(allocator, "}}, std.heap.smp_allocator); }}\n", .{}); try writer.print(allocator, "const std = @import(\"std\");\n\n", .{}); // Write to specified output file // Open or create the file for writing (overwrites if it exists) const file = try std.fs.cwd().createFile(out_path, .{}); defer file.close(); try writer.append(allocator, 0); var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(writer.items[0 .. writer.items.len - 1])), .zig); // std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]}); defer ast.deinit(allocator); const out = try ast.renderAlloc(allocator); // Write the content to the file try file.writeAll(out); }