307 lines
9.8 KiB
Zig
307 lines
9.8 KiB
Zig
pub const std = @import("std");
|
|
pub const core = @import("core");
|
|
|
|
fn __debugPrint(comptime fmt: []const u8, args: anytype) void {
|
|
std.debug.print(fmt ++ "\n", args);
|
|
}
|
|
|
|
fn noprint(comptime fmt: []const u8, args: anytype) void {
|
|
_ = fmt;
|
|
_ = args;
|
|
}
|
|
|
|
const debugPrint = __debugPrint;
|
|
|
|
pub const SubprocessTask = struct {
|
|
// any and all string values added to this subprocesstask must exist and be usable across threads.
|
|
// i reeccomend converting strings to a name via core.MakeName before passing them over. if they are freed
|
|
// by the time the thread uses it, it will crash .
|
|
|
|
mutex: std.Thread.Mutex = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
child: ?std.process.Child = null,
|
|
completed: bool = false,
|
|
success: bool = false,
|
|
|
|
workingDir: ?[]const u8 = null,
|
|
|
|
stdout: std.ArrayListUnmanaged(u8) = .{},
|
|
stderr: std.ArrayListUnmanaged(u8) = .{},
|
|
|
|
argsOwned: std.ArrayListUnmanaged([]const u8) = .{},
|
|
argsArena: std.heap.ArenaAllocator,
|
|
|
|
pub fn run(task: *@This()) !void {
|
|
const L = struct {
|
|
self: *SubprocessTask,
|
|
|
|
pub fn func(ctx: @This(), _: *core.JobContext) void {
|
|
const self = ctx.self;
|
|
self.mutex.lock();
|
|
self.stderr = .{};
|
|
self.stdout = .{};
|
|
core.engine_log("running task", .{});
|
|
core.engine_log("cwd = {?s}", .{self.workingDir});
|
|
self.child = std.process.Child.init(self.argsOwned.items, self.allocator);
|
|
self.child.?.stdout_behavior = .Inherit;
|
|
self.child.?.stderr_behavior = .Inherit;
|
|
self.child.?.cwd = self.workingDir;
|
|
|
|
_ = self.child.?.spawn() catch unreachable;
|
|
self.mutex.unlock();
|
|
|
|
// close and cleanup everything
|
|
self.waitInner() catch {};
|
|
}
|
|
};
|
|
|
|
try core.dispatchJob(L{ .self = task });
|
|
}
|
|
|
|
pub fn create(allocator: std.mem.Allocator, argv: []const []const u8) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
|
|
self.* = .{
|
|
.child = null,
|
|
.allocator = allocator,
|
|
.argsArena = std.heap.ArenaAllocator.init(allocator),
|
|
};
|
|
|
|
for (argv) |a| {
|
|
const v = try self.argsArena.allocator().dupe(u8, a);
|
|
try self.argsOwned.append(self.allocator, v);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
self.mutex.lock();
|
|
self.argsArena.deinit();
|
|
self.argsOwned.deinit(self.allocator);
|
|
if (self.child) |*child| {
|
|
_ = child;
|
|
core.engine_log("destroying child process", .{});
|
|
}
|
|
self.mutex.unlock();
|
|
self.stdout.deinit(self.allocator);
|
|
self.stderr.deinit(self.allocator);
|
|
self.allocator.destroy(self);
|
|
}
|
|
|
|
pub fn checkComplete(self: *@This()) bool {
|
|
return self.completed;
|
|
}
|
|
|
|
pub fn wait(self: *@This()) void {
|
|
var completed: bool = self.completed;
|
|
while (completed == false) {
|
|
std.Thread.sleep(1 * 1000 * 1000);
|
|
self.mutex.lock();
|
|
completed = self.completed;
|
|
self.mutex.unlock();
|
|
}
|
|
debugPrint("task completed", .{});
|
|
}
|
|
|
|
pub fn waitInner(self: *@This()) !void {
|
|
self.mutex.lock();
|
|
if (self.child.?.stdout_behavior == .Pipe) {
|
|
try self.child.?.collectOutput(self.allocator, &self.stdout, &self.stderr, 1 * 1024 * 1024);
|
|
}
|
|
|
|
debugPrint("task completed stdout: {s}", .{self.stdout.items});
|
|
const term = try self.child.?.wait();
|
|
debugPrint("wait completed", .{});
|
|
self.mutex.unlock();
|
|
|
|
switch (term) {
|
|
.Exited => |m| {
|
|
if (m == 0) self.success = true;
|
|
},
|
|
.Signal => |m| {
|
|
core.engine_log("process Signaled {d}", .{m});
|
|
},
|
|
.Stopped => |m| {
|
|
core.engine_log("process Stopped {d}", .{m});
|
|
},
|
|
.Unknown => |m| {
|
|
core.engine_log("process Unknown {d}", .{m});
|
|
},
|
|
}
|
|
|
|
self.mutex.lock();
|
|
self.child = null;
|
|
self.completed = true;
|
|
self.mutex.unlock();
|
|
}
|
|
|
|
pub fn runCommand(allocator: std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8) !*@This() {
|
|
const task = try @This().create(allocator, argv);
|
|
task.workingDir = cwd;
|
|
|
|
try task.run();
|
|
|
|
return task;
|
|
}
|
|
};
|
|
|
|
fn findRootDir(allocator: std.mem.Allocator) ![]u8 {
|
|
var current_dir = std.fs.cwd();
|
|
|
|
// Start from current directory and walk up
|
|
var path_components = std.ArrayList([]const u8){};
|
|
defer path_components.deinit(allocator);
|
|
|
|
// Try current directory first
|
|
current_dir.access("content.txt", .{}) catch {
|
|
// content.txt not found, need to walk up
|
|
var temp_dir = current_dir;
|
|
|
|
while (true) {
|
|
// Try to go up one directory
|
|
const parent_dir = temp_dir.openDir("..", .{}) catch return error.RootNotFound;
|
|
temp_dir = parent_dir;
|
|
|
|
// Check if content.txt exists in parent directory
|
|
temp_dir.access("content.txt", .{}) catch {
|
|
continue;
|
|
};
|
|
|
|
// Found content.txt, get the absolute path
|
|
return try temp_dir.realpathAlloc(allocator, ".");
|
|
}
|
|
};
|
|
|
|
// content.txt found in current directory
|
|
return try current_dir.realpathAlloc(allocator, ".");
|
|
}
|
|
|
|
pub fn start_CompileShaders(context: ?*anyopaque, callback: ?*const fn (?*anyopaque) void) !void {
|
|
const self = core.EngineObject(SystemRunner).get();
|
|
|
|
const root_dir = findRootDir(self.allocator) catch {
|
|
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
|
|
return;
|
|
};
|
|
|
|
defer self.allocator.free(root_dir);
|
|
core.engine_log("Found root directory: {s}", .{root_dir});
|
|
|
|
const compile_shaders_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "compileShaders" });
|
|
defer self.allocator.free(compile_shaders_dir);
|
|
|
|
var compile_shaders_name = core.MakeName(compile_shaders_dir);
|
|
|
|
core.engine_log("CompileShaders directory: {s}", .{compile_shaders_dir});
|
|
|
|
const exe_path = try std.fs.path.join(self.allocator, &.{ compile_shaders_dir, "zig-out", "bin", "compileShaders.exe" });
|
|
defer self.allocator.free(exe_path);
|
|
core.engine_log("Looking for executable at: {s}", .{exe_path});
|
|
|
|
// Check if compileShaders.exe exists
|
|
if (std.fs.cwd().access(exe_path, .{})) |_| {
|
|
// File exists, run it directly
|
|
core.engine_log("CompileShaders executable found, running directly", .{});
|
|
try self.commandQueue.pushLocked(.{ .subprocess = .{
|
|
.onComplete = callback,
|
|
.context = context,
|
|
.task = try SubprocessTask.runCommand(
|
|
self.allocator,
|
|
&.{"zig-out/bin/compileShaders.exe"},
|
|
compile_shaders_name.utf8(),
|
|
),
|
|
} });
|
|
} else |_| {
|
|
// File doesn't exist, build it first
|
|
core.engine_log("CompileShaders executable not found, building first", .{});
|
|
try self.commandQueue.pushLocked(.{
|
|
.subprocess = .{ .task = try SubprocessTask.runCommand(
|
|
self.allocator,
|
|
&.{ "zig", "build", "install" },
|
|
compile_shaders_name.utf8(),
|
|
) },
|
|
});
|
|
|
|
// Then run it
|
|
core.engine_log("Queuing compileShaders execution after build", .{});
|
|
try self.commandQueue.pushLocked(.{
|
|
.subprocess = .{
|
|
.context = context,
|
|
.onComplete = callback,
|
|
.task = try SubprocessTask.runCommand(
|
|
self.allocator,
|
|
&.{"zig-out/bin/compileShaders.exe"},
|
|
compile_shaders_name.utf8(),
|
|
),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
const Task = union(enum(u8)) {
|
|
subprocess: struct {
|
|
task: *SubprocessTask,
|
|
onComplete: ?*const fn (?*anyopaque) void = null,
|
|
context: ?*anyopaque = null,
|
|
},
|
|
};
|
|
|
|
pub const SystemRunner = struct {
|
|
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "sys.SystemRunner");
|
|
|
|
allocator: std.mem.Allocator,
|
|
|
|
commandQueue: core.RingQueue(Task),
|
|
activeCommand: ?Task = null,
|
|
|
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
.commandQueue = try core.RingQueue(Task).init(self.allocator, 256),
|
|
};
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn tick(self: *@This(), dt: f64) void {
|
|
_ = dt;
|
|
if (self.activeCommand == null) {
|
|
if (self.commandQueue.popFromLocked()) |task| {
|
|
switch (task) {
|
|
.subprocess => |t| {
|
|
self.activeCommand = task;
|
|
// core.engine_log("starting task {any}", .{t.args});
|
|
t.task.run() catch {
|
|
self.activeCommand = null;
|
|
return;
|
|
};
|
|
},
|
|
//.func => |f| {
|
|
//f(self);
|
|
// },
|
|
}
|
|
}
|
|
}
|
|
|
|
if (self.activeCommand) |active| {
|
|
if (active.subprocess.task.checkComplete()) {
|
|
if (active.subprocess.onComplete) |onComplete| {
|
|
onComplete(active.subprocess.context);
|
|
}
|
|
|
|
active.subprocess.task.destroy();
|
|
core.engine_logs("task complete");
|
|
self.activeCommand = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
self.commandQueue.deinit();
|
|
self.allocator.destroy(self);
|
|
}
|
|
};
|