From b086bdee3ad67cc1f85c18652b3d02aea22438ef Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Sun, 26 Mar 2023 19:48:37 +0330 Subject: [PATCH] wasmserve: almost working --- build.zig | 6 +- libs/core/sdk.zig | 117 ++++++++++++++++++++++------- tools/html-generator/main.zig | 29 ------- tools/html-generator/template.html | 54 ------------- tools/wasmserve/wasmserve.zig | 79 +++++++------------ 5 files changed, 122 insertions(+), 163 deletions(-) delete mode 100644 tools/html-generator/main.zig delete mode 100644 tools/html-generator/template.html diff --git a/build.zig b/build.zig index 21e88be8..8500f88d 100644 --- a/build.zig +++ b/build.zig @@ -10,8 +10,7 @@ const earcut = @import("libs/earcut/build.zig"); const gamemode = @import("libs/gamemode/build.zig"); const model3d = @import("libs/model3d/build.zig"); const dusk = @import("libs/dusk/build.zig"); -// TODO: get wasmserve working -// const wasmserve = @import("tools/wasmserve/wasmserve.zig"); +const wasmserve = @import("tools/wasmserve/wasmserve.zig"); pub const gpu_dawn = @import("libs/gpu-dawn/sdk.zig").Sdk(.{ .glfw_include_dir = sdkPath("/libs/glfw/upstream/glfw/include"), .system_sdk = system_sdk, @@ -28,8 +27,7 @@ const core = @import("libs/core/sdk.zig").Sdk(.{ .gpu_dawn = gpu_dawn, .glfw = glfw, .gamemode = gamemode, - // TODO: get wasmserve working - // .wasmserve = wasmserve, + .wasmserve = wasmserve, .sysjs = sysjs, }); diff --git a/libs/core/sdk.zig b/libs/core/sdk.zig index 39636be6..2f1b6636 100644 --- a/libs/core/sdk.zig +++ b/libs/core/sdk.zig @@ -91,7 +91,7 @@ pub fn Sdk(comptime deps: anytype) type { pub const LinkError = deps.glfw.LinkError; pub const RunError = error{ ParsingIpFailed, - } || std.fmt.ParseIntError; + } || deps.wasmserve.Error || std.fmt.ParseIntError; pub const Platform = enum { native, @@ -129,7 +129,12 @@ pub fn Sdk(comptime deps: anytype) type { const step = blk: { if (platform == .web) { - const lib = b.addSharedLibrary(.{ .name = options.name, .root_source_file = .{ .path = sdkPath("/src/main.zig") }, .target = options.target, .optimize = options.optimize }); + const lib = b.addSharedLibrary(.{ + .name = options.name, + .root_source_file = .{ .path = sdkPath("/src/entry.zig") }, + .target = options.target, + .optimize = options.optimize, + }); lib.rdynamic = true; lib.addModule("sysjs", deps.sysjs.module(b)); @@ -182,7 +187,7 @@ pub fn Sdk(comptime deps: anytype) type { // Set install directory to '{prefix}/www' app.getInstallStep().?.dest_dir = web_install_dir; - inline for (.{ "/src/platform/wasm/mach.js", "/libs/sysjs/src/mach-sysjs.js" }) |js| { + inline for (.{ "/src/platform/wasm/mach.js", "/libs/mach-sysjs/src/mach-sysjs.js" }) |js| { const install_js = app.b.addInstallFileWithDir( .{ .path = sdkPath(js) }, web_install_dir, @@ -191,15 +196,9 @@ pub fn Sdk(comptime deps: anytype) type { app.getInstallStep().?.step.dependOn(&install_js.step); } - const html_generator = app.b.addExecutable(.{ - .name = "html-generator", - .root_source_file = .{ .path = sdkPath("/tools/html-generator/main.zig") }, - }); - const run_html_generator = html_generator.run(); - run_html_generator.addArgs(&.{ "index.html", app.name }); - - run_html_generator.cwd = app.b.getInstallPath(web_install_dir, ""); - app.getInstallStep().?.step.dependOn(&run_html_generator.step); + genHtml(app.b.allocator, app.b.getInstallPath(web_install_dir, "index.html"), app.name) catch |err| { + std.log.err("unable to generate html: {s}", .{@errorName(err)}); + }; } // Install resources @@ -218,20 +217,19 @@ pub fn Sdk(comptime deps: anytype) type { pub fn run(app: *const App) RunError!*std.build.Step { if (app.platform == .web) { - @panic("TODO: wasmserve is broken! sorry"); - // TODO: get wasmserve working - // const address = std.process.getEnvVarOwned(app.b.allocator, "MACH_ADDRESS") catch try app.b.allocator.dupe(u8, "127.0.0.1"); - // const port = std.process.getEnvVarOwned(app.b.allocator, "MACH_PORT") catch try app.b.allocator.dupe(u8, "8080"); - // const address_parsed = std.net.Address.parseIp4(address, try std.fmt.parseInt(u16, port, 10)) catch return error.ParsingIpFailed; - // const serve_step = try deps.wasmserve.serve( - // app.step, - // .{ - // .install_dir = web_install_dir, - // .watch_paths = app.watch_paths, - // .listen_address = address_parsed, - // }, - // ); - // return &serve_step.step; + const address = std.process.getEnvVarOwned(app.b.allocator, "MACH_ADDRESS") catch try app.b.allocator.dupe(u8, "127.0.0.1"); + const port = std.process.getEnvVarOwned(app.b.allocator, "MACH_PORT") catch try app.b.allocator.dupe(u8, "8080"); + const address_parsed = std.net.Address.parseIp4(address, try std.fmt.parseInt(u16, port, 10)) catch return error.ParsingIpFailed; + const serve_step = try deps.wasmserve.serve( + app.b, + .{ + // .step_name = + .install_dir = web_install_dir, + .watch_paths = app.watch_paths, + .listen_address = address_parsed, + }, + ); + return &serve_step.step; } else { return &app.step.run().step; } @@ -241,5 +239,72 @@ pub fn Sdk(comptime deps: anytype) type { return app.step.install_step; } }; + + pub fn genHtml(allocator: std.mem.Allocator, output_name: []const u8, app_name: []const u8) !void { + const file = try std.fs.cwd().createFile(output_name, .{}); + defer file.close(); + + var buf = try std.fmt.allocPrint(allocator, html_template, .{ .app_name = app_name }); + defer allocator.free(buf); + + _ = try file.write(buf); + } + + const html_template = + \\ + \\ + \\ + \\ + \\ + \\ {[app_name]s} + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ; }; } diff --git a/tools/html-generator/main.zig b/tools/html-generator/main.zig deleted file mode 100644 index 9dcba5dd..00000000 --- a/tools/html-generator/main.zig +++ /dev/null @@ -1,29 +0,0 @@ -const std = @import("std"); - -const source = @embedFile("template.html"); -const app_name_needle = "{ app_name }"; - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - defer _ = gpa.deinit(); - - const args = try std.process.argsAlloc(allocator); - defer std.process.argsFree(allocator, args); - - if (args.len < 3) { - std.debug.print("Usage: html-generator \n", .{}); - return; - } - - const output_name = args[1]; - const app_name = args[2]; - - const file = try std.fs.cwd().createFile(output_name, .{}); - defer file.close(); - var buf = try allocator.alloc(u8, std.mem.replacementSize(u8, source, app_name_needle, app_name)); - defer allocator.free(buf); - - _ = std.mem.replace(u8, source, app_name_needle, app_name, buf); - _ = try file.write(buf); -} diff --git a/tools/html-generator/template.html b/tools/html-generator/template.html deleted file mode 100644 index 3a34470d..00000000 --- a/tools/html-generator/template.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - { app_name } - - - - - - - \ No newline at end of file diff --git a/tools/wasmserve/wasmserve.zig b/tools/wasmserve/wasmserve.zig index 3e0b81fc..6e0a5c10 100644 --- a/tools/wasmserve/wasmserve.zig +++ b/tools/wasmserve/wasmserve.zig @@ -19,6 +19,7 @@ const esc = struct { }; pub const Options = struct { + step_name: []const u8 = "install", install_dir: ?build.InstallDir = null, watch_paths: ?[]const []const u8 = null, listen_address: ?net.Address = null, @@ -26,30 +27,30 @@ pub const Options = struct { pub const Error = error{CannotOpenDirectory} || mem.Allocator.Error; -pub fn serve(step: *build.CompileStep, options: Options) Error!*Wasmserve { - const self = try step.step.owner.allocator.create(Wasmserve); +pub fn serve(b: *std.Build.Builder, options: Options) Error!*Wasmserve { + const self = try b.allocator.create(Wasmserve); const install_dir = options.install_dir orelse build.InstallDir{ .lib = {} }; - const install_dir_iter = fs.cwd().makeOpenPathIterable(step.step.owner.getInstallPath(install_dir, ""), .{}) catch + const install_dir_iter = fs.cwd().makeOpenPathIterable(b.getInstallPath(install_dir, ""), .{}) catch return error.CannotOpenDirectory; self.* = Wasmserve{ - .step = build.Step.init(.run, "wasmserve", step.step.owner.allocator, Wasmserve.make), - .b = step.step.owner, - .exe_step = step, + .b = b, + .step = build.Step.init(.{ .id = .run, .name = "wasmserve", .owner = b, .makeFn = Wasmserve.make }), + .step_name = options.step_name, .install_dir = install_dir, .install_dir_iter = install_dir_iter, .address = options.listen_address orelse net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 8080), .subscriber = null, - .watch_paths = options.watch_paths orelse &.{step.root_src.?.path}, - .mtimes = std.AutoHashMap(fs.File.INode, i128).init(step.step.owner.allocator), + .watch_paths = options.watch_paths orelse &.{"src"}, + .mtimes = std.AutoHashMap(fs.File.INode, i128).init(b.allocator), .notify_msg = null, }; return self; } const Wasmserve = struct { - step: build.Step, b: *build.Builder, - exe_step: *build.CompileStep, + step: build.Step, + step_name: []const u8, install_dir: build.InstallDir, install_dir_iter: fs.IterableDir, address: net.Address, @@ -69,11 +70,13 @@ const Wasmserve = struct { data: []const u8, }; - pub fn make(step: *build.Step) !void { + pub fn make(step: *build.Step, prog_node: *std.Progress.Node) !void { + std.debug.print("Really!\n", .{}); + const self = @fieldParentPtr(Wasmserve, "step", step); - self.compile(); - std.debug.assert(mem.eql(u8, fs.path.extension(self.exe_step.out_filename), ".wasm")); + try self.compile(); + // std.debug.assert(mem.eql(u8, fs.path.extension(self.compile_step.out_filename), ".wasm")); var www_dir = try fs.cwd().openIterableDir(www_dir_path, .{}); defer www_dir.close(); @@ -86,7 +89,7 @@ const Wasmserve = struct { self.install_dir, file.name, ); - try install_www.step.make(); + try install_www.step.make(prog_node); } const watch_thread = try std.Thread.spawn(.{}, watch, .{self}); @@ -134,7 +137,12 @@ const Wasmserve = struct { const url = dropFragment(uri)[1..]; if (mem.eql(u8, url, "notify")) { - _ = try conn.stream.write("HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: text/event-stream\r\nCache-Control: No-Cache\r\n\r\n"); + _ = try conn.stream.write( + "HTTP/1.1 200 OK\r\n" ++ + "Connection: Keep-Alive\r\n" ++ + "Content-Type: text/event-stream\r\n" ++ + "Cache-Control: No-Cache\r\n\r\n", + ); self.subscriber = try self.b.allocator.create(net.StreamServer.Connection); self.subscriber.?.* = conn; if (self.notify_msg) |msg| @@ -232,7 +240,7 @@ const Wasmserve = struct { const entry = try self.mtimes.getOrPut(stat.inode); if (entry.found_existing and stat.mtime > entry.value_ptr.*) { std.log.info(esc.yellow ++ esc.underline ++ "{s}" ++ esc.reset ++ " updated", .{path}); - self.compile(); + try self.compile(); entry.value_ptr.* = stat.mtime; return true; } @@ -253,14 +261,10 @@ const Wasmserve = struct { } } - fn compile(self: *Wasmserve) void { + fn compile(self: *Wasmserve) !void { std.log.info("Building...", .{}); - const argv = getExecArgs(self.exe_step) catch |err| { - logErr(err, @src()); - return; - }; - defer self.b.allocator.free(argv); - var res = std.ChildProcess.exec(.{ .argv = argv, .allocator = self.b.allocator }) catch |err| { + + var res = std.ChildProcess.exec(.{ .argv = &.{ self.b.zig_exe, "build", self.step_name, "-Dtarget=wasm32-freestanding-none" }, .allocator = self.b.allocator }) catch |err| { logErr(err, @src()); return; }; @@ -307,8 +311,8 @@ fn dropFragment(input: []const u8) []const u8 { } fn logErr(err: anyerror, src: std.builtin.SourceLocation) void { - if (@errorReturnTrace()) |bt| { - std.log.err(esc.red ++ esc.bold ++ "{s}" ++ esc.reset ++ " >>>\n{s}", .{ @errorName(err), bt }); + if (@errorReturnTrace()) |et| { + std.log.err(esc.red ++ esc.bold ++ "{s}" ++ esc.reset ++ " >>>\n{s}", .{ @errorName(err), et }); } else { var file_name_buf: [1024]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&file_name_buf); @@ -333,28 +337,3 @@ fn sdkPath(comptime suffix: []const u8) []const u8 { break :blk root_dir ++ suffix; }; } - -// copied from CompileStep.make() -// TODO: this is very tricky -// TODO(wasmserve): wasmserve is broken after recent Zig build changes, need to expose -// this from Zig stdlib or something instead of copying this huge function out of stdlib -// like this (nasty!) -fn getExecArgs(_: *build.CompileStep) ![]const []const u8 { - @panic("wasmserve is currently not working"); -} - -fn makePackageCmd(self: *std.build.CompileStep, pkg: std.build.Pkg, zig_args: *std.ArrayList([]const u8)) error{OutOfMemory}!void { - const builder = self.builder; - - try zig_args.append("--pkg-begin"); - try zig_args.append(pkg.name); - try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); - - if (pkg.dependencies) |dependencies| { - for (dependencies) |sub_pkg| { - try makePackageCmd(self, sub_pkg, zig_args); - } - } - - try zig_args.append("--pkg-end"); -}